Backed out 2 changesets (bug 1853910) for causing mochitests failures in browser_ext_urlbar_attributionURL.js. CLOSED TREE

Backed out changeset eea288462cbb (bug 1853910)
Backed out changeset 75543d7d4250 (bug 1853910)
This commit is contained in:
Stanca Serban 2023-10-07 00:17:27 +03:00
parent 6bca2cc752
commit 270068fd8d
19 changed files with 2328 additions and 17 deletions

View File

@ -584,6 +584,8 @@ pref("browser.urlbar.shortcuts.bookmarks", true);
pref("browser.urlbar.shortcuts.tabs", true);
pref("browser.urlbar.shortcuts.history", true);
pref("browser.urlbar.eventTelemetry.enabled", false);
// When we send events to Urlbar extensions, we wait this amount of time in
// milliseconds for them to respond before timing out.
pref("browser.urlbar.extension.timeout", 400);

View File

@ -5,6 +5,7 @@
"use strict";
ChromeUtils.defineESModuleGetters(this, {
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
UrlbarProviderExtension:
"resource:///modules/UrlbarProviderExtension.sys.mjs",
});
@ -144,8 +145,7 @@ this.urlbar = class extends ExtensionAPI {
engagementTelemetry: getSettingsAPI({
context,
name: "engagementTelemetry",
readOnly: true,
callback: () => false,
callback: () => UrlbarPrefs.get("eventTelemetry.enabled"),
}),
},
};

View File

