Bug 1546248 - Add attribution parameters to AMO links r=mstriemer
authorRob Wu <rob@robwu.nl>
Thu, 09 May 2019 14:22:17 +0000
changeset 532038 077dec75c81a580e49256b4e988daab15e54427b
parent 532037 53535f2ceb4c5569b7aa615ec1aee176bed92e17
child 532039 af2c36273f0c06c24d6dcc5625805f92f390eec0
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
@@ -157,16 +157,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 {
   /**
    * @param {object} details
    *        An item in the "results" array from AMO's discovery API.
    */
   constructor(details) {
@@ -1177,17 +1202,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")
@@ -1693,19 +1719,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
@@ -16,17 +16,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);
 
 const amoServer = AddonTestUtils.createHttpServer({hosts: [AMO_TEST_HOST]});
@@ -315,16 +315,20 @@ add_task(async function discopane_with_r
       let text = card.querySelector(selector).textContent;
       is(text, expectation, `Content of selector "${selector}"`);
     };
     checkContent(".disco-addon-name", expectations.addonName);
     await win.document.l10n.translateFragment(card);
     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,