Bug 1404126 - Upgrade Follow-on search add-on to 0.9.5. r?past draft
authorMark Banner <standard8@mozilla.com>
Fri, 06 Oct 2017 13:13:31 +0100
changeset 676015 d8329af3adce6b9c0a932685d7559769dd6780d3
parent 675689 19b32a138d08f73961df878a29de6f0aad441683
child 734817 b18425c56ab93a5313671b67e4ec7a263d7352d9
push id83358
push userbmo:standard8@mozilla.com
push dateFri, 06 Oct 2017 12:20:42 +0000
reviewerspast
bugs1404126
milestone58.0a1
Bug 1404126 - Upgrade Follow-on search add-on to 0.9.5. r?past
browser/extensions/followonsearch/bootstrap.js
browser/extensions/followonsearch/content/followonsearch-fs.js
browser/extensions/followonsearch/install.rdf
--- a/browser/extensions/followonsearch/bootstrap.js
+++ b/browser/extensions/followonsearch/bootstrap.js
@@ -1,12 +1,14 @@
 /* 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/. */
 
+/* global APP_SHUTDOWN:false */
+
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
 // Preferences this add-on uses.
@@ -54,17 +56,21 @@ function handleSaveTelemetryMsg(message)
 
   if (!validSearchTypes.includes(info.type)) {
     throw new Error("Unexpected type!");
   }
 
   log(info);
 
   let histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