@ -489,6 +489,9 @@ add_task(async function closeView() {
add_task(async function onEngagement() {
gURLBar.blur();
// Enable engagement telemetry.
Services.prefs.setBoolPref("browser.urlbar.eventTelemetry.enabled", true);
let ext = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["urlbar"],
@ -599,4 +602,5 @@ add_task(async function onEngagement() {
Assert.equal(state, "engagement");
await ext.unload();
Services.prefs.clearUserPref("browser.urlbar.eventTelemetry.enabled");
});

View File

@ -5,6 +5,7 @@ const { AddonTestUtils } = ChromeUtils.importESModule(
);
ChromeUtils.defineESModuleGetters(this, {
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.sys.mjs",
@ -31,6 +32,17 @@ AddonTestUtils.createAppInfo(
SearchTestUtils.init(this);
SearchTestUtils.initXPCShellAddonManager(this, "system");
function promiseUninstallCompleted(extensionId) {
return new Promise(resolve => {
// eslint-disable-next-line mozilla/balanced-listeners
ExtensionParent.apiManager.on("uninstall-complete", (type, { id }) => {
if (id === extensionId) {
executeSoon(resolve);
}
});
});
}
function getPayload(result) {
let payload = {};
for (let [key, value] of Object.entries(result.payload)) {
@ -1450,3 +1462,45 @@ add_task(async function test_nonPrivateBrowsing() {
await ext.unload();
});
// Tests the engagementTelemetry property.
add_task(async function test_engagementTelemetry() {
let getPrefValue = () => UrlbarPrefs.get("eventTelemetry.enabled");
Assert.equal(
getPrefValue(),
false,
"Engagement telemetry should be disabled by default"
);
let ext = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["urlbar"],
},
isPrivileged: true,
incognitoOverride: "spanning",
useAddonManager: "temporary",
async background() {
await browser.urlbar.engagementTelemetry.set({ value: true });
browser.test.sendMessage("ready");
},
});
await ext.startup();
await ext.awaitMessage("ready");
Assert.equal(
getPrefValue(),
true,
"Successfully enabled the engagement telemetry"
);
let completed = promiseUninstallCompleted(ext.id);
await ext.unload();
await completed;
Assert.equal(
getPrefValue(),
false,
"Engagement telemetry should be reset after unloading the add-on"
);
});

View File

@ -30,6 +30,7 @@ add_setup(async () => {
["browser.urlbar.suggest.searches", true],
["browser.urlbar.trending.featureGate", true],
["browser.urlbar.trending.requireSearchMode", false],
["browser.urlbar.eventTelemetry.enabled", true],
// Bug 1775917: Disable the persisted-search-terms search tip because if
// not dismissed, it can cause issues with other search tests.
["browser.urlbar.tipShownCount.searchTip_persist", 999],
@ -84,6 +85,24 @@ async function check_results({ featureEnabled = false }) {
EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
EventUtils.synthesizeKey("VK_RETURN", {}, window);
let event = {
category: "urlbar",
method: "engagement",
object: "enter",
value: "typed",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
selIndex: "0",
selType: featureEnabled ? "trending_rich" : "trending",
provider: "SearchSuggestions",
},
};
TelemetryTestUtils.assertEvents([event], {
category: "urlbar",
});
let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
TelemetryTestUtils.assertScalar(scalars, "urlbar.engagement", 1);

View File

@ -995,6 +995,48 @@ class TelemetryEvent {
return;
}
if (action == "go_button") {
// Fall back since the conventional telemetry dones't support "go_button" action.
action = "click";
}
let endTime = (event && event.timeStamp) || Cu.now();
let startTime = startEventInfo.timeStamp || endTime;
// Synthesized events in tests may have a bogus timeStamp, causing a
// subtraction between monotonic and non-monotonic timestamps; that's why
// abs is necessary here. It should only happen in tests, anyway.
let elapsed = Math.abs(Math.round(endTime - startTime));
// Rather than listening to the pref, just update status when we record an
// event, if the pref changed from the last time.
let recordingEnabled = lazy.UrlbarPrefs.get("eventTelemetry.enabled");
if (this._eventRecordingEnabled != recordingEnabled) {
this._eventRecordingEnabled = recordingEnabled;
Services.telemetry.setEventRecordingEnabled("urlbar", recordingEnabled);
}
let extra = {
elapsed: elapsed.toString(),
numChars,
numWords,
};
if (method == "engagement") {
extra.selIndex = details.selIndex.toString();
extra.selType = details.selType;
extra.provider = details.provider || "";
}
// We invoke recordEvent regardless, if recording is disabled this won't
// report the events remotely, but will count it in the event_counts scalar.
Services.telemetry.recordEvent(
this._category,
method,
action,
startEventInfo.interactionType,
extra
);
Services.telemetry.scalarAdd(
method == "engagement"
? TELEMETRY_SCALAR_ENGAGEMENT

View File

@ -105,6 +105,9 @@ const PREF_URLBAR_DEFAULTS = new Map([
// 0 - never resolve; 1 - use heuristics (default); 2 - always resolve
["dnsResolveSingleWordsAfterSearch", 0],
// Whether telemetry events should be recorded.
["eventTelemetry.enabled", false],
// Whether we expand the font size when when the urlbar is
// focused.
["experimental.expandTextOnFocus", false],

View File

@ -131,6 +131,10 @@ browser.urlbar.dnsResolveSingleWordsAfterSearch (number, default: 0)
"Did you mean to go to 'host'" prompt.
Set to 0. 0: Never resolve, 1: Use heuristics, 2. Always resolve.
browser.urlbar.eventTelemetry.enabled (boolean, default: false)
Whether telemetry events should be recorded. This is expensive and should only
be enabled by experiments with a small population.
browser.urlbar.extension.timeout (integer, default: 400)
When sending events to extensions, they have this amount of time in
milliseconds to respond before timing out. This affects the omnibox API.

View File

@ -650,3 +650,120 @@ Firefox Suggest
:doc:`firefox-suggest-telemetry` document.
.. _search telemetry: /browser/search/telemetry.html
Event Telemetry
---------------
.. note::
This is a legacy event telemetry. For the current telemetry, please see
`Search Engagement Telemetry`_. These legacy events were disabled by default
and required enabling through a preference or a Urlbar WebExtension
experimental API.
.. _Search Engagement Telemetry: #search-engagement-telemetry
The event telemetry is grouped under the ``urlbar`` category.
Event Method
~~~~~~~~~~~~
There are two methods to describe the interaction with the urlbar:
- ``engagement``
It is defined as a completed action in urlbar, where a user inserts text
and executes one of the actions described in the Event Object.
- ``abandonment``
It is defined as an action where the user inserts text but does not
complete an engagement action, usually unfocusing the urlbar. This also
happens when the user switches to another window, regardless of urlbar
focus.
Event Value
~~~~~~~~~~~
This is how the user interaction started
- ``typed``: The text was typed into the urlbar.
- ``dropped``: The text was drag and dropped into the urlbar.
- ``pasted``: The text was pasted into the urlbar.
- ``topsites``: The user opened the urlbar view without typing, dropping,
or pasting.
In these cases, if the urlbar input is showing the URL of the loaded page
and the user has not modified the inputs content, the urlbar views shows
the users top sites. Otherwise, if the user had modified the inputs
content, the urlbar view shows results based on what the user has typed.
To tell whether top sites were shown, it's enough to check whether value is
``topsites``. To know whether the user actually picked a top site, check
check that ``numChars`` == 0. If ``numChars`` > 0, the user initially opened
top sites, but then they started typing and confirmed a different result.
- ``returned``: The user abandoned a search, for example by switching to
another tab/window, or focusing something else, then came back to it
and continued. We consider a search continued if the user kept at least the
first char of the original search string.
- ``restarted``: The user abandoned a search, for example by switching to
another tab/window, or focusing something else, then came back to it,
cleared it and then typed a new string.
Event Object
~~~~~~~~~~~~
These describe actions in the urlbar:
- ``click``
The user clicked on a result.
- ``enter``
The user confirmed a result with Enter.
- ``drop_go``
The user dropped text on the input field.
- ``paste_go``
The user used Paste and Go feature. It is not the same as paste and Enter.
- ``blur``
The user unfocused the urlbar. This is only valid for ``abandonment``.
Event Extra
~~~~~~~~~~~
This object contains additional information about the interaction.
Extra is a key-value store, where all the keys and values are strings.
- ``elapsed``
Time in milliseconds from the initial interaction to an action.
- ``numChars``
Number of input characters the user typed or pasted at the time of
submission.
- ``numWords``
Number of words in the input. The measurement is taken from a trimmed input
split up by its spaces. This is not a perfect measurement, since it will
return an incorrect value for languages that do not use spaces or URLs
containing spaces in its query parameters, for example.
- ``selType``
The type of the selected result at the time of submission.
This is only present for ``engagement`` events.
It can be one of: ``none``, ``autofill``, ``visiturl``, ``bookmark``,
``bookmark_adaptive`` ``history``, ``history_adaptive``, ``keyword``,
``searchengine``, ``searchsuggestion``, ``switchtab``, ``remotetab``,
``extension``, ``oneoff``, ``keywordoffer``, ``canonized``, ``tip``,
``tiphelp``, ``formhistory``, ``tabtosearch``, ``help``, ``block``,
``quicksuggest``, ``unknown``
In practice, ``tabtosearch`` should not appear in real event telemetry.
Opening a tab-to-search result enters search mode and entering search mode
does not currently mark the end of an engagement. It is noted here for
completeness. Similarly, ``block`` indicates a result was blocked or deleted
but should not appear because blocking a result does not end the engagement.
- ``selIndex``
Index of the selected result in the urlbar panel, or -1 for no selection.
There won't be a selection when a one-off button is the only selection, and
for the ``paste_go`` or ``drop_go`` objects. There may also not be a
selection if the system was busy and results arrived too late, then we
directly decide whether to search or visit the given string without having
a fully built result.
This is only present for ``engagement`` events.
- ``provider``
The name of the result provider for the selected result. Existing values
are: ``HeuristicFallback``, ``Autofill``, ``Places``,
``TokenAliasEngines``, ``SearchSuggestions``, ``UrlbarProviderTopSites``.
Data from before Firefox 91 will also list ``UnifiedComplete`` as a
provider. This is equivalent to ``Places``.
Values can also be defined by `URLBar provider experiments`_.
.. _URLBar provider experiments: experiments.html#developing-address-bar-extensions

View File

@ -14,6 +14,10 @@ add_setup(async function () {
window.windowUtils.disableNonTestMouseEvents(false);
});
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
});
add_task(async function enter_mainButton_url() {
@ -173,6 +177,21 @@ async function doTest({ click, buttonUrl = undefined, helpUrl = undefined }) {
helpUrl ? "test-help" : "test-picked",
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object:
click && !(helpUrl && UrlbarPrefs.get("resultMenu"))
? "click"
: "enter",
value: "typed",
},
],
{ category: "urlbar" }
);
// Done.
UrlbarProvidersManager.unregisterProvider(provider);
if (tab) {

View File

@ -101,6 +101,11 @@ add_setup(async function () {
// be not to be shown again in any session. Telemetry should be updated.
add_task(async function pickButton_onboard() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: "about:newtab",
@ -124,6 +129,17 @@ add_task(async function pickButton_onboard() {
`${UrlbarProviderSearchTips.TIP_TYPE.ONBOARD}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -136,12 +152,18 @@ add_task(async function pickButton_onboard() {
resetSearchTipsProvider();
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
// Picking the tip's button should cause the Urlbar to blank out and the tip to
// be not to be shown again in any session. Telemetry should be updated.
add_task(async function pickButton_redirect() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Google");
await BrowserTestUtils.withNewTab("about:blank", async () => {
await withDNSRedirect("www.google.com", "/", async url => {
@ -167,6 +189,17 @@ add_task(async function pickButton_redirect() {
`${UrlbarProviderSearchTips.TIP_TYPE.REDIRECT}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -177,6 +210,7 @@ add_task(async function pickButton_redirect() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
await SpecialPowers.popPrefEnv();
});
// Picking the tip's button should cause the Urlbar to keep its current
@ -185,8 +219,12 @@ add_task(async function pickButton_redirect() {
add_task(async function pickButton_persist() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.showSearchTerms.featureGate", true]],
set: [
["browser.urlbar.eventTelemetry.enabled", true],
["browser.urlbar.showSearchTerms.featureGate", true],
],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Example");
@ -226,6 +264,17 @@ add_task(async function pickButton_persist() {
`${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -243,6 +292,11 @@ add_task(async function pickButton_persist() {
// effect as picking the tip.
add_task(async function clickInInput_onboard() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Google");
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
@ -265,6 +319,17 @@ add_task(async function clickInInput_onboard() {
`${UrlbarProviderSearchTips.TIP_TYPE.ONBOARD}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -275,13 +340,20 @@ add_task(async function clickInInput_onboard() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
// Pressing Ctrl+L (the open location command) while the onboard tip is showing
// should have the same effect as picking the tip.
add_task(async function openLocation_onboard() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Google");
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
@ -304,6 +376,17 @@ add_task(async function openLocation_onboard() {
`${UrlbarProviderSearchTips.TIP_TYPE.ONBOARD}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "enter",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -314,13 +397,20 @@ add_task(async function openLocation_onboard() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
// Clicking in the input while the redirect tip is showing should have the same
// effect as picking the tip.
add_task(async function clickInInput_redirect() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Google");
await BrowserTestUtils.withNewTab("about:blank", async () => {
await withDNSRedirect("www.google.com", "/", async url => {
@ -344,6 +434,17 @@ add_task(async function clickInInput_redirect() {
`${UrlbarProviderSearchTips.TIP_TYPE.REDIRECT}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -354,6 +455,7 @@ add_task(async function clickInInput_redirect() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
await SpecialPowers.popPrefEnv();
});
// Clicking in the input while the persist tip is showing should have the same
@ -361,8 +463,12 @@ add_task(async function clickInInput_redirect() {
add_task(async function clickInInput_persist() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.showSearchTerms.featureGate", true]],
set: [
["browser.urlbar.eventTelemetry.enabled", true],
["browser.urlbar.showSearchTerms.featureGate", true],
],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Example");
await BrowserTestUtils.withNewTab("about:blank", async () => {
@ -398,6 +504,17 @@ add_task(async function clickInInput_persist() {
`${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "click",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -408,12 +525,18 @@ add_task(async function clickInInput_persist() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
await SpecialPowers.popPrefEnv();
});
// Pressing Ctrl+L (the open location command) while the redirect tip is showing
// should have the same effect as picking the tip.
add_task(async function openLocation_redirect() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Google");
await BrowserTestUtils.withNewTab("about:blank", async () => {
await withDNSRedirect("www.google.com", "/", async url => {
@ -437,6 +560,17 @@ add_task(async function openLocation_redirect() {
`${UrlbarProviderSearchTips.TIP_TYPE.REDIRECT}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "enter",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(
@ -447,6 +581,7 @@ add_task(async function openLocation_redirect() {
);
Assert.equal(gURLBar.value, "", "The Urlbar should be empty.");
resetSearchTipsProvider();
await SpecialPowers.popPrefEnv();
});
// Pressing Ctrl+L (the open location command) while the persist tip is showing
@ -454,8 +589,13 @@ add_task(async function openLocation_redirect() {
add_task(async function openLocation_persist() {
UrlbarProviderSearchTips.disableTipsForCurrentSession = false;
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.showSearchTerms.featureGate", true]],
set: [
["browser.urlbar.eventTelemetry.enabled", true],
["browser.urlbar.showSearchTerms.featureGate", true],
],
});
Services.telemetry.clearEvents();
await setDefaultEngine("Example");
await BrowserTestUtils.withNewTab("about:blank", async () => {
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
@ -490,6 +630,17 @@ add_task(async function openLocation_persist() {
`${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}-picked`,
1
);
TelemetryTestUtils.assertEvents(
[
{
category: "urlbar",
method: "engagement",
object: "enter",
value: "typed",
},
],
{ category: "urlbar" }
);
Assert.equal(
UrlbarPrefs.get(

View File

@ -609,6 +609,26 @@ https_first_disabled = true
["browser_urlbar_annotation.js"]
support-files = ["redirect_to.sjs"]
["browser_urlbar_event_telemetry_abandonment.js"]
support-files = [
"searchSuggestionEngine.xml",
"searchSuggestionEngine.sjs",
]
["browser_urlbar_event_telemetry_engagement.js"]
https_first_disabled = true
skip-if = [
"apple_catalina", # Bug 1625690
"apple_silicon", # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
"os == 'linux'", # Bug 1748986, bug 1775824
]
support-files = [
"searchSuggestionEngine.xml",
"searchSuggestionEngine.sjs",
]
["browser_urlbar_event_telemetry_noEvent.js"]
["browser_urlbar_selection.js"]
skip-if = ["(os == 'mac')"] # bug 1570474

View File

@ -0,0 +1,357 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.defineESModuleGetters(this, {
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
});
const TEST_ENGINE_NAME = "Test";
const TEST_ENGINE_ALIAS = "@test";
const TEST_ENGINE_DOMAIN = "example.com";
// Each test is a function that executes an urlbar action and returns the
// expected event object.
const tests = [
async function (win) {
info("Type something, blur.");
win.gURLBar.select();
EventUtils.synthesizeKey("x", {}, win);
win.gURLBar.blur();
return {
category: "urlbar",
method: "abandonment",
object: "blur",
value: "typed",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "1",
numWords: "1",
},
};
},
async function (win) {
info("Open the panel with DOWN, don't type, blur it.");
await addTopSite("http://example.org/");
win.gURLBar.value = "";
win.gURLBar.select();
await UrlbarTestUtils.promisePopupOpen(win, () => {
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
});
win.gURLBar.blur();
return {
category: "urlbar",
method: "abandonment",
object: "blur",
value: "topsites",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
},
};
},
async function (win) {
info("With pageproxystate=valid, autoopen the panel, don't type, blur it.");
win.gURLBar.value = "";
await UrlbarTestUtils.promisePopupOpen(win, () => {
win.document.getElementById("Browser:OpenLocation").doCommand();
});
win.gURLBar.blur();
return {
category: "urlbar",
method: "abandonment",
object: "blur",
value: "topsites",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
},
};
},
async function (win) {
info("Enter search mode from Top Sites.");
await updateTopSites(sites => true, /* enableSearchShorcuts */ true);
win.gURLBar.value = "";
win.gURLBar.select();
await BrowserTestUtils.waitForCondition(async () => {
await UrlbarTestUtils.promisePopupOpen(win, () => {
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
});
if (UrlbarTestUtils.getResultCount(win) > 1) {
return true;
}
win.gURLBar.view.close();
return false;
});
while (win.gURLBar.searchMode?.engineName != "Google") {
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
}
let element = UrlbarTestUtils.getSelectedRow(win);
Assert.ok(
element.result.source == UrlbarUtils.RESULT_SOURCE.SEARCH,
"The selected result is a search Top Site."
);
let engine = element.result.payload.engine;
let searchPromise = UrlbarTestUtils.promiseSearchComplete(win);
EventUtils.synthesizeMouseAtCenter(element, {}, win);
await searchPromise;
await UrlbarTestUtils.assertSearchMode(win, {
engineName: engine,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
entry: "topsites_urlbar",
});
await UrlbarTestUtils.exitSearchMode(win);
// To avoid needing to add a custom search shortcut Top Site, we just
// abandon this interaction.
await UrlbarTestUtils.promisePopupClose(win, () => {
win.gURLBar.blur();
});
return [
// engagement on the top sites search engine to enter search mode
{
category: "urlbar",
method: "engagement",
object: "click",
value: "topsites",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
selIndex: "0",
selType: "searchengine",
provider: "UrlbarProviderTopSites",
},
},
// abandonment
{
category: "urlbar",
method: "abandonment",
object: "blur",
value: "topsites",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
},
},
];
},
async function (win) {
info("Open search mode from a tab-to-search result.");
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.tabToSearch.onboard.interactionsLeft", 0]],
});
await PlacesUtils.history.clear();
for (let i = 0; i < 3; i++) {
await PlacesTestUtils.addVisits([`https://${TEST_ENGINE_DOMAIN}/`]);
}
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
value: TEST_ENGINE_DOMAIN.slice(0, 4),
});
let tabToSearchResult = (
await UrlbarTestUtils.waitForAutocompleteResultAt(win, 1)
).result;
Assert.equal(
tabToSearchResult.providerName,
"TabToSearch",
"The second result is a tab-to-search result."
);
// Select the tab-to-search result.
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
let searchPromise = UrlbarTestUtils.promiseSearchComplete(win);
EventUtils.synthesizeKey("KEY_Enter", {}, win);
await searchPromise;
await UrlbarTestUtils.assertSearchMode(win, {
engineName: TEST_ENGINE_NAME,
entry: "tabtosearch",
});
// Abandon the interaction since simply entering search mode is not
// considered the end of an engagement.
await UrlbarTestUtils.promisePopupClose(win, () => {
win.gURLBar.blur();
});
await PlacesUtils.history.clear();
await SpecialPowers.popPrefEnv();
return [
// engagement on the tab-to-search to enter search mode
{
category: "urlbar",
method: "engagement",
object: "enter",
value: "typed",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "4",
numWords: "1",
selIndex: "1",
selType: "tabtosearch",
provider: "TabToSearch",
},
},
// abandonment
{
category: "urlbar",
method: "abandonment",
object: "blur",
value: "typed",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "0",
numWords: "0",
},
},
];
},
async function (win) {
info(
"With pageproxystate=invalid, open retained results, don't type, blur it."
);
win.gURLBar.value = "mochi.test";
win.gURLBar.setPageProxyState("invalid");
await UrlbarTestUtils.promisePopupOpen(win, () => {
win.document.getElementById("Browser:OpenLocation").doCommand();
});
win.gURLBar.blur();
return {
category: "urlbar",
method: "abandonment",
object: "blur",
value: "returned",
extra: {
elapsed: val => parseInt(val) > 0,
numChars: "10",
numWords: "1",
},
};
},
];
add_setup(async function () {
await PlacesUtils.history.clear();
// Create a new search engine and mark it as default
let engine = await SearchTestUtils.promiseNewSearchEngine({
url: getRootDirectory(gTestPath) + "searchSuggestionEngine.xml",
setAsDefault: true,
});
await Services.search.moveEngine(engine, 0);
await SearchTestUtils.installSearchExtension({
name: TEST_ENGINE_NAME,
keyword: TEST_ENGINE_ALIAS,
search_url: `https://${TEST_ENGINE_DOMAIN}/`,
});
// This test used to rely on the initial timer of
// TestUtils.waitForCondition. See bug 1667216.
let originalWaitForCondition = TestUtils.waitForCondition;
TestUtils.waitForCondition = async function (
condition,
msg,
interval = 100,
maxTries = 50
) {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 100));
return originalWaitForCondition(condition, msg, interval, maxTries);
};
registerCleanupFunction(async function () {
await PlacesUtils.history.clear();
TestUtils.waitForCondition = originalWaitForCondition;
});
});
async function doTest(eventTelemetryEnabled) {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", eventTelemetryEnabled]],
});
const win = await BrowserTestUtils.openNewBrowserWindow();
// This is not necessary after each loop, because assertEvents does it.
Services.telemetry.clearEvents();
Services.telemetry.clearScalars();
for (let i = 0; i < tests.length; i++) {
info(`Running test at index ${i}`);
let events = await tests[i](win);
if (!Array.isArray(events)) {
events = [events];
}
// Always blur to ensure it's not accounted as an additional abandonment.
win.gURLBar.setSearchMode({});
win.gURLBar.blur();
TelemetryTestUtils.assertEvents(eventTelemetryEnabled ? events : [], {
category: "urlbar",
});
// Scalars should be recorded regardless of `eventTelemetry.enabled`.
let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
TelemetryTestUtils.assertScalar(
scalars,
"urlbar.engagement",
events.filter(e => e.method == "engagement").length || undefined
);
TelemetryTestUtils.assertScalar(
scalars,
"urlbar.abandonment",
events.filter(e => e.method == "abandonment").length || undefined
);
await UrlbarTestUtils.formHistory.clear(win);
}
await BrowserTestUtils.closeWindow(win);
await SpecialPowers.popPrefEnv();
}
add_task(async function enabled() {
await doTest(true);
});
add_task(async function disabled() {
await doTest(false);
});
/**
* Replaces the contents of Top Sites with the specified site.
*
* @param {string} site
* A site to add to Top Sites.
*/
async function addTopSite(site) {
await PlacesUtils.history.clear();
for (let i = 0; i < 5; i++) {
await PlacesTestUtils.addVisits(site);
}
await updateTopSites(sites => sites && sites[0] && sites[0].url == site);
}

View File

@ -0,0 +1,81 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const tests = [
async function (win) {
info("Type something, click on search settings.");
await BrowserTestUtils.withNewTab(
{ gBrowser: win.gBrowser, url: "about:blank" },
async browser => {
win.gURLBar.select();
const promise = onSyncPaneLoaded();
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
value: "x",
fireInputEvent: true,
});
UrlbarTestUtils.getOneOffSearchButtons(win).settingsButton.click();
await promise;
}
);
return null;
},
async function (win) {
info("Type something, Up, Enter on search settings.");
await BrowserTestUtils.withNewTab(
{ gBrowser: win.gBrowser, url: "about:blank" },
async browser => {
win.gURLBar.select();
const promise = onSyncPaneLoaded();
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
value: "x",
fireInputEvent: true,
});
EventUtils.synthesizeKey("KEY_ArrowUp", {}, win);
Assert.ok(
UrlbarTestUtils.getOneOffSearchButtons(
win
).selectedButton.classList.contains("search-setting-button"),
"Should have selected the settings button"
);
EventUtils.synthesizeKey("VK_RETURN", {}, win);
await promise;
}
);
return null;
},
];
function onSyncPaneLoaded() {
return new Promise(resolve => {
Services.obs.addObserver(function panesLoadedObs() {
Services.obs.removeObserver(panesLoadedObs, "sync-pane-loaded");
resolve();
}, "sync-pane-loaded");
});
}
add_task(async function test() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.eventTelemetry.enabled", true]],
});
const win = await BrowserTestUtils.openNewBrowserWindow();
// This is not necessary after each loop, because assertEvents does it.
Services.telemetry.clearEvents();
for (let i = 0; i < tests.length; i++) {
info(`Running no event test at index ${i}`);
await tests[i](win);
// Always blur to ensure it's not accounted as an additional abandonment.
win.gURLBar.blur();
TelemetryTestUtils.assertEvents([], { category: "urlbar" });
}
await BrowserTestUtils.closeWindow(win);
});

View File

@ -10,12 +10,6 @@ const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
const { ExtensionPreferenceManager } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPreferencesManager.sys.mjs"
);
var { getSettingsAPI } = ExtensionPreferenceManager;
ChromeUtils.defineESModuleGetters(this, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
Preferences: "resource://gre/modules/Preferences.sys.mjs",
@ -56,12 +50,9 @@ this.experiments_urlbar = class extends ExtensionAPI {
window.gURLBar.setPageProxyState("invalid");
},
engagementTelemetry: getSettingsAPI({
context,
name: "engagementTelemetry",
readOnly: true,
callback: () => false,
}),
engagementTelemetry: this._getDefaultSettingsAPI(
"browser.urlbar.eventTelemetry.enabled"
),
extensionTimeout: this._getDefaultSettingsAPI(
"browser.urlbar.extension.timeout"

View File

@ -18,4 +18,6 @@ prefs = ["browser.bookmarks.testing.skipDefaultBookmarksImport=true"]
["browser_ext_urlbar_dynamicResult.js"]
support-files = ["dynamicResult.css"]
["browser_ext_urlbar_engagementTelemetry.js"]
["browser_ext_urlbar_extensionTimeout.js"]

View File

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* global browser */
// This tests the browser.experiments.urlbar.engagementTelemetry WebExtension
// Experiment API.
"use strict";
add_settings_tasks("browser.urlbar.eventTelemetry.enabled", "boolean", () => {
browser.test.onMessage.addListener(async (method, arg) => {
let result = await browser.experiments.urlbar.engagementTelemetry[method](
arg
);
browser.test.sendMessage("done", result);
});
});

View File

@ -756,6 +756,56 @@ aboutpreferences:
- "gijs@mozilla.com"
expiry_version: never
urlbar:
engagement:
objects: ["click", "enter", "paste_go", "drop_go"]
release_channel_collection: opt-out
products:
- "firefox"
record_in_processes: ["main"]
description: >
This is recorded on urlbar engagement, that is when the user picks a
search result.
The value field records the initial interaction type. One of:
"typed", "dropped", "pasted", "topsites"
bug_numbers: [1559136, 1671404, 1466103]
notification_emails:
- "fx-search-telemetry@mozilla.com"
expiry_version: never
extra_keys:
elapsed: engagement time in milliseconds.
numChars: number of input characters.
numWords: number of words in the input.
selIndex: index of the selected result in the urlbar panel, or -1.
selType: >
type of the selected result in the urlbar panel. One of:
"autofill", "visit", "bookmark", "bookmark_adaptive", "history",
"history_adaptive", "keyword", "search", "searchsuggestion",
"searchsuggestion_rich", "switchtab", "remotetab", "extension",
"oneoff", "keywordoffer", "canonized", "tip", "tiphelp",
"formhistory", "tabtosearch", "quicksuggest", "weather",
"clipboard", "dynamic-wikipedia", "navigational", "none"
provider: The name of the provider that presented the result.
abandonment:
objects: ["blur"]
release_channel_collection: opt-out
products:
- "firefox"
record_in_processes: ["main"]
description: >
This is recorded on urlbar search abandon, that is when the user starts
an interaction but then blurs the urlbar.
The value field records the initial interaction type. One of:
"typed", "dropped", "pasted", "topsites"
bug_numbers: [1559136]
notification_emails:
- "fx-search-telemetry@mozilla.com"
expiry_version: never
extra_keys:
elapsed: abandonement time in milliseconds.
numChars: number of input characters.
numWords: number of words in the input.
normandy:
enroll:
objects: ["preference_study", "addon_study", "preference_rollout", "addon_rollout", "nimbus_experiment"]