--- 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;