Bug 1546248 - Add attribution parameters to AMO links r=mstriemer
☠☠ backed out by 2f3479842f91 ☠ ☠
authorRob Wu <rob@robwu.nl>
Mon, 06 May 2019 10:41:24 +0000
changeset 531520 cb165c7bea674ed655c306c8c176b00452be2e88
parent 531519 75766279872af87f19b0d763e5e6bc27e905720f
child 531521 2209fbf12d8b8cb749316b7cfa55222c9a93844a
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstriemer
bugs1546248
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1546248 - Add attribution parameters to AMO links r=mstriemer Differential Revision: https://phabricator.services.mozilla.com/D29479
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -148,16 +148,41 @@ function nl2br(text) {
       frag.appendChild(document.createElement("br"));
     }
     frag.appendChild(new Text(part));
     hasAppended = true;
   }
   return frag;
 }
 
+/**
+ * Adds UTM parameters to a given URL, if it is an AMO URL.
+ *
+ * @param {string} contentAttribute
+ *        Identifies the part of the UI with which the link is associated.
+ * @param {string} url
+ * @returns {string}
+ *          The url with UTM parameters if it is an AMO URL.
+ *          Otherwise the url in unmodified form.
+ */
+function formatAmoUrl(contentAttribute, url) {
+  let parsedUrl = new URL(url);
+  let domain = `.${parsedUrl.hostname}`;
+  if (!domain.endsWith(".addons.mozilla.org") &&
+      // For testing: addons-dev.allizom.org and addons.allizom.org
+      !domain.endsWith(".allizom.org")) {
+    return url;
+  }
+
+  parsedUrl.searchParams.set("utm_source", "firefox-browser");
+  parsedUrl.searchParams.set("utm_medium", "firefox-browser");
+  parsedUrl.searchParams.set("utm_content", contentAttribute);
+  return parsedUrl.href;
+}
+
 // A wrapper around an item from the "results" array from AMO's discovery API.
 // See https://addons-server.readthedocs.io/en/latest/topics/api/discovery.html
 class DiscoAddonWrapper {
   constructor(details) {
     // Reuse AddonRepository._parseAddon to have the AMO response parsing logic
     // in one place.
     let repositoryAddon = AddonRepository._parseAddon(details.addon);
 
@@ -1123,17 +1148,18 @@ class RecommendedAddonCard extends HTMLE
     // Set the author name and link to AMO.
     if (addon.creator) {
       let authorInfo = card.querySelector(".disco-addon-author");
       document.l10n.setAttributes(authorInfo, "created-by-author", {
         author: addon.creator.name,
       });
       // This is intentionally a link to the add-on listing instead of the
       // author page, because the add-on listing provides more relevant info.
-      authorInfo.querySelector("a").href = addon.amoListingUrl;
+      authorInfo.querySelector("a").href =
+        formatAmoUrl("discopane-entry-link", addon.amoListingUrl);
       authorInfo.hidden = false;
     }
   }
 
   setCardDescription(card, addon) {
     // Set the description. Note that this is the editorial description, not
     // the add-on's original description that would normally appear on a card.
     card.querySelector(".disco-description-main")
@@ -1561,19 +1587,20 @@ class DiscoveryPane extends HTMLElement 
     let action = event.target.getAttribute("action");
     switch (action) {
       case "notice-learn-more":
         windowRoot.ownerGlobal.openTrustedLinkIn(
           Services.urlFormatter.formatURLPref("app.support.baseURL") +
           "personalized-extension-recommendations", "tab");
         break;
       case "open-amo":
-        windowRoot.ownerGlobal.openTrustedLinkIn(
-          Services.urlFormatter.formatURLPref("extensions.getAddons.link.url"),
-          "tab");
+        let amoUrl =
+          Services.urlFormatter.formatURLPref("extensions.getAddons.link.url");
+        amoUrl = formatAmoUrl("find-more-link-bottom", amoUrl);
+        windowRoot.ownerGlobal.openTrustedLinkIn(amoUrl, "tab");
         break;
     }
   }
 }
 
 customElements.define("discovery-pane", DiscoveryPane);
 
 class ListView {
--- a/toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_discover_view.js
@@ -17,17 +17,17 @@ const {
 // https://addons-server.readthedocs.io/en/latest/topics/api/discovery.html
 //
 // The test is designed to easily verify whether the discopane works with the
 // latest AMO API, by replacing API_RESPONSE_FILE's content with latest AMO API
 // response, e.g. from https://addons.allizom.org/api/v4/discovery/?lang=en-US
 // The response must contain at least one theme, and one extension.
 const API_RESPONSE_FILE = RELATIVE_DIR + "discovery/api_response.json";
 
-const AMO_TEST_HOST = "addons.example.com";
+const AMO_TEST_HOST = "rewritten-for-testing.addons.allizom.org";
 
 const ArrayBufferInputStream =
   Components.Constructor("@mozilla.org/io/arraybuffer-input-stream;1",
                          "nsIArrayBufferInputStream", "setData");
 
 AddonTestUtils.initMochitest(this);
 
 // `result` is an element in the `results` array from AMO's discovery API,
@@ -420,16 +420,20 @@ add_task(async function discopane_with_r
     let checkContent = (selector, expectation) => {
       let text = card.querySelector(selector).textContent;
       is(text, expectation, `Content of selector "${selector}"`);
     };
     checkContent(".disco-addon-name", expectations.addonName);
     checkContent(".disco-addon-author [data-l10n-name='author']",
                  expectations.authorName);
 
+    let amoListingLink = card.querySelector(".disco-addon-author a");
+    ok(amoListingLink.search.includes("utm_source=firefox-browser"),
+       `Listing link should have attribution parameter, url=${amoListingLink}`);
+
     let actions = getVisibleActions(card);
     is(actions.length, 1, "Card should only have one install button");
     let installButton = actions[0];
     if (expectations.typeIsTheme) {
       // Theme button + screenshot
       ok(installButton.matches("[data-l10n-id='install-theme-button'"),
          "Has theme install button");
       ok(card.querySelector(".card-heading-image").offsetWidth,