--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -372,16 +372,18 @@ file, You can obtain one at http://mozil
<parameter name="openUILinkParams"/>
<body><![CDATA[
let isMouseEvent = event instanceof MouseEvent;
if (isMouseEvent && event.button == 2) {
// Do nothing for right clicks.
return;
}
+ BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(event);
+
// Determine whether to use the selected one-off search button. In
// one-off search buttons parlance, "selected" means that the button
// has been navigated to via the keyboard. So we want to use it if
// the triggering event is not a mouse click -- i.e., it's a Return
// key -- or if the one-off was mouse-clicked.
let selectedOneOff = this.popup.oneOffSearchButtons.selectedButton;
if (selectedOneOff &&
isMouseEvent &&
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -373,16 +373,21 @@
<parameter name="aParams"/>
<body><![CDATA[
var textBox = this._textbox;
var textValue = textBox.value;
let selection = this.telemetrySearchDetails;
let oneOffRecorded = false;
+ BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
+ aEvent,
+ selection ? selection.index : -1
+ );
+
if (!selection || (selection.index == -1)) {
oneOffRecorded = this.textbox.popup.oneOffButtons
.maybeRecordTelemetry(aEvent, aWhere, aParams);
if (!oneOffRecorded) {
let source = "unknown";
let type = "unknown";
let target = aEvent.originalTarget;
if (aEvent instanceof KeyboardEvent) {
@@ -966,16 +971,21 @@
if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
!aEvent.altKey && !aEvent.metaKey) {
controller.handleEnter(true, aEvent);
return;
}
// Check for middle-click or modified clicks on the search bar
if (popupForSearchBar) {
+ BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
+ aEvent,
+ this.selectedIndex
+ );
+
// Handle search bar popup clicks
var search = controller.getValueAt(this.selectedIndex);
// open the search results according to the clicking subtlety
var where = whereToOpenLink(aEvent, false, true);
let params = {};
// But open ctrl/cmd clicks on autocomplete items in a new background tab.
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -1,16 +1,20 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* 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";
-this.EXPORTED_SYMBOLS = ["BrowserUsageTelemetry", "URLBAR_SELECTED_RESULT_TYPES"];
+this.EXPORTED_SYMBOLS = [
+ "BrowserUsageTelemetry",
+ "URLBAR_SELECTED_RESULT_TYPES",
+ "URLBAR_SELECTED_RESULT_METHODS",
+ ];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
@@ -63,16 +67,29 @@ const URLBAR_SELECTED_RESULT_TYPES = {
switchtab: 6,
tag: 7,
visiturl: 8,
remotetab: 9,
extension: 10,
"preloaded-top-site": 11,
};
+/**
+ * This maps the categories used by the FX_URLBAR_SELECTED_RESULT_METHOD and
+ * FX_SEARCHBAR_SELECTED_RESULT_METHOD histograms to their indexes in the
+ * `labels` array. This only needs to be used by tests that need to map from
+ * category names to indexes in histogram snapshots. Actual app code can use
+ * these category names directly when they add to a histogram.
+ */
+const URLBAR_SELECTED_RESULT_METHODS = {
+ enter: 0,
+ enterSelection: 1,
+ click: 2,
+};
+
function getOpenTabsAndWinsCounts() {
let tabCount = 0;
let winCount = 0;
let browserEnum = Services.wm.getEnumerator("navigator:browser");
while (browserEnum.hasMoreElements()) {
let win = browserEnum.getNext();
winCount++;
@@ -203,16 +220,20 @@ let URICountListener = {
this._domainSet.clear();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
};
let urlbarListener = {
+
+ // This is needed for recordUrlbarSelectedResultMethod().
+ selectedIndex: -1,
+
init() {
Services.obs.addObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC, true);
},
uninit() {
Services.obs.removeObserver(this, AUTOCOMPLETE_ENTER_TEXT_TOPIC);
},
@@ -226,22 +247,32 @@ let urlbarListener = {
/**
* Used to log telemetry when the user enters text in the urlbar.
*
* @param {nsIAutoCompleteInput} input The autocomplete element where the
* text was entered.
*/
_handleURLBarTelemetry(input) {
- if (!input ||
- input.id != "urlbar" ||
- input.inPrivateContext ||
- input.popup.selectedIndex < 0) {
+ if (!input || input.id != "urlbar") {
+ return;
+ }
+ if (input.inPrivateContext || input.popup.selectedIndex < 0) {
+ this.selectedIndex = -1;
return;
}
+
+ // Except for the history popup, the urlbar always has a selection. The
+ // first result at index 0 is the "heuristic" result that indicates what
+ // will happen when you press the Enter key. Treat it as no selection.
+ this.selectedIndex =
+ input.popup.selectedIndex > 0 || !input.popup._isFirstResultHeuristic ?
+ input.popup.selectedIndex :
+ -1;
+
let controller =
input.popup.view.QueryInterface(Ci.nsIAutoCompleteController);
let idx = input.popup.selectedIndex;
let value = controller.getValueAt(idx);
let action = input._parseActionUrl(value);
let actionType;
if (action) {
actionType =
@@ -459,16 +490,67 @@ let BrowserUsageTelemetry = {
return;
}
// The search signal was generated by typing something and pressing enter.
this._recordSearch(engine, sourceName, "enter");
},
/**
+ * Records the method by which the user selected a urlbar result.
+ *
+ * @param {nsIDOMEvent} event
+ * The event that triggered the selection.
+ */
+ recordUrlbarSelectedResultMethod(event) {
+ // The reason this method relies on urlbarListener instead of having the
+ // caller pass in an index is that by the time the urlbar handles a
+ // selection, the selection in its popup has been cleared, so it's not easy
+ // to tell which popup index was selected. Fortunately this file already
+ // has urlbarListener, which gets notified of selections in the urlbar
+ // before the popup selection is cleared, so just use that.
+ this._recordUrlOrSearchbarSelectedResultMethod(
+ event, urlbarListener.selectedIndex,
+ "FX_URLBAR_SELECTED_RESULT_METHOD"
+ );
+ },
+
+ /**
+ * Records the method by which the user selected a searchbar result.
+ *
+ * @param {nsIDOMEvent} event
+ * The event that triggered the selection.
+ * @param {number} highlightedIndex
+ * The index that the user chose in the popup, or -1 if there wasn't a
+ * selection.
+ */
+ recordSearchbarSelectedResultMethod(event, highlightedIndex) {
+ this._recordUrlOrSearchbarSelectedResultMethod(
+ event, highlightedIndex,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD"
+ );
+ },
+
+ _recordUrlOrSearchbarSelectedResultMethod(event, highlightedIndex, histogramID) {
+ let histogram = Services.telemetry.getHistogramById(histogramID);
+ // command events are from the one-off context menu. Treat them as clicks.
+ let isClick = event instanceof Ci.nsIDOMMouseEvent ||
+ (event && event.type == "command");
+ let category;
+ if (isClick) {
+ category = "click";
+ } else if (highlightedIndex >= 0) {
+ category = "enterSelection";
+ } else {
+ category = "enter";
+ }
+ histogram.add(category);
+ },
+
+ /**
* This gets called shortly after the SessionStore has finished restoring
* windows and tabs. It counts the open tabs and adds listeners to all the
* windows.
*/
_setupAfterRestore() {
// Make sure to catch new chrome windows and subsession splits.
Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, false);
Services.obs.addObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, false);
--- a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js
@@ -1,12 +1,27 @@
"use strict";
const SCALAR_SEARCHBAR = "browser.engagement.navigation.searchbar";
+XPCOMUtils.defineLazyModuleGetter(this, "URLBAR_SELECTED_RESULT_METHODS",
+ "resource:///modules/BrowserUsageTelemetry.jsm");
+
+function checkHistogramResults(resultIndexes, expected, histogram) {
+ for (let i = 0; i < resultIndexes.counts.length; i++) {
+ if (i == expected) {
+ Assert.equal(resultIndexes.counts[i], 1,
+ `expected counts should match for ${histogram} index ${i}`);
+ } else {
+ Assert.equal(resultIndexes.counts[i], 0,
+ `unexpected counts should be zero for ${histogram} index ${i}`);
+ }
+ }
+}
+
let searchInSearchbar = Task.async(function* (inputText) {
let win = window;
yield new Promise(r => waitForFocus(r, win));
let sb = BrowserSearch.searchBar;
// Write the search query in the searchbar.
sb.focus();
sb.value = inputText;
sb.textbox.controller.startSearch(inputText);
@@ -62,35 +77,42 @@ add_task(function* setup() {
let engineDefault = Services.search.getEngineByName("MozSearch");
let originalEngine = Services.search.currentEngine;
Services.search.currentEngine = engineDefault;
// Move the second engine at the beginning of the one-off list.
let engineOneOff = Services.search.getEngineByName("MozSearch2");
Services.search.moveEngine(engineOneOff, 0);
+ // Enable local telemetry recording for the duration of the tests.
+ let oldCanRecord = Services.telemetry.canRecordExtended;
+ Services.telemetry.canRecordExtended = true;
+
// Enable Extended Telemetry.
yield SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]});
// Enable event recording for the events tested here.
Services.telemetry.setEventRecordingEnabled("navigation", true);
// Make sure to restore the engine once we're done.
registerCleanupFunction(function* () {
+ Services.telemetry.canRecordExtended = oldCanRecord;
Services.search.currentEngine = originalEngine;
Services.search.removeEngine(engineDefault);
Services.search.removeEngine(engineOneOff);
Services.telemetry.setEventRecordingEnabled("navigation", false);
});
});
add_task(function* test_plainQuery() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
info("Simulate entering a simple search.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInSearchbar("simple query");
EventUtils.sendKey("return");
@@ -105,23 +127,33 @@ add_task(function* test_plainQuery() {
// Make sure SEARCH_COUNTS contains identical values.
checkKeyedHistogram(search_hist, "other-MozSearch.searchbar", 1);
// Also check events.
let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
checkEvents(events, [["navigation", "search", "searchbar", "enter", {engine: "other-MozSearch"}]]);
+ // Check the histograms as well.
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enter,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
yield BrowserTestUtils.removeTab(tab);
});
-add_task(function* test_oneOff() {
+// Performs a search using the first result, a one-off button, and the Return
+// (Enter) key.
+add_task(function* test_oneOff_enter() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
info("Perform a one-off search using the first engine.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInSearchbar("query");
@@ -139,23 +171,102 @@ add_task(function* test_oneOff() {
// Make sure SEARCH_COUNTS contains identical values.
checkKeyedHistogram(search_hist, "other-MozSearch2.searchbar", 1);
// Also check events.
let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
checkEvents(events, [["navigation", "search", "searchbar", "oneoff", {engine: "other-MozSearch2"}]]);
+ // Check the histograms as well.
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enter,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
yield BrowserTestUtils.removeTab(tab);
});
-add_task(function* test_suggestion() {
+// Performs a search using the second result, a one-off button, and the Return
+// (Enter) key. This only tests the FX_SEARCHBAR_SELECTED_RESULT_METHOD
+// histogram since test_oneOff_enter covers everything else.
+add_task(function* test_oneOff_enterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = yield new Promise((resolve, reject) => {
+ Services.search.addEngine(url, null, "", false, {
+ onSuccess(engine) { resolve(engine) },
+ onError() { reject() }
+ });
+ });
+
+ let previousEngine = Services.search.currentEngine;
+ Services.search.currentEngine = suggestionEngine;
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInSearchbar("query");
+
+ info("Select the second result, press Alt+Down to take us to the first one-off engine.");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
+ EventUtils.sendKey("return");
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enterSelection,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ yield BrowserTestUtils.removeTab(tab);
+});
+
+// Performs a search using a click on a one-off button. This only tests the
+// FX_SEARCHBAR_SELECTED_RESULT_METHOD histogram since test_oneOff_enter covers
+// everything else.
+add_task(function* test_oneOff_click() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInSearchbar("query");
+ info("Click the first one-off button.");
+ BrowserSearch.searchBar.textbox.popup.oneOffButtons.getSelectableButtons(false)[0].click();
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.click,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
+ yield BrowserTestUtils.removeTab(tab);
+});
+
+// Clicks the first suggestion offered by the test search engine.
+add_task(function* test_suggestion_click() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
// Create an engine to generate search suggestions and add it as default
// for this test.
const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
let suggestionEngine = yield new Promise((resolve, reject) => {
Services.search.addEngine(url, null, "", false, {
onSuccess(engine) { resolve(engine) },
@@ -185,12 +296,61 @@ add_task(function* test_suggestion() {
let searchEngineId = "other-" + suggestionEngine.name;
checkKeyedHistogram(search_hist, searchEngineId + ".searchbar", 1);
// Also check events.
let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
checkEvents(events, [["navigation", "search", "searchbar", "suggestion", {engine: searchEngineId}]]);
+ // Check the histograms as well.
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.click,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
Services.search.currentEngine = previousEngine;
Services.search.removeEngine(suggestionEngine);
yield BrowserTestUtils.removeTab(tab);
});
+
+// Selects and presses the Return (Enter) key on the first suggestion offered by
+// the test search engine. This only tests the
+// FX_SEARCHBAR_SELECTED_RESULT_METHOD histogram since test_suggestion_click
+// covers everything else.
+add_task(function* test_suggestion_enterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = yield new Promise((resolve, reject) => {
+ Services.search.addEngine(url, null, "", false, {
+ onSuccess(engine) { resolve(engine) },
+ onError() { reject() }
+ });
+ });
+
+ let previousEngine = Services.search.currentEngine;
+ Services.search.currentEngine = suggestionEngine;
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInSearchbar("query");
+ info("Select the second result and press Return.");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ EventUtils.sendKey("return");
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enterSelection,
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -6,16 +6,19 @@ const SCALAR_URLBAR = "browser.engagemen
const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
// The name of the search engine used to generate suggestions.
const SUGGESTION_ENGINE_NAME = "browser_UsageTelemetry usageTelemetrySearchSuggestions.xml";
const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches";
XPCOMUtils.defineLazyModuleGetter(this, "URLBAR_SELECTED_RESULT_TYPES",
"resource:///modules/BrowserUsageTelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "URLBAR_SELECTED_RESULT_METHODS",
+ "resource:///modules/BrowserUsageTelemetry.jsm");
+
function checkHistogramResults(resultIndexes, expected, histogram) {
for (let i = 0; i < resultIndexes.counts.length; i++) {
if (i == expected) {
Assert.equal(resultIndexes.counts[i], 1,
`expected counts should match for ${histogram} index ${i}`);
} else {
Assert.equal(resultIndexes.counts[i], 0,
`unexpected counts should be zero for ${histogram} index ${i}`);
@@ -79,16 +82,20 @@ add_task(function* setup() {
// Enable local telemetry recording for the duration of the tests.
let oldCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
// Enable event recording for the events tested here.
Services.telemetry.setEventRecordingEnabled("navigation", true);
+ // Clear history so that history added by previous tests doesn't mess up this
+ // test when it selects results in the urlbar.
+ yield PlacesTestUtils.clearHistory();
+
// Make sure to restore the engine once we're done.
registerCleanupFunction(function* () {
Services.telemetry.canRecordExtended = oldCanRecord;
Services.search.currentEngine = originalEngine;
Services.search.removeEngine(engine);
Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF);
Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
yield PlacesTestUtils.clearHistory();
@@ -97,20 +104,22 @@ add_task(function* setup() {
});
add_task(function* test_simpleQuery() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX");
let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE");
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
let resultIndexByTypeHist = Services.telemetry.getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
resultIndexByTypeHist.clear();
resultIndexHist.clear();
resultTypeHist.clear();
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
info("Simulate entering a simple search.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInAwesomebar("simple query");
@@ -140,29 +149,36 @@ add_task(function* test_simpleQuery() {
URLBAR_SELECTED_RESULT_TYPES.searchengine,
"FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByType = resultIndexByTypeHist.snapshot("searchengine");
checkHistogramResults(resultIndexByType,
0,
"FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enter,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
yield BrowserTestUtils.removeTab(tab);
});
add_task(function* test_searchAlias() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX");
let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByTypeHist = Services.telemetry.getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
resultIndexByTypeHist.clear();
resultIndexHist.clear();
resultTypeHist.clear();
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
info("Search using a search alias.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInAwesomebar("mozalias query");
@@ -192,29 +208,38 @@ add_task(function* test_searchAlias() {
URLBAR_SELECTED_RESULT_TYPES.searchengine,
"FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByType = resultIndexByTypeHist.snapshot("searchengine");
checkHistogramResults(resultIndexByType,
0,
"FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enter,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
yield BrowserTestUtils.removeTab(tab);
});
-add_task(function* test_oneOff() {
+// Performs a search using the first result, a one-off button, and the Return
+// (Enter) key.
+add_task(function* test_oneOff_enter() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX");
let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByTypeHist = Services.telemetry.getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
resultIndexByTypeHist.clear();
resultIndexHist.clear();
resultTypeHist.clear();
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
info("Perform a one-off search using the first engine.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInAwesomebar("query");
@@ -247,29 +272,109 @@ add_task(function* test_oneOff() {
URLBAR_SELECTED_RESULT_TYPES.searchengine,
"FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByType = resultIndexByTypeHist.snapshot("searchengine");
checkHistogramResults(resultIndexByType,
0,
"FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enter,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
yield BrowserTestUtils.removeTab(tab);
});
-add_task(function* test_suggestion() {
+// Performs a search using the second result, a one-off button, and the Return
+// (Enter) key. This only tests the FX_URLBAR_SELECTED_RESULT_METHOD histogram
+// since test_oneOff_enter covers everything else.
+add_task(function* test_oneOff_enterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = yield new Promise((resolve, reject) => {
+ Services.search.addEngine(url, null, "", false, {
+ onSuccess(engine) { resolve(engine) },
+ onError() { reject() }
+ });
+ });
+
+ let previousEngine = Services.search.currentEngine;
+ Services.search.currentEngine = suggestionEngine;
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInAwesomebar("query");
+
+ info("Select the second result, press Alt+Down to take us to the first one-off engine.");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ EventUtils.synthesizeKey("VK_DOWN", { altKey: true });
+ EventUtils.sendKey("return");
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enterSelection,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ yield BrowserTestUtils.removeTab(tab);
+});
+
+// Performs a search using a click on a one-off button. This only tests the
+// FX_URLBAR_SELECTED_RESULT_METHOD histogram since test_oneOff_enter covers
+// everything else.
+add_task(function* test_oneOff_click() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInAwesomebar("query");
+ info("Click the first one-off button.");
+ gURLBar.popup.oneOffSearchButtons.getSelectableButtons(false)[0].click();
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.click,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ yield BrowserTestUtils.removeTab(tab);
+});
+
+// Clicks the first suggestion offered by the test search engine.
+add_task(function* test_suggestion_click() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
let resultIndexHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX");
let resultTypeHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByTypeHist = Services.telemetry.getKeyedHistogramById("FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
resultIndexByTypeHist.clear();
resultIndexHist.clear();
resultTypeHist.clear();
+ resultMethodHist.clear();
let search_hist = getSearchCountsHistogram();
// Create an engine to generate search suggestions and add it as default
// for this test.
const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
let suggestionEngine = yield new Promise((resolve, reject) => {
Services.search.addEngine(url, null, "", false, {
@@ -278,17 +383,17 @@ add_task(function* test_suggestion() {
});
});
let previousEngine = Services.search.currentEngine;
Services.search.currentEngine = suggestionEngine;
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
- info("Perform a one-off search using the first engine.");
+ info("Type a query. Suggestions should be generated by the test engine.");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield searchInAwesomebar("query");
info("Clicking the urlbar suggestion.");
clickURLBarSuggestion("queryfoo");
yield p;
// Check if the scalars contain the expected values.
const scalars = getParentProcessScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true, false);
@@ -314,12 +419,60 @@ add_task(function* test_suggestion() {
URLBAR_SELECTED_RESULT_TYPES.searchsuggestion,
"FX_URLBAR_SELECTED_RESULT_TYPE");
let resultIndexByType = resultIndexByTypeHist.snapshot("searchsuggestion");
checkHistogramResults(resultIndexByType,
3,
"FX_URLBAR_SELECTED_RESULT_INDEX_BY_TYPE");
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.click,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
Services.search.currentEngine = previousEngine;
Services.search.removeEngine(suggestionEngine);
yield BrowserTestUtils.removeTab(tab);
});
+
+// Selects and presses the Return (Enter) key on the first suggestion offered by
+// the test search engine. This only tests the FX_URLBAR_SELECTED_RESULT_METHOD
+// histogram since test_suggestion_click covers everything else.
+add_task(function* test_suggestion_enterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+
+ let resultMethodHist = Services.telemetry.getHistogramById("FX_URLBAR_SELECTED_RESULT_METHOD");
+ resultMethodHist.clear();
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = yield new Promise((resolve, reject) => {
+ Services.search.addEngine(url, null, "", false, {
+ onSuccess(engine) { resolve(engine) },
+ onError() { reject() }
+ });
+ });
+
+ let previousEngine = Services.search.currentEngine;
+ Services.search.currentEngine = suggestionEngine;
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ yield searchInAwesomebar("query");
+ info("Select the second result and press Return.");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ EventUtils.sendKey("return");
+ yield p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.enterSelection,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ yield BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5906,16 +5906,42 @@
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 14,
"keyed": true,
"releaseChannelCollection": "opt-out",
"bug_numbers": [1345834],
"description": "Firefox: The index of the selected result in the URL bar popup by the type of the selected result in the URL bar popup. See BrowserUsageTelemetry.jsm:URLBAR_SELECTED_RESULT_TYPES for the result types."
},
+ "FX_URLBAR_SELECTED_RESULT_METHOD": {
+ "alert_emails": ["dzeber@mozilla.com"],
+ "expires_in_version": "63",
+ "releaseChannelCollection": "opt-out",
+ "kind": "categorical",
+ "labels": [
+ "enter",
+ "enterSelection",
+ "click"
+ ],
+ "bug_numbers": [1334615],
+ "description": "The input method the user used to select a result in the urlbar. 'enter' => The user hit the Enter key on the heuristic result at index 0. 'enterSelection' => The user chose a non-heuristic result and then hit the Enter key. 'click' => The user clicked a result with the mouse."
+ },
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD": {
+ "alert_emails": ["dzeber@mozilla.com"],
+ "expires_in_version": "63",
+ "releaseChannelCollection": "opt-out",
+ "kind": "categorical",
+ "labels": [
+ "enter",
+ "enterSelection",
+ "click"
+ ],
+ "bug_numbers": [1334615],
+ "description": "The input method the user used to select a result in the searchbar. 'enter' => The user hit the Enter key without choosing a result in the popup. 'enterSelection' => The user chose a result and then hit the Enter key. 'click' => The user clicked a result with the mouse."
+ },
"INNERWINDOWS_WITH_MUTATION_LISTENERS": {
"expires_in_version": "never",
"kind": "boolean",
"description": "Deleted or to-be-reused innerwindow which has had mutation event listeners."
},
"CHARSET_OVERRIDE_SITUATION": {
"expires_in_version": "never",
"kind": "enumerated",