-  histogram.add(`${info.sap}.${info.type}:unknown:${info.code}`);
+  let payload = `${info.sap}.${info.type}:unknown:${info.code}`;
+  if (info.extra) {
+    payload += `:${info.extra}`
+  }
+  histogram.add(payload);
 }
 
 /**
  * Activates recording of telemetry if it isn't already activated.
  */
 function activateTelemetry() {
   if (gTelemetryActivated) {
     return;
@@ -128,36 +134,27 @@ var cohortManager = {
 
 /**
  * Called when the add-on is installed.
  *
  * @param {Object} data Data about the add-on.
  * @param {Number} reason Indicates why the extension is being installed.
  */
 function install(data, reason) {
-  try {
-    gLoggingEnabled = Services.prefs.getBoolPref(PREF_LOGGING, false);
-  } catch (e) {
-    // Needed until Firefox 54
-  }
-
-  cohortManager.init();
-  if (cohortManager.enableForUser) {
-    activateTelemetry();
-  }
+  // Nothing specifically to do, startup will set everything up for us.
 }
 
 /**
  * Called when the add-on is uninstalled.
  *
  * @param {Object} data Data about the add-on.
  * @param {Number} reason Indicates why the extension is being uninstalled.
  */
 function uninstall(data, reason) {
-  deactivateTelemetry();
+  // Nothing specifically to do, shutdown does what we need.
 }
 
 /**
  * Called when the add-on starts up.
  *
  * @param {Object} data Data about the add-on.
  * @param {Number} reason Indicates why the extension is being started.
  */
@@ -182,10 +179,15 @@ function startup(data, reason) {
 
 /**
  * Called when the add-on shuts down.
  *
  * @param {Object} data Data about the add-on.
  * @param {Number} reason Indicates why the extension is being shut down.
  */
 function shutdown(data, reason) {
+  // If we're shutting down, skip the cleanup to save time.
+  if (reason === APP_SHUTDOWN) {
+    return;
+  }
+
   deactivateTelemetry();
 }
--- a/browser/extensions/followonsearch/content/followonsearch-fs.js
+++ b/browser/extensions/followonsearch/content/followonsearch-fs.js
@@ -8,33 +8,34 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.importGlobalProperties(["URLSearchParams"]);
 
 const kExtensionID = "followonsearch@mozilla.com";
 const kSaveTelemetryMsg = `${kExtensionID}:save-telemetry`;
 const kShutdownMsg = `${kExtensionID}:shutdown`;
+const kLastSearchQueueDepth = 10;
 
 /**
  * A map of search domains with their expected codes.
  */
 let searchDomains = [{
   "domains": [ "search.yahoo.co.jp" ],
   "search": "p",
   "followOnSearch": "ai",
   "prefix": "fr",
   "codes": ["mozff"],
   "sap": "yahoo",
 }, {
   "domains": [ "www.bing.com" ],
   "search": "q",
   "prefix": "pc",
   "reportPrefix": "form",
-  "codes": ["MOZI"],
+  "codes": ["MOZI", "MOZD", "MZSL01", "MZSL02", "MZSL03", "MOZ2"],
   "sap": "bing",
 }, {
   // The Yahoo domains to watch for.
   "domains": [
     "search.yahoo.com", "ca.search.yahoo.com", "hk.search.yahoo.com",
     "tw.search.yahoo.com", "mozilla.search.yahoo.com", "us.search.yahoo.com"
   ],
   "search": "p",
@@ -134,70 +135,105 @@ function getSearchDomainCodes(host) {
  * Used for debugging to log messages.
  *
  * @param {String} message The message to log.
  */
 function log(message) {
   // console.log(message);
 }
 
-// Hack to handle the most common reload case.
-// If gLastSearch is the same as the current URL, ignore the search.
+// Hack to handle the most common reload/back/forward case.
+// If gLastSearchQueue includes the current URL, ignore the search.
 // This also prevents us from handling reloads with hashes twice
-let gLastSearch = null;
+let gLastSearchQueue = [];
+gLastSearchQueue.push = function(...args) {
+  if (this.length >= kLastSearchQueueDepth) {
+    this.shift();
+  }
+  return Array.prototype.push.apply(this, args);
+};
 
-// Keep track of the original window we were loaded in
-// so we don't handle requests for other windows.
-let gOriginalWindow = null;
+// Track if we are in the middle of a Google session
+// that started from Firefox
+let searchingGoogle = false;
 
 /**
  * Since most codes are in the URL, we can handle them via
  * a progress listener.
  */
 var webProgressListener = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags)
   {
-    if (aWebProgress.DOMWindow && (aWebProgress.DOMWindow != gOriginalWindow)) {
+    if (aWebProgress.DOMWindow && (aWebProgress.DOMWindow != content)) {
       return;
     }
     try {
       if (!aWebProgress.isTopLevel ||
           // Not a URL
           (!aLocation.schemeIs("http") && !aLocation.schemeIs("https")) ||
           // Doesn't have a query string or a ref
-          (!aLocation.query && !aLocation.ref) ||
-          // Is the same as our last search (avoids reloads)
-          aLocation.spec == gLastSearch) {
+          (!aLocation.query && !aLocation.ref)) {
+        searchingGoogle = false;
+        return;
+      }
+      if (gLastSearchQueue.includes(aLocation.spec)) {
+        // If it's a recent search, just return. We
+        // don't reset searchingGoogle though because
+        // we might still be doing that.
         return;
       }
       let domainInfo = getSearchDomainCodes(aLocation.host);
       if (!domainInfo) {
+        searchingGoogle = false;
         return;
       }
 
       let queries = new URLSearchParams(aLocation.query);
       let code = queries.get(domainInfo.prefix);
+      // Special case Google so we can track searches
+      // without codes from the browser.
+      if (domainInfo.sap == "google") {
+        if (aLocation.filePath == "/search") {
+          gLastSearchQueue.push(aLocation.spec);
+          // Our engine currently sends oe and ie - no one else does
+          if (queries.get("oe") && queries.get("ie")) {
+            sendSaveTelemetryMsg(code ? code : "none", code ? domainInfo.sap : "google-nocodes", "sap");
+            searchingGoogle = true;
+          } else {
+            // The tbm value is the specific type of search (Books, Images, News, etc).
+            // These are referred to as vertical searches.
+            let tbm = queries.get("tbm");
+            if (searchingGoogle) {
+              sendSaveTelemetryMsg(code ? code : "none", code ? domainInfo.sap : "google-nocodes", "follow-on", tbm ? `vertical-${tbm}` : null);
+            } else if (code) {
+              // Trying to do the right thing for back button to existing entries
+              sendSaveTelemetryMsg(code, domainInfo.sap, "follow-on", tbm ? `vertical-${tbm}` : null);
+            }
+          }
+        }
+        // Special case all Google. Otherwise our code can
+        // show up in maps
+        return;
+      }
+      searchingGoogle = false;
       if (queries.get(domainInfo.search)) {
         if (domainInfo.codes.includes(code)) {
           if (domainInfo.reportPrefix &&
               queries.get(domainInfo.reportPrefix)) {
             code = queries.get(domainInfo.reportPrefix);
           }
-          if (domainInfo.sap == "google" && aLocation.ref) {
-            log(`${aLocation.host} search with code ${code} - Follow on`);
-            sendSaveTelemetryMsg(code, domainInfo.sap, "follow-on");
-          } else if (queries.get(domainInfo.followOnSearch)) {
+          if (queries.get(domainInfo.followOnSearch)) {
             log(`${aLocation.host} search with code ${code} - Follow on`);
             sendSaveTelemetryMsg(code, domainInfo.sap, "follow-on");
           } else {
             log(`${aLocation.host} search with code ${code} - First search via Firefox`);
             sendSaveTelemetryMsg(code, domainInfo.sap, "sap");
           }
-          gLastSearch = aLocation.spec;
+          gLastSearchQueue.push(aLocation.spec);
         }
       }
     } catch (e) {
       console.error(e);
     }
   },
 };
 
@@ -231,53 +267,53 @@ function onPageLoad(event) {
   if (win != win.top) {
     return;
   }
   var uri = doc.documentURIObject;
   if (!(uri instanceof Ci.nsIStandardURL) ||
       (!uri.schemeIs("http") && !uri.schemeIs("https")) ||
        uri.host != "www.bing.com" ||
       !doc.location.search ||
-      uri.spec == gLastSearch) {
+      gLastSearchQueue.includes(uri.spec)) {
     return;
   }
   var queries = new URLSearchParams(doc.location.search.toLowerCase());
   // For Bing, QBRE form code is used for all follow-on search
   if (queries.get("form") != "qbre") {
     return;
   }
   if (parseCookies(doc.cookie).SRCHS == "PC=MOZI") {
     log(`${uri.host} search with code MOZI - Follow on`);
     sendSaveTelemetryMsg("MOZI", "bing", "follow-on");
-    gLastSearch = uri.spec;
+    gLastSearchQueue.push(uri.spec);
   }
 }
 
 /**
  * Sends a message to the process that added this script to tell it to save
  * telemetry.
  *
  * @param {String} code The codes used for the search engine.
  * @param {String} sap The SAP code.
  * @param {String} type The type of search (sap/follow-on).
+ * @param {String} extra Any additional parameters (Optional)
  */
-function sendSaveTelemetryMsg(code, sap, type) {
+function sendSaveTelemetryMsg(code, sap, type, extra) {
   sendAsyncMessage(kSaveTelemetryMsg, {
     code,
     sap,
     type,
+    extra,
   });
 }
 
 addEventListener("DOMContentLoaded", onPageLoad, false);
 docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress)
         .addProgressListener(webProgressListener, Ci.nsIWebProgress.NOTIFY_LOCATION);
 
-gOriginalWindow = content;
-
 let gDisabled = false;
 
 addMessageListener(kShutdownMsg, () => {
   if (!gDisabled) {
     removeEventListener("DOMContentLoaded", onPageLoad, false);
     docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress)
             .removeProgressListener(webProgressListener);
     gDisabled = true;
--- a/browser/extensions/followonsearch/install.rdf
+++ b/browser/extensions/followonsearch/install.rdf
@@ -2,17 +2,17 @@
 <!-- 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/. -->
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>followonsearch@mozilla.com</em:id>
     <em:name>Follow-on Search Telemetry</em:name>
-    <em:version>0.9.3</em:version>
+    <em:version>0.9.5</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>52.0</em:minVersion>
         <em:maxVersion>59.*</em:maxVersion>