Bug 1915252 - Add telemetry for use of shift-click/enter in the search bar & address bar. r=urlbar-reviewers,scunnane,daisuke
Differential Revision: https://phabricator.services.mozilla.com/D245704
This commit is contained in:
@@ -55,7 +55,7 @@ class BrowserSearchTelemetryHandler {
|
||||
* Determines if we should record a search for this browser instance.
|
||||
* Private Browsing mode is normally skipped.
|
||||
*
|
||||
* @param {browser} browser
|
||||
* @param {XULBrowserElement} browser
|
||||
* The browser where the search was loaded.
|
||||
* @returns {boolean}
|
||||
* True if the search should be recorded, false otherwise.
|
||||
@@ -127,7 +127,7 @@ class BrowserSearchTelemetryHandler {
|
||||
* Telemetry records only search counts per engine and action origin, but
|
||||
* nothing pertaining to the search contents themselves.
|
||||
*
|
||||
* @param {browser} browser
|
||||
* @param {XULBrowserElement} browser
|
||||
* The browser where the search originated.
|
||||
* @param {nsISearchEngine} engine
|
||||
* The engine handling the search.
|
||||
@@ -232,11 +232,27 @@ class BrowserSearchTelemetryHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records visits to a search engine's search form.
|
||||
*
|
||||
* @param {nsISearchEngine} engine
|
||||
* The engine whose search form is being visited.
|
||||
* @param {string} source
|
||||
* Where the search form was opened from.
|
||||
* This can be "urlbar" or "searchbar".
|
||||
*/
|
||||
recordSearchForm(engine, source) {
|
||||
Glean.sap.searchFormCounts.record({
|
||||
source,
|
||||
provider_id: engine.isAppProvided ? engine.id : "other",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the "urlbar", "urlbar-oneoff", "searchbar" and
|
||||
* "searchbar-oneoff" sources.
|
||||
*
|
||||
* @param {browser} browser
|
||||
* @param {XULBrowserElement} browser
|
||||
* The browser where the search originated.
|
||||
* @param {nsISearchEngine} engine
|
||||
* The engine handling the search.
|
||||
|
||||
@@ -519,6 +519,7 @@
|
||||
this._needBrowserFocusAtEnterKeyUp = true;
|
||||
}
|
||||
|
||||
lazy.BrowserSearchTelemetry.recordSearchForm(engine, "searchbar");
|
||||
openTrustedLinkIn(searchForm, where, params);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,34 @@ sap:
|
||||
expires: never
|
||||
telemetry_mirror: h#SEARCH_COUNTS
|
||||
|
||||
search_form_counts:
|
||||
type: event
|
||||
description: >
|
||||
Records how often users visit the homepages of search engines (also
|
||||
known as SearchForm) using a SAP. It only counts how many visits were
|
||||
initiated, not how many homepages were loaded successfully.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1915252
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1915252
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- fx-search-telemetry@mozilla.com
|
||||
expires: never
|
||||
extra_keys:
|
||||
source:
|
||||
description: >
|
||||
The search access point from which the user initiated the search.
|
||||
Possible values are `searchbar` and `urlbar`.
|
||||
type: string
|
||||
provider_id:
|
||||
description: >
|
||||
The identifier of the search engine provider that is being used for the
|
||||
search. Note this may be different to the identifier of the provider in
|
||||
SERP reports. For user installed engines, this will be the string `other`.
|
||||
type: string
|
||||
|
||||
serp:
|
||||
impression:
|
||||
type: event
|
||||
|
||||
@@ -184,6 +184,8 @@ support-files = [
|
||||
"telemetrySearchSuggestions.xml",
|
||||
]
|
||||
|
||||
["browser_search_telemetry_searchform.js"]
|
||||
|
||||
["browser_search_telemetry_shopping.js"]
|
||||
support-files = ["searchTelemetryAd_shopping.html"]
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const CONFIG = [
|
||||
{
|
||||
identifier: "defaultEngine",
|
||||
},
|
||||
{
|
||||
identifier: "second_engine",
|
||||
base: {
|
||||
name: "Second Engine",
|
||||
urls: {
|
||||
search_form: {
|
||||
base: "https://www.example.com/searchform",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const TEST_ENGINE_BASENAME = "testEngine.xml";
|
||||
const TEST_ENGINE_NAME = "Foo";
|
||||
|
||||
function findOneOff(engineName) {
|
||||
let oneOffButtons = document.getElementById("PopupSearchAutoComplete")
|
||||
.oneOffButtons.buttons;
|
||||
let oneOffChildren = [...oneOffButtons.children];
|
||||
let oneOffButton = oneOffChildren.find(
|
||||
node => node.engine?.name == engineName
|
||||
);
|
||||
Assert.notEqual(
|
||||
oneOffButton,
|
||||
undefined,
|
||||
`One-off for ${engineName} should exist`
|
||||
);
|
||||
return oneOffButton;
|
||||
}
|
||||
|
||||
async function openSearchbarPopup(searchBarValue) {
|
||||
let searchBar = document.getElementById("searchbar");
|
||||
|
||||
searchBar.focus();
|
||||
searchBar.value = searchBarValue;
|
||||
if (searchBar.textbox.popupOpen) {
|
||||
info("searchPanel is already open");
|
||||
return;
|
||||
}
|
||||
let searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
let shownPromise = BrowserTestUtils.waitForEvent(searchPopup, "popupshown");
|
||||
|
||||
let searchIcon = searchBar.querySelector(".searchbar-search-button");
|
||||
let oneOffInstance = searchPopup.oneOffButtons;
|
||||
let builtPromise = BrowserTestUtils.waitForEvent(oneOffInstance, "rebuild");
|
||||
|
||||
info("Opening search panel");
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
await Promise.all([shownPromise, builtPromise]);
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
await SearchTestUtils.updateRemoteSettingsConfig(CONFIG);
|
||||
await SearchTestUtils.installOpenSearchEngine({
|
||||
url: getRootDirectory(gTestPath) + "../" + TEST_ENGINE_BASENAME,
|
||||
});
|
||||
await gCUITestUtils.addSearchBar();
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
gCUITestUtils.removeSearchBar();
|
||||
resetTelemetry();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_appProvidedSearchbar() {
|
||||
await openSearchbarPopup("");
|
||||
let oneOff = findOneOff("Second Engine");
|
||||
EventUtils.synthesizeMouseAtCenter(oneOff, { shiftKey: true });
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
Assert.equal(events.length, 1, "Event was recorded.");
|
||||
Assert.equal(events[0].extra.source, "searchbar", "Source is correct");
|
||||
Assert.equal(events[0].extra.provider_id, "second_engine", "Id is correct");
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_extensionSearchbar() {
|
||||
await openSearchbarPopup("");
|
||||
let oneOff = findOneOff(TEST_ENGINE_NAME);
|
||||
EventUtils.synthesizeMouseAtCenter(oneOff, { shiftKey: true });
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
Assert.equal(events.length, 1, "Event was recorded");
|
||||
Assert.equal(events[0].extra.source, "searchbar", "Source is correct");
|
||||
Assert.equal(events[0].extra.provider_id, "other", "Id is correct");
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_actualSearchSearchbar() {
|
||||
// Enter something in the searchbar to start an actual search.
|
||||
await openSearchbarPopup("foo");
|
||||
let oneOff = findOneOff("Second Engine");
|
||||
EventUtils.synthesizeMouseAtCenter(oneOff, { shiftKey: true });
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
// Since we used the one off button to search (not to open the search form),
|
||||
// no event should be recorded in `sap.searchFormCounts`.
|
||||
Assert.equal(events, null, "No search form event is recorded for searches");
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_appProvidedUrlbar() {
|
||||
let popup = await UrlbarTestUtils.openSearchModeSwitcher(window);
|
||||
info("Choose Second Engine in the unified search button popup.");
|
||||
let item = popup.querySelector('menuitem[label="Second Engine"]');
|
||||
let popupHidden = UrlbarTestUtils.searchModeSwitcherPopupClosed(window);
|
||||
EventUtils.synthesizeMouseAtCenter(item, { shiftKey: true });
|
||||
await popupHidden;
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
Assert.equal(events.length, 1, "Event was recorded");
|
||||
Assert.equal(events[0].extra.source, "urlbar", "Source is correct");
|
||||
Assert.equal(events[0].extra.provider_id, "second_engine", "Id is correct");
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_extensionUrlbar() {
|
||||
let popup = await UrlbarTestUtils.openSearchModeSwitcher(window);
|
||||
info("Choose extension engine in the unified search button popup.");
|
||||
let item = popup.querySelector(`menuitem[label="${TEST_ENGINE_NAME}"]`);
|
||||
let popupHidden = UrlbarTestUtils.searchModeSwitcherPopupClosed(window);
|
||||
EventUtils.synthesizeMouseAtCenter(item, { shiftKey: true });
|
||||
await popupHidden;
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
Assert.equal(events.length, 1, "Event was recorded");
|
||||
Assert.equal(events[0].extra.source, "urlbar", "Source is correct");
|
||||
Assert.equal(events[0].extra.provider_id, "other");
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_actualSearchUrlbar() {
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
value: "foo",
|
||||
});
|
||||
let popup = await UrlbarTestUtils.openSearchModeSwitcher(window);
|
||||
info("Shift-click Second Engine in the unified search button popup.");
|
||||
let item = popup.querySelector('menuitem[label="Second Engine"]');
|
||||
let popupHidden = UrlbarTestUtils.searchModeSwitcherPopupClosed(window);
|
||||
EventUtils.synthesizeMouseAtCenter(item, { shiftKey: true });
|
||||
await popupHidden;
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
let events = Glean.sap.searchFormCounts.testGetValue();
|
||||
// Since we used the unified search button to search (not to open the search
|
||||
// form), no event should be recorded in `sap.searchFormCounts`.
|
||||
Assert.equal(events, null, "No search form event was recorded");
|
||||
resetTelemetry();
|
||||
});
|
||||
@@ -1867,8 +1867,10 @@ export class UrlbarInput {
|
||||
null,
|
||||
"search-mode-switcher"
|
||||
).uri.spec;
|
||||
// TODO: record SAP telemetry, see Bug 1961789.
|
||||
} else {
|
||||
url = searchEngine.searchForm;
|
||||
lazy.BrowserSearchTelemetry.recordSearchForm(searchEngine, "urlbar");
|
||||
}
|
||||
|
||||
this._lastSearchString = "";
|
||||
|
||||
8
tools/@types/lib.gecko.glean.d.ts
vendored
8
tools/@types/lib.gecko.glean.d.ts
vendored
@@ -908,6 +908,7 @@ interface GleanImpl {
|
||||
sap: {
|
||||
counts: GleanEvent;
|
||||
deprecatedCounts: Record<string, GleanCounter>;
|
||||
searchFormCounts: GleanEvent;
|
||||
}
|
||||
|
||||
serp: {
|
||||
@@ -1176,6 +1177,7 @@ interface GleanImpl {
|
||||
tabgroup: {
|
||||
createGroup: GleanEvent;
|
||||
reopen: GleanEvent;
|
||||
addTab: GleanEvent;
|
||||
activeGroups: Record<string, GleanQuantity>;
|
||||
tabsPerActiveGroup: Record<string, GleanQuantity>;
|
||||
tabCountInGroups: Record<string, GleanQuantity>;
|
||||
@@ -5237,6 +5239,11 @@ interface GleanImpl {
|
||||
intervalHours: GleanTimingDistribution;
|
||||
}
|
||||
|
||||
hangs: {
|
||||
reports: GleanObject;
|
||||
modules: GleanObject;
|
||||
}
|
||||
|
||||
backgroundTasksRmdirBase: {
|
||||
metricBase: GleanEvent;
|
||||
elapsedMs: GleanQuantity;
|
||||
@@ -6968,6 +6975,7 @@ interface GleanPingsImpl {
|
||||
useCounters: nsIGleanPingWithReason<"app_shutdown_confirmed"|"idle_startup">;
|
||||
fxAccounts: nsIGleanPingWithReason<"active"|"dirty_startup"|"inactive">;
|
||||
bounceTrackingProtection: nsIGleanPingNoReason;
|
||||
hangReport: nsIGleanPingNoReason;
|
||||
backgroundTasks: nsIGleanPingNoReason;
|
||||
captchaDetection: nsIGleanPingNoReason;
|
||||
crash: nsIGleanPingWithReason<"crash"|"event_found">;
|
||||
|
||||
Reference in New Issue
Block a user