Bug 1437803 - Add telemetry to understand how many users use TAB to cycle through Address Bar results. r=adw, dzeber
MozReview-Commit-ID: 6zdiIoxVIn1
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -264,16 +264,26 @@ file, You can obtain one at http://mozil
switch (aEvent.keyCode) {
case KeyEvent.DOM_VK_LEFT:
case KeyEvent.DOM_VK_RIGHT:
case KeyEvent.DOM_VK_HOME:
// Reset the selected index so that nsAutoCompleteController
// simply closes the popup without trying to fill anything.
this.popup.selectedIndex = -1;
break;
+ case KeyEvent.DOM_VK_TAB:
+ this.userSelectionBehavior = "tab";
+ break;
+ case KeyEvent.DOM_VK_UP:
+ case KeyEvent.DOM_VK_DOWN:
+ case KeyEvent.DOM_VK_PAGE_UP:
+ case KeyEvent.DOM_VK_PAGE_DOWN:
+ if (this.userSelectionBehavior != "tab")
+ this.userSelectionBehavior = "arrow";
+ break;
}
if (!this.popup.disableKeyNavigation) {
if (this._shouldDeferKeyEvent(aEvent)) {
this._deferKeyEvent(aEvent, "onKeyPress");
return false;
}
if (this.popup.popupOpen && this.popup.handleKeyPress(aEvent)) {
return true;
@@ -662,17 +672,18 @@ file, You can obtain one at http://mozil
<parameter name="triggeringPrincipal"/>
<body><![CDATA[
let isMouseEvent = event instanceof MouseEvent;
if (isMouseEvent && event.button == 2) {
// Do nothing for right clicks.
return;
}
- BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(event);
+ BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(
+ event, this.userSelectionBehavior);
// 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 &&
@@ -2047,17 +2058,18 @@ file, You can obtain one at http://mozil
// according to whatever's in the CSS.
this.margins = undefined;
needsHandleOverUnderflow = true;
}
// Now that the margins have been set, start adding items (via
// _invalidate).
this.mInput = aInput;
- aInput.controller.setInitiallySelectedIndex(this._isFirstResultHeuristic ? 0 : -1);
+ this.input.controller.setInitiallySelectedIndex(this._isFirstResultHeuristic ? 0 : -1);
+ this.input.userSelectionBehavior = "none";
this._invalidate();
try {
let whichNotification = aInput.whichSearchSuggestionsNotification;
if (whichNotification != "none") {
// Update the impressions count on real popupshown, since there's
// no guarantee openPopup will be respected by the platform.
// Though, we must ensure the handled event is the expected one.
@@ -2217,17 +2229,17 @@ file, You can obtain one at http://mozil
<method name="createResultLabel">
<parameter name="item"/>
<parameter name="proposedLabel"/>
<body>
<![CDATA[
let parts = [proposedLabel];
- let action = this.mInput._parseActionUrl(item.getAttribute("url"));
+ let action = this.input._parseActionUrl(item.getAttribute("url"));
if (action) {
switch (action.type) {
case "searchengine":
parts = [
action.params.searchSuggestion || action.params.searchQuery,
action.params.engineName,
];
break;
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -76,16 +76,18 @@ const URLBAR_SELECTED_RESULT_TYPES = {
* `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,
+ arrowEnterSelection: 3,
+ tabEnterSelection: 4,
};
const MINIMUM_TAB_COUNT_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes, in ms
function getOpenTabsAndWinsCounts() {
let tabCount = 0;
@@ -504,56 +506,71 @@ let BrowserUsageTelemetry = {
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.
+ * @param {string} userSelectionBehavior
+ * How the user cycled through results before picking the current match.
+ * Could be one of "tab", "arrow" or "none".
*/
- recordUrlbarSelectedResultMethod(event) {
+ recordUrlbarSelectedResultMethod(event, userSelectionBehavior = "none") {
// 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"
+ "FX_URLBAR_SELECTED_RESULT_METHOD",
+ userSelectionBehavior
);
},
/**
* 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"
+ "FX_SEARCHBAR_SELECTED_RESULT_METHOD",
+ "none"
);
},
- _recordUrlOrSearchbarSelectedResultMethod(event, highlightedIndex, histogramID) {
+ _recordUrlOrSearchbarSelectedResultMethod(event, highlightedIndex, histogramID, userSelectionBehavior) {
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";
+ switch (userSelectionBehavior) {
+ case "tab":
+ category = "tabEnterSelection";
+ break;
+ case "arrow":
+ category = "arrowEnterSelection";
+ break;
+ default:
+ category = "enterSelection";
+ }
} else {
category = "enter";
}
histogram.add(category);
},
/**
* This gets called shortly after the SessionStore has finished restoring
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -320,17 +320,17 @@ add_task(async function test_oneOff_ente
info("Select the second result, press Alt+Down to take us to the first one-off engine.");
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_ArrowDown", {altKey: true});
EventUtils.synthesizeKey("KEY_Enter");
await p;
let resultMethods = resultMethodHist.snapshot();
checkHistogramResults(resultMethods,
- URLBAR_SELECTED_RESULT_METHODS.enterSelection,
+ URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection,
"FX_URLBAR_SELECTED_RESULT_METHOD");
Services.search.currentEngine = previousEngine;
Services.search.removeEngine(suggestionEngine);
await BrowserTestUtils.removeTab(tab);
});
// Performs a search using a click on a one-off button. This only tests the
@@ -430,17 +430,17 @@ add_task(async function test_suggestion_
Services.search.currentEngine = previousEngine;
Services.search.removeEngine(suggestionEngine);
await 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(async function test_suggestion_enterSelection() {
+add_task(async function test_suggestion_arrowEnterSelection() {
// Let's reset the counts.
Services.telemetry.clearScalars();
let resultMethodHist = getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD");
// Create an engine to generate search suggestions and add it as default
// for this test.
const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
@@ -461,15 +461,97 @@ add_task(async function test_suggestion_
await searchInAwesomebar("query");
info("Select the second result and press Return.");
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_Enter");
await p;
let resultMethods = resultMethodHist.snapshot();
checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.arrowEnterSelection,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ await BrowserTestUtils.removeTab(tab);
+});
+
+// Selects through tab and presses the Return (Enter) key on the first
+// suggestion offered by the test search engine.
+add_task(async function test_suggestion_tabEnterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+
+ let resultMethodHist = getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = await 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 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ await searchInAwesomebar("query");
+ info("Select the second result and press Return.");
+ EventUtils.synthesizeKey("KEY_Tab");
+ EventUtils.synthesizeKey("KEY_Enter");
+ await p;
+
+ let resultMethods = resultMethodHist.snapshot();
+ checkHistogramResults(resultMethods,
+ URLBAR_SELECTED_RESULT_METHODS.tabEnterSelection,
+ "FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ Services.search.currentEngine = previousEngine;
+ Services.search.removeEngine(suggestionEngine);
+ await BrowserTestUtils.removeTab(tab);
+});
+
+// Selects through code and presses the Return (Enter) key on the first
+// suggestion offered by the test search engine.
+add_task(async function test_suggestion_enterSelection() {
+ // Let's reset the counts.
+ Services.telemetry.clearScalars();
+
+ let resultMethodHist = getAndClearHistogram("FX_URLBAR_SELECTED_RESULT_METHOD");
+
+ // Create an engine to generate search suggestions and add it as default
+ // for this test.
+ const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
+ let suggestionEngine = await 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 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+
+ info("Type a query. Suggestions should be generated by the test engine.");
+ let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ await searchInAwesomebar("query");
+ info("Select the second result and press Return.");
+ gURLBar.popup.selectedIndex = 1;
+ EventUtils.synthesizeKey("KEY_Enter");
+ await 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);
await BrowserTestUtils.removeTab(tab);
});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6738,20 +6738,22 @@
"record_in_processes": ["main", "content"],
"alert_emails": ["dzeber@mozilla.com"],
"expires_in_version": "63",
"releaseChannelCollection": "opt-out",
"kind": "categorical",
"labels": [
"enter",
"enterSelection",
- "click"
+ "click",
+ "arrowEnterSelection",
+ "tabEnterSelection"
],
"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."
+ "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 (in exotic ways) and then hit the Enter key. 'click' => The user clicked a result with the mouse. 'arrowEnterSelection' => The user chose a non-heuristic result using arrow keys and then hit the Enter key. 'tabEnterSelection' => The user chose a non-heuristic result using tab at least once and then hit the Enter key."
},
"FX_SEARCHBAR_SELECTED_RESULT_METHOD": {
"record_in_processes": ["main", "content"],
"alert_emails": ["dzeber@mozilla.com"],
"expires_in_version": "63",
"releaseChannelCollection": "opt-out",
"kind": "categorical",
"labels": [