Bug 1805717: Introduce search_mode property. r=mak,jteow

Differential Revision: https://phabricator.services.mozilla.com/D164794
This commit is contained in:
Daisuke Akatsuka
2022-12-20 07:10:11 +00:00
parent 75d6d575cc
commit 4b4fa32e15
9 changed files with 406 additions and 10 deletions

View File

@@ -957,6 +957,7 @@ class TelemetryEvent {
searchWords, searchWords,
provider: details.provider, provider: details.provider,
searchSource: details.searchSource, searchSource: details.searchSource,
searchMode: details.searchMode,
selectedElement: details.element, selectedElement: details.element,
selIndex: details.selIndex, selIndex: details.selIndex,
selType: details.selType, selType: details.selType,
@@ -1040,6 +1041,7 @@ class TelemetryEvent {
reason, reason,
searchWords, searchWords,
searchSource, searchSource,
searchMode,
selectedElement, selectedElement,
selIndex, selIndex,
selType, selType,
@@ -1057,14 +1059,17 @@ class TelemetryEvent {
sap = "urlbar_addonpage"; sap = "urlbar_addonpage";
} }
searchMode = searchMode ?? this._controller.input.searchMode;
// Distinguish user typed search strings from persisted search terms. // Distinguish user typed search strings from persisted search terms.
const interaction = this.#getInteractionType( const interaction = this.#getInteractionType(
method, method,
startEventInfo, startEventInfo,
searchSource, searchSource,
searchWords searchWords,
searchMode
); );
const search_mode = this.#getSearchMode(searchMode);
const currentResults = queryContext?.results ?? []; const currentResults = queryContext?.results ?? [];
const numResults = currentResults.length; const numResults = currentResults.length;
const groups = currentResults const groups = currentResults
@@ -1080,6 +1085,7 @@ class TelemetryEvent {
eventInfo = { eventInfo = {
sap, sap,
interaction, interaction,
search_mode,
n_chars: numChars, n_chars: numChars,
n_words: numWords, n_words: numWords,
n_results: numResults, n_results: numResults,
@@ -1100,6 +1106,7 @@ class TelemetryEvent {
eventInfo = { eventInfo = {
sap, sap,
interaction, interaction,
search_mode,
n_chars: numChars, n_chars: numChars,
n_words: numWords, n_words: numWords,
n_results: numResults, n_results: numResults,
@@ -1111,6 +1118,7 @@ class TelemetryEvent {
reason, reason,
sap, sap,
interaction, interaction,
search_mode,
n_chars: numChars, n_chars: numChars,
n_words: numWords, n_words: numWords,
n_results: numResults, n_results: numResults,
@@ -1131,8 +1139,14 @@ class TelemetryEvent {
} }
} }
#getInteractionType(method, startEventInfo, searchSource, searchWords) { #getInteractionType(
if (this._controller.input.searchMode?.entry === "topsites_newtab") { method,
startEventInfo,
searchSource,
searchWords,
searchMode
) {
if (searchMode?.entry === "topsites_newtab") {
return "topsite_search"; return "topsite_search";
} }
@@ -1171,6 +1185,21 @@ class TelemetryEvent {
return interaction; return interaction;
} }
#getSearchMode(searchMode) {
if (!searchMode) {
return "";
}
if (searchMode.engineName) {
return "search_engine";
}
const source = lazy.UrlbarUtils.LOCAL_SEARCH_MODES.find(
m => m.source == searchMode.source
)?.telemetryLabel;
return source ?? "unknown";
}
_parseSearchString(searchString) { _parseSearchString(searchString) {
let numChars = searchString.length.toString(); let numChars = searchString.length.toString();
let searchWords = searchString let searchWords = searchString

View File

@@ -929,6 +929,8 @@ export class UrlbarInput {
break; break;
} }
// Keep the searchMode for telemetry since handleRevert sets it to null.
const searchMode = this.searchMode;
this.handleRevert(); this.handleRevert();
let prevTab = this.window.gBrowser.selectedTab; let prevTab = this.window.gBrowser.selectedTab;
let loadOpts = { let loadOpts = {
@@ -941,6 +943,7 @@ export class UrlbarInput {
let searchString = this._lastSearchString; let searchString = this._lastSearchString;
this.controller.engagementEvent.record(event, { this.controller.engagementEvent.record(event, {
searchString, searchString,
searchMode,
selIndex, selIndex,
selType: "tabswitch", selType: "tabswitch",
provider: result.providerName, provider: result.providerName,
@@ -1053,6 +1056,8 @@ export class UrlbarInput {
break; break;
} }
url = result.payload.url; url = result.payload.url;
// Keep the searchMode for telemetry since handleRevert sets it to null.
const searchMode = this.searchMode;
// Do not revert the Urlbar if we're going to navigate. We want the URL // Do not revert the Urlbar if we're going to navigate. We want the URL
// populated so we can navigate to it. // populated so we can navigate to it.
if (!url || !result.payload.shouldNavigate) { if (!url || !result.payload.shouldNavigate) {
@@ -1062,8 +1067,8 @@ export class UrlbarInput {
result.providerName result.providerName
); );
// Keep startEventInfo since the startEventInfo state might be changed // Keep startEventInfo for telemetry since the startEventInfo state might
// if the URL Bar loses focus on pickResult. // be changed if the URL Bar loses focus on pickResult.
const startEventInfo = this.controller.engagementEvent._startEventInfo; const startEventInfo = this.controller.engagementEvent._startEventInfo;
provider?.tryMethod("pickResult", result, element); provider?.tryMethod("pickResult", result, element);
@@ -1072,6 +1077,7 @@ export class UrlbarInput {
this.controller.engagementEvent.record(event, { this.controller.engagementEvent.record(event, {
selIndex, selIndex,
searchString: this._lastSearchString, searchString: this._lastSearchString,
searchMode,
selType: this.controller.engagementEvent.typeFromElement(element), selType: this.controller.engagementEvent.typeFromElement(element),
provider: result.providerName, provider: result.providerName,
element, element,

View File

@@ -217,24 +217,28 @@ export var UrlbarUtils = {
restrict: lazy.UrlbarTokenizer.RESTRICT.BOOKMARK, restrict: lazy.UrlbarTokenizer.RESTRICT.BOOKMARK,
icon: "chrome://browser/skin/bookmark.svg", icon: "chrome://browser/skin/bookmark.svg",
pref: "shortcuts.bookmarks", pref: "shortcuts.bookmarks",
telemetryLabel: "bookmarks",
}, },
{ {
source: UrlbarUtils.RESULT_SOURCE.TABS, source: UrlbarUtils.RESULT_SOURCE.TABS,
restrict: lazy.UrlbarTokenizer.RESTRICT.OPENPAGE, restrict: lazy.UrlbarTokenizer.RESTRICT.OPENPAGE,
icon: "chrome://browser/skin/tab.svg", icon: "chrome://browser/skin/tab.svg",
pref: "shortcuts.tabs", pref: "shortcuts.tabs",
telemetryLabel: "tabs",
}, },
{ {
source: UrlbarUtils.RESULT_SOURCE.HISTORY, source: UrlbarUtils.RESULT_SOURCE.HISTORY,
restrict: lazy.UrlbarTokenizer.RESTRICT.HISTORY, restrict: lazy.UrlbarTokenizer.RESTRICT.HISTORY,
icon: "chrome://browser/skin/history.svg", icon: "chrome://browser/skin/history.svg",
pref: "shortcuts.history", pref: "shortcuts.history",
telemetryLabel: "history",
}, },
{ {
source: UrlbarUtils.RESULT_SOURCE.ACTIONS, source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
restrict: lazy.UrlbarTokenizer.RESTRICT.ACTION, restrict: lazy.UrlbarTokenizer.RESTRICT.ACTION,
icon: "chrome://browser/skin/quickactions.svg", icon: "chrome://browser/skin/quickactions.svg",
pref: "shortcuts.quickactions", pref: "shortcuts.quickactions",
telemetryLabel: "actions",
}, },
]; ];
}, },

View File

@@ -23,6 +23,15 @@ urlbar:
description: > description: >
How the user started the search. e.g. `typed`, `pasted` etc. How the user started the search. e.g. `typed`, `pasted` etc.
type: string type: string
search_mode:
description: >
If the urlbar is in search mode, thus restricting results to a
specific search engine or local source, this is set to the search mode
source. The possible sources are: `actions`, `bookmarks`, `history`,
`search_engine`, and `tabs`. If search mode is active but the source
did not fall into any of these categories, this will be `unknown`. If
search mode is not active, this will be an empty string.
type: string
n_chars: n_chars:
description: Number of typed characters. description: Number of typed characters.
type: quantity type: quantity
@@ -47,9 +56,10 @@ urlbar:
type: string type: string
bugs: bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800414 - https://bugzilla.mozilla.org/show_bug.cgi?id=1800414
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717
data_reviews: data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800414
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800414#c2 - https://bugzilla.mozilla.org/show_bug.cgi?id=1800414#c2
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717#c4
data_sensitivity: data_sensitivity:
- interaction - interaction
notification_emails: notification_emails:
@@ -67,6 +77,15 @@ urlbar:
description: > description: >
How the user started the search. e.g. `typed`, `pasted` etc. How the user started the search. e.g. `typed`, `pasted` etc.
type: string type: string
search_mode:
description: >
If the urlbar is in search mode, thus restricting results to a
specific search engine or local source, this is set to the search mode
source. The possible sources are: `actions`, `bookmarks`, `history`,
`search_engine`, and `tabs`. If search mode is active but the source
did not fall into any of these categories, this will be `unknown`. If
search mode is not active, this will be an empty string.
type: string
n_chars: n_chars:
description: Number of typed characters. description: Number of typed characters.
type: quantity type: quantity
@@ -107,9 +126,10 @@ urlbar:
type: string type: string
bugs: bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797265 - https://bugzilla.mozilla.org/show_bug.cgi?id=1797265
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717
data_reviews: data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797265
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797265#c3 - https://bugzilla.mozilla.org/show_bug.cgi?id=1797265#c3
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717#c4
data_sensitivity: data_sensitivity:
- interaction - interaction
notification_emails: notification_emails:
@@ -130,6 +150,15 @@ urlbar:
description: > description: >
How the user started the search. e.g. `typed`, `pasted` etc. How the user started the search. e.g. `typed`, `pasted` etc.
type: string type: string
search_mode:
description: >
If the urlbar is in search mode, thus restricting results to a
specific search engine or local source, this is set to the search mode
source. The possible sources are: `actions`, `bookmarks`, `history`,
`search_engine`, and `tabs`. If search mode is active but the source
did not fall into any of these categories, this will be `unknown`. If
search mode is not active, this will be an empty string.
type: string
n_chars: n_chars:
description: Number of typed characters. description: Number of typed characters.
type: quantity type: quantity
@@ -154,9 +183,10 @@ urlbar:
type: string type: string
bugs: bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800579 - https://bugzilla.mozilla.org/show_bug.cgi?id=1800579
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717
data_reviews: data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800579
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800579#c4 - https://bugzilla.mozilla.org/show_bug.cgi?id=1800579#c4
- https://bugzilla.mozilla.org/show_bug.cgi?id=1805717#c4
data_sensitivity: data_sensitivity:
- interaction - interaction
notification_emails: notification_emails:

View File

@@ -564,7 +564,7 @@ export var UrlbarTestUtils = {
// names that are not usually included in actual search mode objects. For // names that are not usually included in actual search mode objects. For
// convenience, ignore those properties if they aren't also present in the // convenience, ignore those properties if they aren't also present in the
// urlbar's actual search mode object. // urlbar's actual search mode object.
let ignoreProperties = ["icon", "pref", "restrict"]; let ignoreProperties = ["icon", "pref", "restrict", "telemetryLabel"];
for (let prop of ignoreProperties) { for (let prop of ignoreProperties) {
if (prop in expectedSearchMode && !(prop in window.gURLBar.searchMode)) { if (prop in expectedSearchMode && !(prop in window.gURLBar.searchMode)) {
this._testScope?.info( this._testScope?.info(

View File

@@ -109,6 +109,8 @@ support-files = head-glean.js
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_abandonment_sap.js] [browser_glean_telemetry_abandonment_sap.js]
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_abandonment_search_mode.js]
support-files = head-glean.js
[browser_glean_telemetry_engagement_groups.js] [browser_glean_telemetry_engagement_groups.js]
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_engagement_interaction.js] [browser_glean_telemetry_engagement_interaction.js]
@@ -119,6 +121,8 @@ support-files = head-glean.js
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_engagement_sap.js] [browser_glean_telemetry_engagement_sap.js]
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_engagement_search_mode.js]
support-files = head-glean.js
[browser_glean_telemetry_engagement_selected_result.js] [browser_glean_telemetry_engagement_selected_result.js]
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_engagement_type.js] [browser_glean_telemetry_engagement_type.js]
@@ -133,6 +137,8 @@ support-files = head-glean.js
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_impression_sap.js] [browser_glean_telemetry_impression_sap.js]
support-files = head-glean.js support-files = head-glean.js
[browser_glean_telemetry_impression_search_mode.js]
support-files = head-glean.js
[browser_glean_telemetry_impression_timing.js] [browser_glean_telemetry_impression_timing.js]
support-files = head-glean.js support-files = head-glean.js
[browser_groupLabels.js] [browser_groupLabels.js]

View File

@@ -0,0 +1,106 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for the following data of abandonment telemetry.
// - search_mode
/* import-globals-from head-glean.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-glean.js",
this
);
add_setup(async function() {
await setup();
});
add_task(async function search_mode_search_not_mode() {
await doTest(async browser => {
await openPopup("x");
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "" }]);
});
});
add_task(async function search_mode_search_engine() {
await doTest(async browser => {
await openPopup("x");
await UrlbarTestUtils.enterSearchMode(window);
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "search_engine" }]);
});
});
add_task(async function search_mode_bookmarks() {
await doTest(async browser => {
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "https://example.com/bookmark",
title: "bookmark",
});
await openPopup("bookmark");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
});
await selectRowByURL("https://example.com/bookmark");
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "bookmarks" }]);
});
});
add_task(async function search_mode_history() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.autoFill", false]],
});
await doTest(async browser => {
await PlacesTestUtils.addVisits("https://example.com/test");
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
});
await selectRowByURL("https://example.com/test");
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "history" }]);
});
await SpecialPowers.popPrefEnv();
});
add_task(async function search_mode_tabs() {
const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/");
await doTest(async browser => {
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.TABS,
});
await selectRowByProvider("Places");
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "tabs" }]);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function search_mode_actions() {
await doTest(async browser => {
await openPopup("add");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
});
await selectRowByProvider("quickactions");
await doBlur();
assertAbandonmentTelemetry([{ search_mode: "actions" }]);
});
});

View File

@@ -0,0 +1,109 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for the following data of engagement telemetry.
// - search_mode
/* import-globals-from head-glean.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-glean.js",
this
);
add_setup(async function() {
await setup();
});
add_task(async function search_mode_search_not_mode() {
await doTest(async browser => {
await openPopup("x");
await doEnter();
assertEngagementTelemetry([{ search_mode: "" }]);
});
});
add_task(async function search_mode_search_engine() {
await doTest(async browser => {
await openPopup("x");
await UrlbarTestUtils.enterSearchMode(window);
await doEnter();
assertEngagementTelemetry([{ search_mode: "search_engine" }]);
});
});
add_task(async function search_mode_bookmarks() {
await doTest(async browser => {
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "https://example.com/bookmark",
title: "bookmark",
});
await openPopup("bookmark");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
});
await selectRowByURL("https://example.com/bookmark");
await doEnter();
assertEngagementTelemetry([{ search_mode: "bookmarks" }]);
});
});
add_task(async function search_mode_history() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.autoFill", false]],
});
await doTest(async browser => {
await PlacesTestUtils.addVisits("https://example.com/test");
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
});
await selectRowByURL("https://example.com/test");
await doEnter();
assertEngagementTelemetry([{ search_mode: "history" }]);
});
await SpecialPowers.popPrefEnv();
});
add_task(async function search_mode_tabs() {
const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/");
await doTest(async browser => {
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.TABS,
});
await selectRowByProvider("Places");
EventUtils.synthesizeKey("KEY_Enter");
await BrowserTestUtils.waitForCondition(() => gBrowser.selectedTab === tab);
assertEngagementTelemetry([{ search_mode: "tabs" }]);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function search_mode_actions() {
await doTest(async browser => {
await openPopup("add");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
});
await selectRowByProvider("quickactions");
const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
doClickSubButton(".urlbarView-quickaction-row[data-key=addons]");
await onLoad;
assertEngagementTelemetry([{ search_mode: "actions" }]);
});
});

View File

@@ -0,0 +1,106 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for the following data of impression telemetry.
// - search_mode
/* import-globals-from head-glean.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-glean.js",
this
);
add_setup(async function() {
await setup();
});
add_task(async function search_mode_search_not_mode() {
await doTest(async browser => {
await openPopup("x");
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "" }]);
});
});
add_task(async function search_mode_search_engine() {
await doTest(async browser => {
await openPopup("x");
await UrlbarTestUtils.enterSearchMode(window);
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "search_engine" }]);
});
});
add_task(async function search_mode_bookmarks() {
await doTest(async browser => {
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "https://example.com/bookmark",
title: "bookmark",
});
await openPopup("bookmark");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
});
await selectRowByURL("https://example.com/bookmark");
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "bookmarks" }]);
});
});
add_task(async function search_mode_history() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.autoFill", false]],
});
await doTest(async browser => {
await PlacesTestUtils.addVisits("https://example.com/test");
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
});
await selectRowByURL("https://example.com/test");
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "history" }]);
});
await SpecialPowers.popPrefEnv();
});
add_task(async function search_mode_tabs() {
const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/");
await doTest(async browser => {
await openPopup("example");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.TABS,
});
await selectRowByProvider("Places");
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "tabs" }]);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function search_mode_actions() {
await doTest(async browser => {
await openPopup("add");
await UrlbarTestUtils.enterSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
});
await selectRowByProvider("quickactions");
await waitForPauseImpression();
assertImpressionTelemetry([{ search_mode: "actions" }]);
});
});