summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
commitda4c7e7ed675c3bf405668739c3012d140856109 (patch)
treecdd868dba063fecba609a1d819de271f0d51b23e /browser/components/urlbar
parentAdding upstream version 125.0.3. (diff)
downloadfirefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz
firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/urlbar')
-rw-r--r--browser/components/urlbar/.eslintrc.js2
-rw-r--r--browser/components/urlbar/UrlbarController.sys.mjs121
-rw-r--r--browser/components/urlbar/UrlbarInput.sys.mjs29
-rw-r--r--browser/components/urlbar/UrlbarPrefs.sys.mjs20
-rw-r--r--browser/components/urlbar/UrlbarProviderAboutPages.sys.mjs4
-rw-r--r--browser/components/urlbar/UrlbarProviderAutofill.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderCalculator.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderClipboard.sys.mjs5
-rw-r--r--browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs4
-rw-r--r--browser/components/urlbar/UrlbarProviderInterventions.sys.mjs8
-rw-r--r--browser/components/urlbar/UrlbarProviderOmnibox.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderPlaces.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs4
-rw-r--r--browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs12
-rw-r--r--browser/components/urlbar/UrlbarProviderQuickSuggestContextualOptIn.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderRecentSearches.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderSearchSuggestions.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderSearchTips.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderTabToSearch.sys.mjs4
-rw-r--r--browser/components/urlbar/UrlbarProviderTokenAliasEngines.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderTopSites.sys.mjs10
-rw-r--r--browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs2
-rw-r--r--browser/components/urlbar/UrlbarProviderWeather.sys.mjs6
-rw-r--r--browser/components/urlbar/UrlbarProvidersManager.sys.mjs8
-rw-r--r--browser/components/urlbar/UrlbarUtils.sys.mjs32
-rw-r--r--browser/components/urlbar/UrlbarView.sys.mjs10
-rw-r--r--browser/components/urlbar/docs/dynamic-result-types.rst6
-rw-r--r--browser/components/urlbar/metrics.yaml38
-rw-r--r--browser/components/urlbar/pings.yaml16
-rw-r--r--browser/components/urlbar/private/AddonSuggestions.sys.mjs10
-rw-r--r--browser/components/urlbar/private/AdmWikipedia.sys.mjs5
-rw-r--r--browser/components/urlbar/private/MDNSuggestions.sys.mjs10
-rw-r--r--browser/components/urlbar/private/SuggestBackendRust.sys.mjs9
-rw-r--r--browser/components/urlbar/private/YelpSuggestions.sys.mjs10
-rw-r--r--browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs17
-rw-r--r--browser/components/urlbar/tests/browser-tips/browser_picks.js10
-rw-r--r--browser/components/urlbar/tests/browser-tips/browser_searchTips.js2
-rw-r--r--browser/components/urlbar/tests/browser-tips/browser_searchTips_interaction.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser.toml14
-rw-r--r--browser/components/urlbar/tests/browser/browser_UrlbarInput_overflow.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser_aboutHomeLoading.js4
-rw-r--r--browser/components/urlbar/tests/browser/browser_acknowledgeFeedbackAndDismissal.js4
-rw-r--r--browser/components/urlbar/tests/browser/browser_copy_during_load.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser_dynamicResults.js4
-rw-r--r--browser/components/urlbar/tests/browser/browser_engagement.js29
-rw-r--r--browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js288
-rw-r--r--browser/components/urlbar/tests/browser/browser_locationBarCommand.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser_primary_selection_safe_on_new_tab.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser_raceWithTabs.js4
-rw-r--r--browser/components/urlbar/tests/browser/browser_result_menu.js12
-rw-r--r--browser/components/urlbar/tests/browser/browser_stop_pending.js4
-rw-r--r--browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js4
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser.toml8
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_type.js8
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js2
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js48
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js2
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_potential_exposure.js438
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_reenter.js79
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/head.js8
-rw-r--r--browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs17
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js37
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_addons.js7
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_block.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_mdn.js68
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_pocket.js20
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_gleanEmptyStrings.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_impressionEdgeCases.js11
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_nonsponsored.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_sponsored.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/head.js41
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/head.js10
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_impressionCaps.js2
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merinoSessions.js2
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_weather.js2
-rw-r--r--browser/components/urlbar/tests/unit/test_exposure.js11
-rw-r--r--browser/components/urlbar/tests/unit/test_l10nCache.js2
80 files changed, 1337 insertions, 327 deletions
diff --git a/browser/components/urlbar/.eslintrc.js b/browser/components/urlbar/.eslintrc.js
index 8ead689bcc..aac2436d20 100644
--- a/browser/components/urlbar/.eslintrc.js
+++ b/browser/components/urlbar/.eslintrc.js
@@ -5,8 +5,6 @@
"use strict";
module.exports = {
- extends: ["plugin:mozilla/require-jsdoc"],
-
rules: {
"mozilla/var-only-at-top-level": "error",
"no-unused-expressions": "error",
diff --git a/browser/components/urlbar/UrlbarController.sys.mjs b/browser/components/urlbar/UrlbarController.sys.mjs
index 9bfc3a645d..7e4d0ff1c5 100644
--- a/browser/components/urlbar/UrlbarController.sys.mjs
+++ b/browser/components/urlbar/UrlbarController.sys.mjs
@@ -133,6 +133,7 @@ export class UrlbarController {
// notifications related to the previous query.
this.notify(NOTIFICATIONS.QUERY_STARTED, queryContext);
await this.manager.startQuery(queryContext, this);
+
// If the query has been cancelled, onQueryFinished was notified already.
// Note this._lastQueryContextWrapper may have changed in the meanwhile.
if (
@@ -144,6 +145,16 @@ export class UrlbarController {
this.manager.cancelQuery(queryContext);
this.notify(NOTIFICATIONS.QUERY_FINISHED, queryContext);
}
+
+ // Record a potential exposure if the current search string matches one of
+ // the registered keywords.
+ if (!queryContext.isPrivate) {
+ let searchStr = queryContext.trimmedLowerCaseSearchString;
+ if (lazy.UrlbarPrefs.get("potentialExposureKeywords").has(searchStr)) {
+ this.engagementEvent.addPotentialExposure(searchStr);
+ }
+ }
+
return queryContext;
}
@@ -335,7 +346,7 @@ export class UrlbarController {
}
event.preventDefault();
break;
- case KeyEvent.DOM_VK_TAB:
+ case KeyEvent.DOM_VK_TAB: {
// It's always possible to tab through results when the urlbar was
// focused with the mouse or has a search string, or when the view
// already has a selection.
@@ -368,6 +379,7 @@ export class UrlbarController {
event.preventDefault();
}
break;
+ }
case KeyEvent.DOM_VK_PAGE_DOWN:
case KeyEvent.DOM_VK_PAGE_UP:
if (event.ctrlKey) {
@@ -592,8 +604,8 @@ export class UrlbarController {
/**
* Triggers a "dismiss" engagement for the selected result if one is selected
* and it's not the heuristic. Providers that can respond to dismissals of
- * their results should implement `onEngagement()`, handle the dismissal, and
- * call `controller.removeResult()`.
+ * their results should implement `onLegacyEngagement()`, handle the
+ * dismissal, and call `controller.removeResult()`.
*
* @param {Event} event
* The event that triggered dismissal.
@@ -783,13 +795,6 @@ class TelemetryEvent {
interactionType: this._getStartInteractionType(event, searchString),
searchString,
};
-
- this._controller.manager.notifyEngagementChange(
- "start",
- queryContext,
- {},
- this._controller
- );
}
/**
@@ -821,17 +826,31 @@ class TelemetryEvent {
* @param {DOMElement} [details.element] The picked view element.
*/
record(event, details) {
+ // Prevent re-entering `record()`. This can happen because
+ // `#internalRecord()` will notify an engagement to the provider, that may
+ // execute an action blurring the input field. Then both an engagement
+ // and an abandonment would be recorded for the same session.
+ // Nulling out `_startEventInfo` doesn't save us in this case, because it
+ // happens after `#internalRecord()`, and `isSessionOngoing` must be
+ // calculated inside it.
+ if (this.#handlingRecord) {
+ return;
+ }
+
// This should never throw, or it may break the urlbar.
try {
- this._internalRecord(event, details);
+ this.#handlingRecord = true;
+ this.#internalRecord(event, details);
} catch (ex) {
console.error("Could not record event: ", ex);
} finally {
+ this.#handlingRecord = false;
+
// Reset the start event info except for engagements that do not end the
// search session. In that case, the view stays open and further
// engagements are possible and should be recorded when they occur.
// (`details.isSessionOngoing` is not a param; rather, it's set by
- // `_internalRecord()`.)
+ // `#internalRecord()`.)
if (!details.isSessionOngoing) {
this._startEventInfo = null;
this._discarded = false;
@@ -839,19 +858,10 @@ class TelemetryEvent {
}
}
- _internalRecord(event, details) {
+ #internalRecord(event, details) {
const startEventInfo = this._startEventInfo;
if (!this._category || !startEventInfo) {
- if (this._discarded && this._category && details?.selType !== "dismiss") {
- let { queryContext } = this._controller._lastQueryContextWrapper || {};
- this._controller.manager.notifyEngagementChange(
- "discard",
- queryContext,
- {},
- this._controller
- );
- }
return;
}
if (
@@ -938,6 +948,10 @@ class TelemetryEvent {
}
);
+ if (!details.isSessionOngoing) {
+ this.#recordEndOfSessionTelemetry(details.searchString);
+ }
+
if (skipLegacyTelemetry) {
this._controller.manager.notifyEngagementChange(
method,
@@ -1091,23 +1105,6 @@ class TelemetryEvent {
return;
}
- // First check to see if we can record an exposure event
- if (method === "abandonment" || method === "engagement") {
- if (this.#exposureResultTypes.size) {
- let exposure = {
- results: [...this.#exposureResultTypes].sort().join(","),
- };
- this._controller.logger.debug(
- `exposure event: ${JSON.stringify(exposure)}`
- );
- Glean.urlbar.exposure.record(exposure);
- }
-
- // reset the provider list on the controller
- this.#exposureResultTypes.clear();
- this.#tentativeExposureResultTypes.clear();
- }
-
this._controller.logger.info(
`${method} event: ${JSON.stringify(eventInfo)}`
);
@@ -1115,6 +1112,38 @@ class TelemetryEvent {
Glean.urlbar[method].record(eventInfo);
}
+ #recordEndOfSessionTelemetry(searchString) {
+ // exposures
+ if (this.#exposureResultTypes.size) {
+ let exposure = {
+ results: [...this.#exposureResultTypes].sort().join(","),
+ };
+ this._controller.logger.debug(
+ `exposure event: ${JSON.stringify(exposure)}`
+ );
+ Glean.urlbar.exposure.record(exposure);
+ this.#exposureResultTypes.clear();
+ }
+ this.#tentativeExposureResultTypes.clear();
+
+ // potential exposures
+ if (this.#potentialExposureKeywords.size) {
+ let normalizedSearchString = searchString.trim().toLowerCase();
+ for (let keyword of this.#potentialExposureKeywords) {
+ let data = {
+ keyword,
+ terminal: keyword == normalizedSearchString,
+ };
+ this._controller.logger.debug(
+ `potential_exposure event: ${JSON.stringify(data)}`
+ );
+ Glean.urlbar.potentialExposure.record(data);
+ }
+ GleanPings.urlbarPotentialExposure.submit();
+ this.#potentialExposureKeywords.clear();
+ }
+ }
+
/**
* Registers an exposure for a result in the current urlbar session. All
* exposures that are added during a session are recorded in an exposure event
@@ -1164,6 +1193,16 @@ class TelemetryEvent {
this.#tentativeExposureResultTypes.clear();
}
+ /**
+ * Registers a potential exposure in the current urlbar session.
+ *
+ * @param {string} keyword
+ * The keyword that was matched.
+ */
+ addPotentialExposure(keyword) {
+ this.#potentialExposureKeywords.add(keyword);
+ }
+
#getInteractionType(
method,
startEventInfo,
@@ -1350,8 +1389,12 @@ class TelemetryEvent {
}
}
+ // Used to avoid re-entering `record()`.
+ #handlingRecord = false;
+
#previousSearchWordsSet = null;
#exposureResultTypes = new Set();
#tentativeExposureResultTypes = new Set();
+ #potentialExposureKeywords = new Set();
}
diff --git a/browser/components/urlbar/UrlbarInput.sys.mjs b/browser/components/urlbar/UrlbarInput.sys.mjs
index 96fc7b9301..a96e862cff 100644
--- a/browser/components/urlbar/UrlbarInput.sys.mjs
+++ b/browser/components/urlbar/UrlbarInput.sys.mjs
@@ -1364,7 +1364,7 @@ export class UrlbarInput {
// The value setter clobbers the actiontype attribute, so we need this
// helper to restore it afterwards.
const setValueAndRestoreActionType = (value, allowTrim) => {
- this._setValue(value, allowTrim);
+ this._setValue(value, { allowTrim });
switch (result.type) {
case lazy.UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
@@ -1555,7 +1555,7 @@ export class UrlbarInput {
!this.value.endsWith(" ")
) {
this._autofillPlaceholder = null;
- this._setValue(this.window.gBrowser.userTypedValue, false);
+ this._setValue(this.window.gBrowser.userTypedValue);
}
return false;
@@ -1940,7 +1940,7 @@ export class UrlbarInput {
}
set value(val) {
- this._setValue(val, true);
+ this._setValue(val, { allowTrim: true });
}
get lastSearchString() {
@@ -2107,7 +2107,7 @@ export class UrlbarInput {
this.searchMode = searchMode;
let value = result.payload.query?.trimStart() || "";
- this._setValue(value, false);
+ this._setValue(value);
if (startQuery) {
this.startQuery({ allowAutofill: false });
@@ -2253,10 +2253,6 @@ export class UrlbarInput {
"--urlbar-height",
px(getBoundsWithoutFlushing(this.textbox).height)
);
- this.textbox.style.setProperty(
- "--urlbar-toolbar-height",
- px(getBoundsWithoutFlushing(this._toolbar).height)
- );
this.setAttribute("breakout", "true");
this.textbox.parentNode.setAttribute("breakout", "true");
@@ -2266,7 +2262,16 @@ export class UrlbarInput {
});
}
- _setValue(val, allowTrim) {
+ /**
+ * Sets the input field value.
+ *
+ * @param {string} val The new value to set.
+ * @param {object} [options] Options for setting.
+ * @param {boolean} [options.allowTrim] Whether the value can be trimmed.
+ *
+ * @returns {string} The set value.
+ */
+ _setValue(val, { allowTrim = false } = {}) {
// Don't expose internal about:reader URLs to the user.
let originalUrl = lazy.ReaderMode.getOriginalUrlObjectForDisplay(val);
if (originalUrl) {
@@ -2730,7 +2735,7 @@ export class UrlbarInput {
}) {
// The autofilled value may be a URL that includes a scheme at the
// beginning. Do not allow it to be trimmed.
- this._setValue(value, false);
+ this._setValue(value);
this.inputField.setSelectionRange(selectionStart, selectionEnd);
this._autofillPlaceholder = {
value,
@@ -3152,8 +3157,8 @@ export class UrlbarInput {
this.select();
this.window.goDoCommand("cmd_paste");
this.setResultForCurrentValue(null);
- this.controller.clearLastQueryContextCache();
this.handleCommand();
+ this.controller.clearLastQueryContextCache();
this._suppressStartQuery = false;
});
@@ -3504,7 +3509,7 @@ export class UrlbarInput {
}
if (untrim) {
this._focusUntrimmedValue = this._untrimmedValue;
- this._setValue(this._focusUntrimmedValue, false);
+ this._setValue(this._focusUntrimmedValue);
}
}
diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs
index 022d0b1c7c..cd8a6b0f4c 100644
--- a/browser/components/urlbar/UrlbarPrefs.sys.mjs
+++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs
@@ -65,7 +65,7 @@ const PREF_URLBAR_DEFAULTS = new Map([
["autoFill.stddevMultiplier", [0.0, "float"]],
// Feature gate pref for clipboard suggestions in the urlbar.
- ["clipboard.featureGate", true],
+ ["clipboard.featureGate", false],
// Whether to show a link for using the search functionality provided by the
// active view if the the view utilizes OpenSearch.
@@ -1507,12 +1507,28 @@ class Preferences {
return this.shouldHandOffToSearchModePrefs.some(
prefName => !this.get(prefName)
);
- case "autoFillAdaptiveHistoryUseCountThreshold":
+ case "autoFillAdaptiveHistoryUseCountThreshold": {
const nimbusValue =
this._nimbus.autoFillAdaptiveHistoryUseCountThreshold;
return nimbusValue === undefined
? this.get("autoFill.adaptiveHistory.useCountThreshold")
: parseFloat(nimbusValue);
+ }
+ case "potentialExposureKeywords": {
+ // Get the keywords array from Nimbus or prefs and convert it to a Set.
+ // If the value comes from Nimbus, it will already be an array. If it
+ // comes from prefs, it should be a stringified array.
+ let value = this._readPref(pref);
+ if (typeof value == "string") {
+ try {
+ value = JSON.parse(value);
+ } catch (e) {}
+ }
+ if (!Array.isArray(value)) {
+ value = null;
+ }
+ return new Set(value);
+ }
}
return this._readPref(pref);
}
diff --git a/browser/components/urlbar/UrlbarProviderAboutPages.sys.mjs b/browser/components/urlbar/UrlbarProviderAboutPages.sys.mjs
index 62f85b2348..be607a80d5 100644
--- a/browser/components/urlbar/UrlbarProviderAboutPages.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderAboutPages.sys.mjs
@@ -49,7 +49,7 @@ class ProviderAboutPages extends UrlbarProvider {
* @returns {boolean} Whether this provider should be invoked for the search.
*/
isActive(queryContext) {
- return queryContext.trimmedSearchString.toLowerCase().startsWith("about:");
+ return queryContext.trimmedLowerCaseSearchString.startsWith("about:");
}
/**
@@ -61,7 +61,7 @@ class ProviderAboutPages extends UrlbarProvider {
* result. A UrlbarResult should be passed to it.
*/
startQuery(queryContext, addCallback) {
- let searchString = queryContext.trimmedSearchString.toLowerCase();
+ let searchString = queryContext.trimmedLowerCaseSearchString;
for (const aboutUrl of lazy.AboutPagesUtils.visibleAboutUrls) {
if (aboutUrl.startsWith(searchString)) {
let result = new lazy.UrlbarResult(
diff --git a/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs b/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
index 32e605206e..7470df0fea 100644
--- a/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
@@ -653,7 +653,7 @@ class ProviderAutofill extends UrlbarProvider {
queryType: QUERYTYPE.AUTOFILL_ADAPTIVE,
// `fullSearchString` is the value the user typed including a prefix if
// they typed one. `searchString` has been stripped of the prefix.
- fullSearchString: queryContext.searchString.toLowerCase(),
+ fullSearchString: queryContext.lowerCaseSearchString,
searchString: this._searchString,
strippedPrefix: this._strippedPrefix,
useCountThreshold: lazy.UrlbarPrefs.get(
diff --git a/browser/components/urlbar/UrlbarProviderCalculator.sys.mjs b/browser/components/urlbar/UrlbarProviderCalculator.sys.mjs
index a55531167c..3f0ffed299 100644
--- a/browser/components/urlbar/UrlbarProviderCalculator.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderCalculator.sys.mjs
@@ -157,7 +157,7 @@ class ProviderCalculator extends UrlbarProvider {
return viewUpdate;
}
- onEngagement(state, queryContext, details) {
+ onLegacyEngagement(state, queryContext, details) {
let { result } = details;
if (result?.providerName == this.name) {
lazy.ClipboardHelper.copyString(result.payload.value);
diff --git a/browser/components/urlbar/UrlbarProviderClipboard.sys.mjs b/browser/components/urlbar/UrlbarProviderClipboard.sys.mjs
index 5337e610cc..1dc5bb9b86 100644
--- a/browser/components/urlbar/UrlbarProviderClipboard.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderClipboard.sys.mjs
@@ -143,10 +143,7 @@ class ProviderClipboard extends UrlbarProvider {
addCallback(this, result);
}
- onEngagement(state, queryContext, details, controller) {
- if (!["engagement", "abandonment"].includes(state)) {
- return;
- }
+ onLegacyEngagement(state, queryContext, details, controller) {
const visibleResults = controller.view?.visibleResults ?? [];
for (const result of visibleResults) {
if (
diff --git a/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs b/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs
index 63c94ee8f3..5714f11e72 100644
--- a/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs
@@ -246,7 +246,7 @@ class ProviderContextualSearch extends UrlbarProvider {
};
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName == this.name) {
this.#pickResult(result, controller.browserWindow);
diff --git a/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs b/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
index f929a1c003..17b6a4c9b0 100644
--- a/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
@@ -200,7 +200,7 @@ class ProviderInputHistory extends UrlbarProvider {
}
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
@@ -236,7 +236,7 @@ class ProviderInputHistory extends UrlbarProvider {
SQL_ADAPTIVE_QUERY,
{
parent: lazy.PlacesUtils.tagsFolderId,
- search_string: queryContext.searchString.toLowerCase(),
+ search_string: queryContext.lowerCaseSearchString,
matchBehavior: Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE,
searchBehavior: lazy.UrlbarPrefs.get("defaultBehavior"),
userContextId: lazy.UrlbarPrefs.get("switchTabs.searchAllContainers")
diff --git a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
index 08b4ea36b7..68b9c1665d 100644
--- a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
@@ -703,7 +703,7 @@ class ProviderInterventions extends UrlbarProvider {
}
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
// `selType` is "tip" when the tip's main button is picked. Ignore clicks on
@@ -714,10 +714,8 @@ class ProviderInterventions extends UrlbarProvider {
this.#pickResult(result, controller.browserWindow);
}
- if (["engagement", "abandonment"].includes(state)) {
- for (let tip of this.tipsShownInCurrentEngagement) {
- Services.telemetry.keyedScalarAdd("urlbar.tips", `${tip}-shown`, 1);
- }
+ for (let tip of this.tipsShownInCurrentEngagement) {
+ Services.telemetry.keyedScalarAdd("urlbar.tips", `${tip}-shown`, 1);
}
this.tipsShownInCurrentEngagement.clear();
}
diff --git a/browser/components/urlbar/UrlbarProviderOmnibox.sys.mjs b/browser/components/urlbar/UrlbarProviderOmnibox.sys.mjs
index 351e8ff60b..362f683027 100644
--- a/browser/components/urlbar/UrlbarProviderOmnibox.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderOmnibox.sys.mjs
@@ -178,7 +178,7 @@ class ProviderOmnibox extends UrlbarProvider {
);
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs b/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
index 650acd1730..c94ebee80a 100644
--- a/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
@@ -1517,7 +1517,7 @@ class ProviderPlaces extends UrlbarProvider {
search.notifyResult(false);
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs b/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs
index f199b6b892..29370cbaaf 100644
--- a/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs
@@ -95,7 +95,7 @@ class ProviderQuickActions extends UrlbarProvider {
*/
async startQuery(queryContext, addCallback) {
await lazy.QuickActionsLoaderDefault.ensureLoaded();
- let input = queryContext.trimmedSearchString.toLowerCase();
+ let input = queryContext.trimmedLowerCaseSearchString;
if (
!queryContext.searchMode &&
@@ -241,7 +241,7 @@ class ProviderQuickActions extends UrlbarProvider {
}
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
// Ignore engagements on other results that didn't end the session.
if (details.result?.providerName != this.name && details.isSessionOngoing) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs b/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
index 78e254616e..fbc8cc8c3f 100644
--- a/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
@@ -229,7 +229,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
}
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
// Ignore engagements on other results that didn't end the session.
if (details.result?.providerName != this.name && details.isSessionOngoing) {
return;
@@ -237,7 +237,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
// Reset the Merino session ID when a session ends. By design for the user's
// privacy, we don't keep it around between engagements.
- if (state != "start" && !details.isSessionOngoing) {
+ if (!details.isSessionOngoing) {
this.#merino?.resetSession();
}
@@ -486,8 +486,8 @@ class ProviderQuickSuggest extends UrlbarProvider {
* end of the engagement or that was dismissed. Null if no quick suggest
* result was present.
* @param {object} details
- * The `details` object that was passed to `onEngagement()`. It must look
- * like this: `{ selType, selIndex }`
+ * The `details` object that was passed to `onLegacyEngagement()`. It must
+ * look like this: `{ selType, selIndex }`
*/
#recordEngagement(queryContext, result, details) {
let resultSelType = "";
@@ -781,8 +781,8 @@ class ProviderQuickSuggest extends UrlbarProvider {
* True if the main part of the result's row was clicked; false if a button
* like help or dismiss was clicked or if no part of the row was clicked.
* @param {object} options.details
- * The `details` object that was passed to `onEngagement()`. It must look
- * like this: `{ selType, selIndex }`
+ * The `details` object that was passed to `onLegacyEngagement()`. It must
+ * look like this: `{ selType, selIndex }`
*/
#recordNavSuggestionTelemetry({
queryContext,
diff --git a/browser/components/urlbar/UrlbarProviderQuickSuggestContextualOptIn.sys.mjs b/browser/components/urlbar/UrlbarProviderQuickSuggestContextualOptIn.sys.mjs
index 48006d09c0..b3c322ffa1 100644
--- a/browser/components/urlbar/UrlbarProviderQuickSuggestContextualOptIn.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderQuickSuggestContextualOptIn.sys.mjs
@@ -188,7 +188,7 @@ class ProviderQuickSuggestContextualOptIn extends UrlbarProvider {
row.ownerGlobal.A11yUtils.announce({ raw: alertText });
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderRecentSearches.sys.mjs b/browser/components/urlbar/UrlbarProviderRecentSearches.sys.mjs
index ceeba729d4..1565013440 100644
--- a/browser/components/urlbar/UrlbarProviderRecentSearches.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderRecentSearches.sys.mjs
@@ -63,7 +63,7 @@ class ProviderRecentSearches extends UrlbarProvider {
return 1;
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderSearchSuggestions.sys.mjs b/browser/components/urlbar/UrlbarProviderSearchSuggestions.sys.mjs
index 8cb3532d94..e3d13feb56 100644
--- a/browser/components/urlbar/UrlbarProviderSearchSuggestions.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderSearchSuggestions.sys.mjs
@@ -352,7 +352,7 @@ class ProviderSearchSuggestions extends UrlbarProvider {
return undefined;
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
let { result } = details;
if (result?.providerName != this.name) {
return;
diff --git a/browser/components/urlbar/UrlbarProviderSearchTips.sys.mjs b/browser/components/urlbar/UrlbarProviderSearchTips.sys.mjs
index b19528619c..a7a23a3228 100644
--- a/browser/components/urlbar/UrlbarProviderSearchTips.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderSearchTips.sys.mjs
@@ -273,7 +273,7 @@ class ProviderSearchTips extends UrlbarProvider {
lazy.UrlbarPrefs.set(`tipShownCount.${tip}`, MAX_SHOWN_COUNT);
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
// Ignore engagements on other results that didn't end the session.
let { result } = details;
if (result?.providerName != this.name && details.isSessionOngoing) {
diff --git a/browser/components/urlbar/UrlbarProviderTabToSearch.sys.mjs b/browser/components/urlbar/UrlbarProviderTabToSearch.sys.mjs
index 9aabef3d19..0cce6481b1 100644
--- a/browser/components/urlbar/UrlbarProviderTabToSearch.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderTabToSearch.sys.mjs
@@ -194,7 +194,7 @@ class ProviderTabToSearch extends UrlbarProvider {
* Called when a result from the provider is selected. "Selected" refers to
* the user highlighing the result with the arrow keys/Tab, before it is
* picked. onSelection is also called when a user clicks a result. In the
- * event of a click, onSelection is called just before onEngagement.
+ * event of a click, onSelection is called just before onLegacyEngagement.
*
* @param {UrlbarResult} result
* The result that was selected.
@@ -226,7 +226,7 @@ class ProviderTabToSearch extends UrlbarProvider {
}
}
- onEngagement(state, queryContext, details) {
+ onLegacyEngagement(state, queryContext, details) {
let { result, element } = details;
if (
result?.providerName == this.name &&
diff --git a/browser/components/urlbar/UrlbarProviderTokenAliasEngines.sys.mjs b/browser/components/urlbar/UrlbarProviderTokenAliasEngines.sys.mjs
index b3a91bcbe4..db9e8df382 100644
--- a/browser/components/urlbar/UrlbarProviderTokenAliasEngines.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderTokenAliasEngines.sys.mjs
@@ -173,7 +173,7 @@ class ProviderTokenAliasEngines extends UrlbarProvider {
}
async _getAutofillResult(queryContext) {
- let lowerCaseSearchString = queryContext.searchString.toLowerCase();
+ let { lowerCaseSearchString } = queryContext;
// The user is typing a specific engine. We should show a heuristic result.
for (let { engine, tokenAliases } of this._engines) {
diff --git a/browser/components/urlbar/UrlbarProviderTopSites.sys.mjs b/browser/components/urlbar/UrlbarProviderTopSites.sys.mjs
index e9d968f20f..a046de37d4 100644
--- a/browser/components/urlbar/UrlbarProviderTopSites.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderTopSites.sys.mjs
@@ -193,7 +193,7 @@ class ProviderTopSites extends UrlbarProvider {
return site;
});
- // Store Sponsored Top Sites so we can use it in `onEngagement`
+ // Store Sponsored Top Sites so we can use it in `onLegacyEngagement`
if (sponsoredSites.length) {
this.sponsoredSites = sponsoredSites;
}
@@ -333,12 +333,8 @@ class ProviderTopSites extends UrlbarProvider {
}
}
- onEngagement(state, queryContext) {
- if (
- !queryContext.isPrivate &&
- this.sponsoredSites &&
- ["engagement", "abandonment"].includes(state)
- ) {
+ onLegacyEngagement(state, queryContext) {
+ if (!queryContext.isPrivate && this.sponsoredSites) {
for (let site of this.sponsoredSites) {
Services.telemetry.keyedScalarAdd(
SCALAR_CATEGORY_TOPSITES,
diff --git a/browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs b/browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs
index 98c4d025e4..a5ad28d2aa 100644
--- a/browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderUnitConversion.sys.mjs
@@ -169,7 +169,7 @@ class ProviderUnitConversion extends UrlbarProvider {
addCallback(this, result);
}
- onEngagement(state, queryContext, details) {
+ onLegacyEngagement(state, queryContext, details) {
let { result, element } = details;
if (result?.providerName == this.name) {
const { textContent } = element.querySelector(
diff --git a/browser/components/urlbar/UrlbarProviderWeather.sys.mjs b/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
index 24342fecab..8e9b6b8f3e 100644
--- a/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
@@ -115,7 +115,7 @@ class ProviderWeather extends UrlbarProvider {
return false;
}
- return keywords.has(queryContext.searchString.trim().toLocaleLowerCase());
+ return keywords.has(queryContext.trimmedLowerCaseSearchString);
}
/**
@@ -163,7 +163,7 @@ class ProviderWeather extends UrlbarProvider {
return lazy.QuickSuggest.weather.getViewUpdate(result);
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
// Ignore engagements on other results that didn't end the session.
if (details.result?.providerName != this.name && details.isSessionOngoing) {
return;
@@ -243,7 +243,7 @@ class ProviderWeather extends UrlbarProvider {
* A non-empty string means the user picked the weather row or some part of
* it, and both impression and click telemetry will be recorded. The
* non-empty-string values come from the `details.selType` passed in to
- * `onEngagement()`; see `TelemetryEvent.typeFromElement()`.
+ * `onLegacyEngagement()`; see `TelemetryEvent.typeFromElement()`.
*/
#recordEngagementTelemetry(result, isPrivate, selType) {
// Indexes recorded in quick suggest telemetry are 1-based, so add 1 to the
diff --git a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
index 609b0735e1..ac70e03e1b 100644
--- a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
+++ b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
@@ -334,11 +334,11 @@ class ProvidersManager {
/**
* Notifies all providers when the user starts and ends an engagement with the
- * urlbar. For details on parameters, see UrlbarProvider.onEngagement().
+ * urlbar. For details on parameters, see
+ * UrlbarProvider.onLegacyEngagement().
*
* @param {string} state
- * The state of the engagement, one of: start, engagement, abandonment,
- * discard
+ * The state of the engagement, one of: engagement, abandonment
* @param {UrlbarQueryContext} queryContext
* The engagement's query context, if available.
* @param {object} details
@@ -349,7 +349,7 @@ class ProvidersManager {
notifyEngagementChange(state, queryContext, details = {}, controller) {
for (let provider of this.providers) {
provider.tryMethod(
- "onEngagement",
+ "onLegacyEngagement",
state,
queryContext,
details,
diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs
index 2bbb5d1ab0..9fca8426a3 100644
--- a/browser/components/urlbar/UrlbarUtils.sys.mjs
+++ b/browser/components/urlbar/UrlbarUtils.sys.mjs
@@ -854,7 +854,7 @@ export var UrlbarUtils = {
* @returns {string} The modified paste data.
*/
stripUnsafeProtocolOnPaste(pasteData) {
- while (true) {
+ for (;;) {
let scheme = "";
try {
scheme = Services.io.extractScheme(pasteData);
@@ -1831,6 +1831,9 @@ UrlbarUtils.RESULT_PAYLOAD_SCHEMA = {
isBlockable: {
type: "boolean",
},
+ isManageable: {
+ type: "boolean",
+ },
isPinned: {
type: "boolean",
},
@@ -2175,6 +2178,8 @@ export class UrlbarQueryContext {
this.pendingHeuristicProviders = new Set();
this.deferUserSelectionProviders = new Set();
this.trimmedSearchString = this.searchString.trim();
+ this.lowerCaseSearchString = this.searchString.toLowerCase();
+ this.trimmedLowerCaseSearchString = this.trimmedSearchString.toLowerCase();
this.userContextId =
lazy.UrlbarProviderOpenTabs.getUserContextIdForOpenPagesTable(
options.userContextId,
@@ -2431,21 +2436,12 @@ export class UrlbarProvider {
* @param {string} _state
* The state of the engagement, one of the following strings:
*
- * start
- * A new query has started in the urlbar.
* engagement
* The user picked a result in the urlbar or used paste-and-go.
* abandonment
* The urlbar was blurred (i.e., lost focus).
- * discard
- * This doesn't correspond to a user action, but it means that the
- * urlbar has discarded the engagement for some reason, and the
- * `onEngagement` implementation should ignore it.
- *
* @param {UrlbarQueryContext} _queryContext
- * The engagement's query context. This is *not* guaranteed to be defined
- * when `state` is "start". It will always be defined for "engagement" and
- * "abandonment".
+ * The engagement's query context.
* @param {object} _details
* This object is non-empty only when `state` is "engagement" or
* "abandonment", and it describes the search string and engaged result.
@@ -2479,7 +2475,7 @@ export class UrlbarProvider {
* @param {UrlbarController} _controller
* The associated controller.
*/
- onEngagement(_state, _queryContext, _details, _controller) {}
+ onLegacyEngagement(_state, _queryContext, _details, _controller) {}
/**
* Called before a result from the provider is selected. See `onSelection`
@@ -2497,8 +2493,8 @@ export class UrlbarProvider {
* Called when a result from the provider is selected. "Selected" refers to
* the user highlighing the result with the arrow keys/Tab, before it is
* picked. onSelection is also called when a user clicks a result. In the
- * event of a click, onSelection is called just before onEngagement. Note that
- * this is called when heuristic results are pre-selected.
+ * event of a click, onSelection is called just before onLegacyEngagement.
+ * Note that this is called when heuristic results are pre-selected.
*
* @param {UrlbarResult} _result
* The result that was selected.
@@ -2581,8 +2577,8 @@ export class UrlbarProvider {
/**
* Gets the list of commands that should be shown in the result menu for a
* given result from the provider. All commands returned by this method should
- * be handled by implementing `onEngagement()` with the possible exception of
- * commands automatically handled by the urlbar, like "help".
+ * be handled by implementing `onLegacyEngagement()` with the possible
+ * exception of commands automatically handled by the urlbar, like "help".
*
* @param {UrlbarResult} _result
* The menu will be shown for this result.
@@ -2594,8 +2590,8 @@ export class UrlbarProvider {
* {string} name
* The name of the command. Must be specified unless `children` is
* present. When a command is picked, its name will be passed as
- * `details.selType` to `onEngagement()`. The special name "separator"
- * will create a menu separator.
+ * `details.selType` to `onLegacyEngagement()`. The special name
+ * "separator" will create a menu separator.
* {object} l10n
* An l10n object for the command's label: `{ id, args }`
* Must be specified unless `name` is "separator".
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
index b5fe1e1955..3d6ea46781 100644
--- a/browser/components/urlbar/UrlbarView.sys.mjs
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
@@ -48,6 +48,7 @@ const ZERO_PREFIX_SCALAR_EXPOSURE = "urlbar.zeroprefix.exposure";
const RESULT_MENU_COMMANDS = {
DISMISS: "dismiss",
HELP: "help",
+ MANAGE: "manage",
};
const getBoundsWithoutFlushing = element =>
@@ -3139,6 +3140,15 @@ export class UrlbarView {
},
});
}
+ if (result.payload.isManageable) {
+ commands.push({
+ name: RESULT_MENU_COMMANDS.MANAGE,
+ l10n: {
+ id: "urlbar-result-menu-manage-firefox-suggest",
+ },
+ });
+ }
+
let rv = commands.length ? commands : null;
this.#resultMenuCommands.set(result, rv);
return rv;
diff --git a/browser/components/urlbar/docs/dynamic-result-types.rst b/browser/components/urlbar/docs/dynamic-result-types.rst
index f72c5e4a13..2c81c1656f 100644
--- a/browser/components/urlbar/docs/dynamic-result-types.rst
+++ b/browser/components/urlbar/docs/dynamic-result-types.rst
@@ -152,8 +152,8 @@ aren't relevant to dynamic result types, and you should choose values
appropriate to your use case.
If any elements created in the view for your results can be picked with the
-keyboard or mouse, then be sure to implement your provider's ``onEngagement``
-method.
+keyboard or mouse, then be sure to implement your provider's
+``onLegacyEngagement`` method.
For help on implementing providers in general, see the address bar's
`Architecture Overview`__.
@@ -616,7 +616,7 @@ URL Navigation
If a result's payload includes a string ``url`` property and a boolean
``shouldNavigate: true`` property, then picking the result will navigate to the
-URL. The ``onEngagement`` method of the result's provider will still be called
+URL. The ``onLegacyEngagement`` method of the result's provider will still be called
before navigation.
Text Highlighting
diff --git a/browser/components/urlbar/metrics.yaml b/browser/components/urlbar/metrics.yaml
index 95337d84eb..173ee08a10 100644
--- a/browser/components/urlbar/metrics.yaml
+++ b/browser/components/urlbar/metrics.yaml
@@ -110,7 +110,6 @@ urlbar:
`intervention_unknown`,
`intervention_update`,
`keyword`,
- `merino_adm_nonsponsored`,
`merino_adm_sponsored`,
`merino_amo`,
`merino_top_picks`,
@@ -159,6 +158,7 @@ urlbar:
notification_emails:
- fx-search-telemetry@mozilla.com
expires: never
+
engagement:
type: event
description: Recorded when the user executes an action on a result.
@@ -242,7 +242,6 @@ urlbar:
`intervention_unknown`,
`intervention_update`,
`keyword`,
- `merino_adm_nonsponsored`,
`merino_adm_sponsored`,
`merino_amo`,
`merino_top_picks`,
@@ -363,7 +362,6 @@ urlbar:
`intervention_unknown`,
`intervention_update`,
`keyword`,
- `merino_adm_nonsponsored`,
`merino_adm_sponsored`,
`merino_amo`,
`merino_top_picks`,
@@ -432,6 +430,40 @@ urlbar:
- fx-search-telemetry@mozilla.com
expires: never
+ potential_exposure:
+ type: event
+ description: >
+ This event is recorded in the `urlbar-potential-exposure` ping, which is
+ submitted at the end of urlbar sessions during which the user typed a
+ keyword defined by the Nimbus variable `potentialExposureKeywords`. A
+ "session" begins when the user focuses the urlbar and ends with an
+ engagement or abandonment. The ping will contain one event per unique
+ keyword that is typed during the session. This ping is not submitted for
+ sessions in private windows.
+ extra_keys:
+ keyword:
+ type: string
+ description: >
+ The matched keyword.
+ terminal:
+ type: boolean
+ description: >
+ Whether the matched keyword was present at the end of the urlbar
+ session. If true, the session ended with the keyword. If false, the
+ keyword was typed at some point during the session but the session
+ did not end with it.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881875
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881875
+ data_sensitivity:
+ - stored_content
+ notification_emails:
+ - fx-search-telemetry@mozilla.com
+ expires: never
+ send_in_pings:
+ - urlbar-potential-exposure
+
quick_suggest_contextual_opt_in:
type: event
description: >
diff --git a/browser/components/urlbar/pings.yaml b/browser/components/urlbar/pings.yaml
index 8153b62863..4c46f16909 100644
--- a/browser/components/urlbar/pings.yaml
+++ b/browser/components/urlbar/pings.yaml
@@ -19,3 +19,19 @@ quick-suggest:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1854755
notification_emails:
- najiang@mozilla.com
+
+urlbar-potential-exposure:
+ description: |
+ This ping is submitted at the end of urlbar sessions during which the user
+ typed a keyword defined by the Nimbus variable `potentialExposureKeywords`.
+ A "session" begins when the user focuses the urlbar and ends with an
+ engagement or abandonment. The ping will contain one
+ `urlbar.potential_exposure` event per unique keyword that is typed during
+ the session. This ping is not submitted for sessions in private windows.
+ include_client_id: false
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881875
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881875
+ notification_emails:
+ - fx-search-telemetry@mozilla.com
diff --git a/browser/components/urlbar/private/AddonSuggestions.sys.mjs b/browser/components/urlbar/private/AddonSuggestions.sys.mjs
index 23311cec1c..ace82e41d3 100644
--- a/browser/components/urlbar/private/AddonSuggestions.sys.mjs
+++ b/browser/components/urlbar/private/AddonSuggestions.sys.mjs
@@ -21,7 +21,7 @@ const UTM_PARAMS = {
};
const RESULT_MENU_COMMAND = {
- HELP: "help",
+ MANAGE: "manage",
NOT_INTERESTED: "not_interested",
NOT_RELEVANT: "not_relevant",
SHOW_LESS_FREQUENTLY: "show_less_frequently",
@@ -212,9 +212,9 @@ export class AddonSuggestions extends BaseFeature {
},
{ name: "separator" },
{
- name: RESULT_MENU_COMMAND.HELP,
+ name: RESULT_MENU_COMMAND.MANAGE,
l10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
+ id: "urlbar-result-menu-manage-firefox-suggest",
},
}
);
@@ -224,8 +224,8 @@ export class AddonSuggestions extends BaseFeature {
handleCommand(view, result, selType) {
switch (selType) {
- case RESULT_MENU_COMMAND.HELP:
- // "help" is handled by UrlbarInput, no need to do anything here.
+ case RESULT_MENU_COMMAND.MANAGE:
+ // "manage" is handled by UrlbarInput, no need to do anything here.
break;
// selType == "dismiss" when the user presses the dismiss key shortcut.
case "dismiss":
diff --git a/browser/components/urlbar/private/AdmWikipedia.sys.mjs b/browser/components/urlbar/private/AdmWikipedia.sys.mjs
index 3ab5bad09f..596e15df4c 100644
--- a/browser/components/urlbar/private/AdmWikipedia.sys.mjs
+++ b/browser/components/urlbar/private/AdmWikipedia.sys.mjs
@@ -190,14 +190,11 @@ export class AdmWikipedia extends BaseFeature {
sponsoredBlockId: suggestion.block_id,
sponsoredAdvertiser: suggestion.advertiser,
sponsoredIabCategory: suggestion.iab_category,
- helpUrl: lazy.QuickSuggest.HELP_URL,
- helpL10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
- },
isBlockable: true,
blockL10n: {
id: "urlbar-result-menu-dismiss-firefox-suggest",
},
+ isManageable: true,
};
let result = new lazy.UrlbarResult(
diff --git a/browser/components/urlbar/private/MDNSuggestions.sys.mjs b/browser/components/urlbar/private/MDNSuggestions.sys.mjs
index c9e7da18af..3efedbd12a 100644
--- a/browser/components/urlbar/private/MDNSuggestions.sys.mjs
+++ b/browser/components/urlbar/private/MDNSuggestions.sys.mjs
@@ -15,7 +15,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
});
const RESULT_MENU_COMMAND = {
- HELP: "help",
+ MANAGE: "manage",
NOT_INTERESTED: "not_interested",
NOT_RELEVANT: "not_relevant",
};
@@ -157,9 +157,9 @@ export class MDNSuggestions extends BaseFeature {
},
{ name: "separator" },
{
- name: RESULT_MENU_COMMAND.HELP,
+ name: RESULT_MENU_COMMAND.MANAGE,
l10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
+ id: "urlbar-result-menu-manage-firefox-suggest",
},
},
];
@@ -167,8 +167,8 @@ export class MDNSuggestions extends BaseFeature {
handleCommand(view, result, selType) {
switch (selType) {
- case RESULT_MENU_COMMAND.HELP:
- // "help" is handled by UrlbarInput, no need to do anything here.
+ case RESULT_MENU_COMMAND.MANAGE:
+ // "manage" is handled by UrlbarInput, no need to do anything here.
break;
// selType == "dismiss" when the user presses the dismiss key shortcut.
case "dismiss":
diff --git a/browser/components/urlbar/private/SuggestBackendRust.sys.mjs b/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
index 2d96e7540f..3993149757 100644
--- a/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
+++ b/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
@@ -136,11 +136,12 @@ export class SuggestBackendRust extends BaseFeature {
suggestion.provider = type;
suggestion.is_sponsored = type == "Amp" || type == "Yelp";
if (Array.isArray(suggestion.icon)) {
- suggestion.icon_blob = new Blob(
- [new Uint8Array(suggestion.icon)],
- type == "Yelp" ? { type: "image/svg+xml" } : null
- );
+ suggestion.icon_blob = new Blob([new Uint8Array(suggestion.icon)], {
+ type: suggestion.iconMimetype ?? "",
+ });
+
delete suggestion.icon;
+ delete suggestion.iconMimetype;
}
}
diff --git a/browser/components/urlbar/private/YelpSuggestions.sys.mjs b/browser/components/urlbar/private/YelpSuggestions.sys.mjs
index 4cf454c71d..e2a2803bd7 100644
--- a/browser/components/urlbar/private/YelpSuggestions.sys.mjs
+++ b/browser/components/urlbar/private/YelpSuggestions.sys.mjs
@@ -15,8 +15,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
});
const RESULT_MENU_COMMAND = {
- HELP: "help",
INACCURATE_LOCATION: "inaccurate_location",
+ MANAGE: "manage",
NOT_INTERESTED: "not_interested",
NOT_RELEVANT: "not_relevant",
SHOW_LESS_FREQUENTLY: "show_less_frequently",
@@ -168,9 +168,9 @@ export class YelpSuggestions extends BaseFeature {
},
{ name: "separator" },
{
- name: RESULT_MENU_COMMAND.HELP,
+ name: RESULT_MENU_COMMAND.MANAGE,
l10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
+ id: "urlbar-result-menu-manage-firefox-suggest",
},
}
);
@@ -180,8 +180,8 @@ export class YelpSuggestions extends BaseFeature {
handleCommand(view, result, selType, searchString) {
switch (selType) {
- case RESULT_MENU_COMMAND.HELP:
- // "help" is handled by UrlbarInput, no need to do anything here.
+ case RESULT_MENU_COMMAND.MANAGE:
+ // "manage" is handled by UrlbarInput, no need to do anything here.
break;
case RESULT_MENU_COMMAND.INACCURATE_LOCATION:
// Currently the only way we record this feedback is in the Glean
diff --git a/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs b/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
index cfc9ecb3d8..f576f4ca19 100644
--- a/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
@@ -158,7 +158,7 @@ export var UrlbarTestUtils = {
lazy.UrlbarPrefs.get("trimURLs") &&
value != lazy.BrowserUIUtils.trimURL(value)
) {
- window.gURLBar._setValue(value, false);
+ window.gURLBar._setValue(value);
fireInputEvent = true;
} else {
window.gURLBar.value = value;
@@ -1315,10 +1315,7 @@ export var UrlbarTestUtils = {
// Set most of the string directly instead of going through sendString,
// so that we don't make life unnecessarily hard for consumers by
// possibly starting multiple searches.
- win.gURLBar._setValue(
- text.substr(0, text.length - 1),
- false /* allowTrim = */
- );
+ win.gURLBar._setValue(text.substr(0, text.length - 1));
}
this.EventUtils.sendString(text.substr(-1, 1), win);
},
@@ -1490,7 +1487,7 @@ class TestProvider extends UrlbarProvider {
* @param {Function} [options.onSelection]
* If given, a function that will be called when
* {@link UrlbarView.#selectElement} method is called.
- * @param {Function} [options.onEngagement]
+ * @param {Function} [options.onLegacyEngagement]
* If given, a function that will be called when engagement.
* @param {Function} [options.delayResultsPromise]
* If given, we'll await on this before returning results.
@@ -1503,7 +1500,7 @@ class TestProvider extends UrlbarProvider {
addTimeout = 0,
onCancel = null,
onSelection = null,
- onEngagement = null,
+ onLegacyEngagement = null,
delayResultsPromise = null,
} = {}) {
if (delayResultsPromise && addTimeout) {
@@ -1520,7 +1517,7 @@ class TestProvider extends UrlbarProvider {
this._type = type;
this._onCancel = onCancel;
this._onSelection = onSelection;
- this._onEngagement = onEngagement;
+ this._onLegacyEngagement = onLegacyEngagement;
// As this has been a common source of mistakes, auto-upgrade the provider
// type to heuristic if any result is heuristic.
@@ -1574,8 +1571,8 @@ class TestProvider extends UrlbarProvider {
this._onSelection?.(result, element);
}
- onEngagement(state, queryContext, details, controller) {
- this._onEngagement?.(state, queryContext, details, controller);
+ onLegacyEngagement(state, queryContext, details, controller) {
+ this._onLegacyEngagement?.(state, queryContext, details, controller);
}
}
diff --git a/browser/components/urlbar/tests/browser-tips/browser_picks.js b/browser/components/urlbar/tests/browser-tips/browser_picks.js
index ba0ff69357..c9d725dfb5 100644
--- a/browser/components/urlbar/tests/browser-tips/browser_picks.js
+++ b/browser/components/urlbar/tests/browser-tips/browser_picks.js
@@ -117,8 +117,8 @@ async function doTest({ click, buttonUrl = undefined, helpUrl = undefined }) {
});
UrlbarProvidersManager.registerProvider(provider);
- let onEngagementPromise = new Promise(
- resolve => (provider.onEngagement = resolve)
+ let onLegacyEngagementPromise = new Promise(
+ resolve => (provider.onLegacyEngagement = resolve)
);
// Do a search to show our tip result.
@@ -142,8 +142,8 @@ async function doTest({ click, buttonUrl = undefined, helpUrl = undefined }) {
);
}
- // Now pick the target and wait for provider.onEngagement to be called and
- // the URL to load if necessary.
+ // Now pick the target and wait for provider.onLegacyEngagement to be called
+ // and the URL to load if necessary.
let loadPromise;
if (buttonUrl || helpUrl) {
loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
@@ -160,7 +160,7 @@ async function doTest({ click, buttonUrl = undefined, helpUrl = undefined }) {
EventUtils.synthesizeKey("KEY_Enter");
}
});
- await onEngagementPromise;
+ await onLegacyEngagementPromise;
await loadPromise;
// Check telemetry.
diff --git a/browser/components/urlbar/tests/browser-tips/browser_searchTips.js b/browser/components/urlbar/tests/browser-tips/browser_searchTips.js
index a82a2d658b..8c98e27993 100644
--- a/browser/components/urlbar/tests/browser-tips/browser_searchTips.js
+++ b/browser/components/urlbar/tests/browser-tips/browser_searchTips.js
@@ -18,7 +18,7 @@ ChromeUtils.defineESModuleGetters(this, {
"resource:///modules/UrlbarProviderSearchTips.sys.mjs",
});
-// These should match the same consts in UrlbarProviderSearchTips.jsm.
+// These should match the same consts in UrlbarProviderSearchTips.sys.mjs.
const MAX_SHOWN_COUNT = 4;
const LAST_UPDATE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
diff --git a/browser/components/urlbar/tests/browser-tips/browser_searchTips_interaction.js b/browser/components/urlbar/tests/browser-tips/browser_searchTips_interaction.js
index 72d05cf632..6c0550a2df 100644
--- a/browser/components/urlbar/tests/browser-tips/browser_searchTips_interaction.js
+++ b/browser/components/urlbar/tests/browser-tips/browser_searchTips_interaction.js
@@ -25,7 +25,7 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIClipboardHelper"
);
-// These should match the same consts in UrlbarProviderSearchTips.jsm.
+// These should match the same consts in UrlbarProviderSearchTips.sys.mjs.
const MAX_SHOWN_COUNT = 4;
const LAST_UPDATE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
diff --git a/browser/components/urlbar/tests/browser/browser.toml b/browser/components/urlbar/tests/browser/browser.toml
index b9934aa838..44b964e5ca 100644
--- a/browser/components/urlbar/tests/browser/browser.toml
+++ b/browser/components/urlbar/tests/browser/browser.toml
@@ -4,7 +4,11 @@ support-files = [
"head.js",
"head-common.js",
]
-
+skip-if = [
+ "os == 'linux' && os_version == '18.04' && asan", # long running manifest
+ "os == 'linux' && os_version == '18.04' && tsan", # long running manifest
+ "win11_2009 && asan", # long running manifest
+]
prefs = [
"browser.bookmarks.testing.skipDefaultBookmarksImport=true",
"browser.urlbar.trending.featureGate=false",
@@ -280,6 +284,8 @@ support-files = [
["browser_keyword_select_and_type.js"]
+["browser_less_common_selection_manipulations.js"]
+
["browser_loadRace.js"]
["browser_locationBarCommand.js"]
@@ -398,9 +404,6 @@ https_first_disabled = true
["browser_revert.js"]
-["browser_search_continuation.js"]
-support-files = ["search-engines", "../../../search/test/browser/trendingSuggestionEngine.sjs"]
-
["browser_searchFunction.js"]
["browser_searchHistoryLimit.js"]
@@ -490,6 +493,9 @@ support-files = [
["browser_search_bookmarks_from_bookmarks_menu.js"]
+["browser_search_continuation.js"]
+support-files = ["search-engines", "../../../search/test/browser/trendingSuggestionEngine.sjs"]
+
["browser_search_history_from_history_panel.js"]
["browser_selectStaleResults.js"]
diff --git a/browser/components/urlbar/tests/browser/browser_UrlbarInput_overflow.js b/browser/components/urlbar/tests/browser/browser_UrlbarInput_overflow.js
index f191cae321..d01734959a 100644
--- a/browser/components/urlbar/tests/browser/browser_UrlbarInput_overflow.js
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_overflow.js
@@ -2,7 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
-async function testVal(aExpected, overflowSide = "") {
+async function testVal(aExpected, overflowSide = null) {
info(`Testing ${aExpected}`);
try {
gURLBar.setURI(makeURI(aExpected));
diff --git a/browser/components/urlbar/tests/browser/browser_aboutHomeLoading.js b/browser/components/urlbar/tests/browser/browser_aboutHomeLoading.js
index 427a7419c8..bb710c7065 100644
--- a/browser/components/urlbar/tests/browser/browser_aboutHomeLoading.js
+++ b/browser/components/urlbar/tests/browser/browser_aboutHomeLoading.js
@@ -98,7 +98,7 @@ add_task(async function clearURLBarAfterManuallyLoadingAboutHome() {
() => {}
);
// This opens about:newtab:
- BrowserOpenTab();
+ BrowserCommands.openTab();
let tab = await promiseTabOpenedAndSwitchedTo;
is(gURLBar.value, "", "URL bar should be empty");
is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null");
@@ -132,7 +132,7 @@ add_task(async function dontTemporarilyShowAboutHome() {
let win = OpenBrowserWindow();
await windowOpenedPromise;
let promiseTabSwitch = BrowserTestUtils.switchTab(win.gBrowser, () => {});
- win.BrowserOpenTab();
+ win.BrowserCommands.openTab();
await promiseTabSwitch;
currentBrowser = win.gBrowser.selectedBrowser;
is(win.gBrowser.visibleTabs.length, 2, "2 tabs opened");
diff --git a/browser/components/urlbar/tests/browser/browser_acknowledgeFeedbackAndDismissal.js b/browser/components/urlbar/tests/browser/browser_acknowledgeFeedbackAndDismissal.js
index 8c4b05501e..54f40a85ee 100644
--- a/browser/components/urlbar/tests/browser/browser_acknowledgeFeedbackAndDismissal.js
+++ b/browser/components/urlbar/tests/browser/browser_acknowledgeFeedbackAndDismissal.js
@@ -389,11 +389,11 @@ class TestProvider extends UrlbarTestUtils.TestProvider {
];
}
- onEngagement(state, queryContext, details, controller) {
+ onLegacyEngagement(state, queryContext, details, controller) {
if (details.result?.providerName == this.name) {
let { selType } = details;
- info(`onEngagement called, selType=` + selType);
+ info(`onLegacyEngagement called, selType=` + selType);
if (!this.commandCount.hasOwnProperty(selType)) {
this.commandCount[selType] = 0;
diff --git a/browser/components/urlbar/tests/browser/browser_copy_during_load.js b/browser/components/urlbar/tests/browser/browser_copy_during_load.js
index 3eaa53bcda..e1d352a171 100644
--- a/browser/components/urlbar/tests/browser/browser_copy_during_load.js
+++ b/browser/components/urlbar/tests/browser/browser_copy_during_load.js
@@ -45,7 +45,7 @@ add_task(async function () {
null,
true
);
- BrowserStop();
+ BrowserCommands.stop();
await browserStoppedPromise;
});
});
diff --git a/browser/components/urlbar/tests/browser/browser_dynamicResults.js b/browser/components/urlbar/tests/browser/browser_dynamicResults.js
index aad15e0145..2ba1b7ab5f 100644
--- a/browser/components/urlbar/tests/browser/browser_dynamicResults.js
+++ b/browser/components/urlbar/tests/browser/browser_dynamicResults.js
@@ -511,7 +511,7 @@ add_task(async function shouldNavigate() {
await UrlbarTestUtils.promisePopupClose(window, () =>
EventUtils.synthesizeKey("KEY_Enter")
);
- // Verify that onEngagement was still called.
+ // Verify that onLegacyEngagement was still called.
let [result, pickedElement] = await pickPromise;
Assert.equal(result, row.result, "Picked result");
Assert.equal(pickedElement, element, "Picked element");
@@ -904,7 +904,7 @@ class TestProvider extends UrlbarTestUtils.TestProvider {
};
}
- onEngagement(state, queryContext, details, _controller) {
+ onLegacyEngagement(state, queryContext, details, _controller) {
if (this._pickPromiseResolve) {
let { result, element } = details;
this._pickPromiseResolve([result, element]);
diff --git a/browser/components/urlbar/tests/browser/browser_engagement.js b/browser/components/urlbar/tests/browser/browser_engagement.js
index b1998b6f55..fbc321e322 100644
--- a/browser/components/urlbar/tests/browser/browser_engagement.js
+++ b/browser/components/urlbar/tests/browser/browser_engagement.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests the UrlbarProvider.onEngagement() method.
+// Tests the UrlbarProvider.onLegacyEngagement() method.
"use strict";
@@ -110,32 +110,21 @@ async function doTest({
let provider = new TestProvider();
UrlbarProvidersManager.registerProvider(provider);
- let startPromise = provider.promiseEngagement();
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
value: "test",
fireInputEvent: true,
});
- let [state, queryContext, details, controller] = await startPromise;
- Assert.equal(
- controller.input.isPrivate,
- expectedIsPrivate,
- "Start isPrivate"
- );
- Assert.equal(state, "start", "Start state");
-
- // `queryContext` isn't always defined for `start`, and `onEngagement`
- // shouldn't rely on it being defined on start, but there's no good reason to
- // assert that it's not defined here.
-
- // Similarly, `details` is never defined for `start`, but there's no good
- // reason to assert that it's not defined.
-
let endPromise = provider.promiseEngagement();
let { result, element } = (await endEngagement()) ?? {};
- [state, queryContext, details, controller] = await endPromise;
+ let [state, queryContext, details, controller] = await endPromise;
+
+ Assert.ok(
+ ["engagement", "abandonment"].includes(state),
+ "State should be either 'engagement' or 'abandonment'"
+ );
Assert.equal(controller.input.isPrivate, expectedIsPrivate, "End isPrivate");
Assert.equal(state, expectedEndState, "End state");
Assert.ok(queryContext, "End queryContext");
@@ -179,7 +168,7 @@ async function doTest({
}
/**
- * Test provider that resolves promises when onEngagement is called.
+ * Test provider that resolves promises when onLegacyEngagement is called.
*/
class TestProvider extends UrlbarTestUtils.TestProvider {
_resolves = [];
@@ -197,7 +186,7 @@ class TestProvider extends UrlbarTestUtils.TestProvider {
});
}
- onEngagement(...args) {
+ onLegacyEngagement(...args) {
let resolve = this._resolves.shift();
if (resolve) {
resolve(args);
diff --git a/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js b/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js
new file mode 100644
index 0000000000..2ad6ee0e07
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js
@@ -0,0 +1,288 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests less common mouse/keyboard manipulations of the address bar input
+ * field selection, for example:
+ * - Home/Del
+ * - Shift+Right/Left
+ * - Drag selection
+ * - Double-click on word
+ *
+ * All the tests set up some initial conditions, and check it. Then optionally
+ * they can manipulate the selection further, and check the results again.
+ * We want to ensure the final selection is the expected one, even if in the
+ * future we change our trimming strategy for the input field value.
+ */
+
+const tests = [
+ {
+ description: "Test HOME starting from full selection",
+ openPanel() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ },
+ get selection() {
+ return [0, gURLBar.value.length];
+ },
+ manipulate() {
+ // Cursor must move to the first visible character, regardless of any
+ // "untrimming" we could be doing.
+ this._visibleValue = gURLBar.value;
+ if (AppConstants.platform == "macosx") {
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { metaKey: true });
+ } else {
+ EventUtils.synthesizeKey("KEY_Home");
+ }
+ },
+ get modifiedSelection() {
+ let start = gURLBar.value.indexOf(this._visibleValue);
+ return [start, start];
+ },
+ },
+ {
+ description: "Test END starting from full selection",
+ openPanel() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ },
+ get selection() {
+ return [0, gURLBar.value.length];
+ },
+ manipulate() {
+ if (AppConstants.platform == "macosx") {
+ EventUtils.synthesizeKey("KEY_ArrowRight", { metaKey: true });
+ } else {
+ EventUtils.synthesizeKey("KEY_End", {});
+ }
+ },
+ get modifiedSelection() {
+ return [gURLBar.value.length, gURLBar.value.length];
+ },
+ },
+ {
+ description: "Test SHIFT+LEFT starting from full selection",
+ openPanel() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ },
+ get selection() {
+ return [0, gURLBar.value.length];
+ },
+ manipulate() {
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ },
+ get modifiedSelection() {
+ return [0, gURLBar.value.length - 1];
+ },
+ },
+ {
+ description: "Test SHIFT+RIGHT starting from full selection",
+ openPanel() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ },
+ get selection() {
+ return [0, gURLBar.value.length];
+ },
+ manipulate() {
+ EventUtils.synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ },
+ get modifiedSelection() {
+ return [0, gURLBar.value.length];
+ },
+ },
+ {
+ description: "Test Drag Selection from the first character",
+ async openPanel() {
+ this._expectedSelectedText = gURLBar.value.substring(0, 5);
+ await selectWithMouseDrag(
+ getTextWidth(gURLBar.value[0]) / 2 - 1,
+ getTextWidth(gURLBar.value.substring(0, 5))
+ );
+ },
+ get selection() {
+ return [
+ 0,
+ gURLBar.value.indexOf(this._expectedSelectedText) +
+ this._expectedSelectedText.length,
+ ];
+ },
+ },
+ {
+ description: "Test Drag Selection from the last character",
+ async openPanel() {
+ this._expectedSelectedText = gURLBar.value.substring(-5);
+ await selectWithMouseDrag(
+ getTextWidth(gURLBar.value) + 1,
+ getTextWidth(this._expectedSelectedText)
+ );
+ },
+ get selection() {
+ return [
+ gURLBar.value.indexOf(this._expectedSelectedText),
+ gURLBar.value.length,
+ ];
+ },
+ },
+ {
+ description: "Test Drag Selection in the middle of the string",
+ async openPanel() {
+ this._expectedSelectedText = gURLBar.value.substring(5, 10);
+ await selectWithMouseDrag(
+ getTextWidth(gURLBar.value.substring(0, 5)),
+ getTextWidth(gURLBar.value.substring(0, 10))
+ );
+ },
+ get selection() {
+ let start = gURLBar.value.indexOf(this._expectedSelectedText);
+ return [start, start + this._expectedSelectedText.length];
+ },
+ },
+ {
+ description: "Test Double-click on word",
+ async openPanel() {
+ let wordBoundaryIndex = gURLBar.value.search(/\btest/);
+ this._expectedSelectedText = "test";
+ await selectWithDoubleClick(
+ getTextWidth(gURLBar.value.substring(0, wordBoundaryIndex))
+ );
+ },
+ get selection() {
+ let start = gURLBar.value.indexOf(this._expectedSelectedText);
+ return [start, start + this._expectedSelectedText.length];
+ },
+ },
+ {
+ description: "Click at the right of the text",
+ openPanel() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ },
+ get selection() {
+ return [0, gURLBar.value.length];
+ },
+ manipulate() {
+ let rect = gURLBar.inputField.getBoundingClientRect();
+ EventUtils.synthesizeMouse(
+ gURLBar.inputField,
+ getTextWidth(gURLBar.value) + 10,
+ rect.height / 2,
+ {}
+ );
+ },
+ get modifiedSelection() {
+ return [gURLBar.value.length, gURLBar.value.length];
+ },
+ },
+];
+
+add_setup(async function () {
+ gURLBar.inputField.style.font = "14px monospace";
+ registerCleanupFunction(() => {
+ gURLBar.inputField.style.font = null;
+ });
+});
+
+add_task(async function https() {
+ await doTest("https://example.com/test/some/page.htm");
+});
+
+add_task(async function http() {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await doTest("http://example.com/test/other/page.htm");
+});
+
+async function doTest(url) {
+ await BrowserTestUtils.withNewTab(url, async () => {
+ for (let test of tests) {
+ gURLBar.blur();
+ info(test.description);
+ await UrlbarTestUtils.promisePopupOpen(window, async () => {
+ await test.openPanel();
+ });
+ info(
+ `Selected text is <${gURLBar.value.substring(
+ gURLBar.selectionStart,
+ gURLBar.selectionEnd
+ )}>`
+ );
+ Assert.deepEqual(
+ test.selection,
+ [gURLBar.selectionStart, gURLBar.selectionEnd],
+ "Check selection"
+ );
+
+ if (test.manipulate) {
+ await test.manipulate();
+ info(
+ `Selected text is <${gURLBar.value.substring(
+ gURLBar.selectionStart,
+ gURLBar.selectionEnd
+ )}>`
+ );
+ Assert.deepEqual(
+ test.modifiedSelection,
+ [gURLBar.selectionStart, gURLBar.selectionEnd],
+ "Check selection after manipulation"
+ );
+ }
+ }
+ });
+}
+
+function getTextWidth(inputText) {
+ const canvas =
+ getTextWidth.canvas ||
+ (getTextWidth.canvas = document.createElement("canvas"));
+ let context = canvas.getContext("2d");
+ context.font = window
+ .getComputedStyle(gURLBar.inputField)
+ .getPropertyValue("font");
+ return context.measureText(inputText).width;
+}
+
+function selectWithMouseDrag(fromX, toX) {
+ let target = gURLBar.inputField;
+ let rect = target.getBoundingClientRect();
+ let promise = BrowserTestUtils.waitForEvent(target, "mouseup");
+ EventUtils.synthesizeMouse(
+ target,
+ fromX,
+ rect.height / 2,
+ { type: "mousemove" },
+ target.ownerGlobal
+ );
+ EventUtils.synthesizeMouse(
+ target,
+ fromX,
+ rect.height / 2,
+ { type: "mousedown" },
+ target.ownerGlobal
+ );
+ EventUtils.synthesizeMouse(
+ target,
+ toX,
+ rect.height / 2,
+ { type: "mousemove" },
+ target.ownerGlobal
+ );
+ EventUtils.synthesizeMouse(
+ target,
+ toX,
+ rect.height / 2,
+ { type: "mouseup" },
+ target.ownerGlobal
+ );
+ return promise;
+}
+
+function selectWithDoubleClick(offsetX) {
+ let target = gURLBar.inputField;
+ let rect = target.getBoundingClientRect();
+ let promise = BrowserTestUtils.waitForEvent(target, "dblclick");
+ EventUtils.synthesizeMouse(target, offsetX, rect.height / 2, {
+ clickCount: 1,
+ });
+ EventUtils.synthesizeMouse(target, offsetX, rect.height / 2, {
+ clickCount: 2,
+ });
+ return promise;
+}
diff --git a/browser/components/urlbar/tests/browser/browser_locationBarCommand.js b/browser/components/urlbar/tests/browser/browser_locationBarCommand.js
index 84c45e586a..92409f979f 100644
--- a/browser/components/urlbar/tests/browser/browser_locationBarCommand.js
+++ b/browser/components/urlbar/tests/browser/browser_locationBarCommand.js
@@ -276,7 +276,7 @@ async function typeAndCommand(eventType, details = {}) {
async function triggerCommand(eventType, details = {}) {
Assert.equal(
await UrlbarTestUtils.promiseUserContextId(window),
- gBrowser.selectedTab.getAttribute("usercontextid"),
+ gBrowser.selectedTab.getAttribute("usercontextid") || "",
"userContextId must be the same as the originating tab"
);
diff --git a/browser/components/urlbar/tests/browser/browser_primary_selection_safe_on_new_tab.js b/browser/components/urlbar/tests/browser/browser_primary_selection_safe_on_new_tab.js
index 2f8e871bfe..66a8ed3a41 100644
--- a/browser/components/urlbar/tests/browser/browser_primary_selection_safe_on_new_tab.js
+++ b/browser/components/urlbar/tests/browser/browser_primary_selection_safe_on_new_tab.js
@@ -42,7 +42,7 @@ add_task(async function () {
// tab button.
let userInput = window.windowUtils.setHandlingUserInput(true);
try {
- BrowserOpenTab();
+ BrowserCommands.openTab();
} finally {
userInput.destruct();
}
diff --git a/browser/components/urlbar/tests/browser/browser_raceWithTabs.js b/browser/components/urlbar/tests/browser/browser_raceWithTabs.js
index 17560ea101..821aa0f0ee 100644
--- a/browser/components/urlbar/tests/browser/browser_raceWithTabs.js
+++ b/browser/components/urlbar/tests/browser/browser_raceWithTabs.js
@@ -41,7 +41,7 @@ add_task(async function hitEnterLoadInRightTab() {
gBrowser.tabContainer,
"TabOpen"
);
- BrowserOpenTab();
+ BrowserCommands.openTab();
let oldTab = (await oldTabOpenPromise).target;
let oldTabLoadedPromise = BrowserTestUtils.browserLoaded(
oldTab.linkedBrowser,
@@ -60,7 +60,7 @@ add_task(async function hitEnterLoadInRightTab() {
EventUtils.sendKey("return");
info("Immediately open a second tab");
- BrowserOpenTab();
+ BrowserCommands.openTab();
let newTab = (await tabOpenPromise).target;
info("Created new tab; waiting for tabs to load");
diff --git a/browser/components/urlbar/tests/browser/browser_result_menu.js b/browser/components/urlbar/tests/browser/browser_result_menu.js
index ccbe247598..f00b92fa63 100644
--- a/browser/components/urlbar/tests/browser/browser_result_menu.js
+++ b/browser/components/urlbar/tests/browser/browser_result_menu.js
@@ -201,10 +201,10 @@ add_task(async function firefoxSuggest() {
],
});
- // Implement the provider's `onEngagement()` so it removes the result.
- let onEngagementCallCount = 0;
- provider.onEngagement = (state, queryContext, details, controller) => {
- onEngagementCallCount++;
+ // Implement the provider's `onLegacyEngagement()` so it removes the result.
+ let onLegacyEngagementCallCount = 0;
+ provider.onLegacyEngagement = (state, queryContext, details, controller) => {
+ onLegacyEngagementCallCount++;
controller.removeResult(details.result);
};
@@ -245,9 +245,9 @@ add_task(async function firefoxSuggest() {
});
Assert.greater(
- onEngagementCallCount,
+ onLegacyEngagementCallCount,
0,
- "onEngagement() should have been called"
+ "onLegacyEngagement() should have been called"
);
Assert.equal(
UrlbarTestUtils.getResultCount(window),
diff --git a/browser/components/urlbar/tests/browser/browser_stop_pending.js b/browser/components/urlbar/tests/browser/browser_stop_pending.js
index 50f5dfdeec..938a57dc28 100644
--- a/browser/components/urlbar/tests/browser/browser_stop_pending.js
+++ b/browser/components/urlbar/tests/browser/browser_stop_pending.js
@@ -125,7 +125,7 @@ add_task(async function () {
null,
true
);
- BrowserStop();
+ BrowserCommands.stop();
await browserStoppedPromise;
is(
@@ -207,7 +207,7 @@ add_task(async function () {
null,
true
);
- BrowserStop();
+ BrowserCommands.stop();
await browserStoppedPromise;
is(
diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
index 318b29ad19..b2591a0c14 100644
--- a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_tabtosearch.js
@@ -344,8 +344,8 @@ async function impressions_test(isOnboarding) {
5
);
- // See javadoc for UrlbarProviderTabToSearch.onEngagement for discussion
- // about retained results.
+ // See javadoc for UrlbarProviderTabToSearch.onLegacyEngagement for
+ // discussion about retained results.
info("Reopen the result set with retained results. Record impression.");
await UrlbarTestUtils.promisePopupOpen(window, () => {
EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser.toml b/browser/components/urlbar/tests/engagementTelemetry/browser/browser.toml
index cf6bc80318..a72f2d9b8d 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser.toml
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser.toml
@@ -26,8 +26,6 @@ prefs = ["browser.bookmarks.testing.skipDefaultBookmarksImport=true"]
["browser_glean_telemetry_abandonment_n_chars_n_words.js"]
-["browser_glean_telemetry_abandonment_type.js"]
-
["browser_glean_telemetry_abandonment_sap.js"]
["browser_glean_telemetry_abandonment_search_engine_default_id.js"]
@@ -36,6 +34,8 @@ prefs = ["browser.bookmarks.testing.skipDefaultBookmarksImport=true"]
["browser_glean_telemetry_abandonment_tips.js"]
+["browser_glean_telemetry_abandonment_type.js"]
+
["browser_glean_telemetry_engagement_edge_cases.js"]
["browser_glean_telemetry_engagement_groups.js"]
@@ -66,4 +66,8 @@ skip-if = ["verify"] # Bug 1852375 - MerinoTestUtils.initWeather() doesn't play
["browser_glean_telemetry_exposure_edge_cases.js"]
+["browser_glean_telemetry_potential_exposure.js"]
+
["browser_glean_telemetry_record_preferences.js"]
+
+["browser_glean_telemetry_reenter.js"]
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_type.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_type.js
index 99145d7cc3..b8a16bd10c 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_type.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_type.js
@@ -19,7 +19,7 @@ function checkUrlbarFocus(win, focusState) {
// URL bar records the correct abandonment telemetry with abandonment type
// "tab_swtich".
add_task(async function tabSwitchFocusedToFocused() {
- await doTest(async browser => {
+ await doTest(async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test search",
@@ -45,7 +45,7 @@ add_task(async function tabSwitchFocusedToFocused() {
// URL bar loses focus logs abandonment telemetry with abandonment type
// "blur".
add_task(async function tabSwitchFocusedToUnfocused() {
- await doTest(async browser => {
+ await doTest(async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test search",
@@ -65,7 +65,7 @@ add_task(async function tabSwitchFocusedToUnfocused() {
// the URL bar gains focus does not record any abandonment telemetry, reflecting
// no change in focus state relevant to abandonment.
add_task(async function tabSwitchUnFocusedToFocused() {
- await doTest(async browser => {
+ await doTest(async () => {
checkUrlbarFocus(window, false);
let promiseTabOpened = BrowserTestUtils.waitForEvent(
@@ -91,7 +91,7 @@ add_task(async function tabSwitchUnFocusedToFocused() {
// Checks that switching between two tabs, both with unfocused URL bars, does
// not trigger any abandonment telmetry.
add_task(async function tabSwitchUnFocusedToUnFocused() {
- await doTest(async browser => {
+ await doTest(async () => {
checkUrlbarFocus(window, false);
let tab2 = await BrowserTestUtils.openNewForegroundTab(window.gBrowser);
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js
index ff31bdc52a..053d307088 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_tips.js
@@ -63,7 +63,7 @@ add_task(async function selected_result_tip() {
),
],
priority: 1,
- onEngagement: () => {
+ onLegacyEngagement: () => {
deferred.resolve();
},
});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js
index 6b1dedbce2..59c4460e52 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_type.js
@@ -101,11 +101,32 @@ add_task(async function engagement_type_dismiss() {
});
add_task(async function engagement_type_help() {
- const cleanupQuickSuggest = await ensureQuickSuggestInit();
+ const url = "https://example.com/";
+ const helpUrl = "https://example.com/help";
+ let provider = new UrlbarTestUtils.TestProvider({
+ priority: Infinity,
+ results: [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ url,
+ isBlockable: true,
+ blockL10n: { id: "urlbar-result-menu-dismiss-firefox-suggest" },
+ helpUrl,
+ helpL10n: {
+ id: "urlbar-result-menu-learn-more-about-firefox-suggest",
+ },
+ }
+ ),
+ ],
+ });
+ UrlbarProvidersManager.registerProvider(provider);
await doTest(async () => {
- await openPopup("sponsored");
- await selectRowByURL("https://example.com/sponsored");
+ await openPopup("test");
+ await selectRowByURL(url);
+
const onTabOpened = BrowserTestUtils.waitForNewTab(gBrowser);
UrlbarTestUtils.openResultMenuAndPressAccesskey(window, "L");
const tab = await onTabOpened;
@@ -114,5 +135,26 @@ add_task(async function engagement_type_help() {
assertEngagementTelemetry([{ engagement_type: "help" }]);
});
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+add_task(async function engagement_type_manage() {
+ const cleanupQuickSuggest = await ensureQuickSuggestInit();
+
+ await doTest(async () => {
+ await openPopup("sponsored");
+ await selectRowByURL("https://example.com/sponsored");
+
+ const onManagePageLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ "about:preferences#search"
+ );
+ UrlbarTestUtils.openResultMenuAndPressAccesskey(window, "M");
+ await onManagePageLoaded;
+
+ assertEngagementTelemetry([{ engagement_type: "manage" }]);
+ });
+
await cleanupQuickSuggest();
});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js
index 07e8b9b360..ef2ec623bc 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_exposure.js
@@ -11,7 +11,7 @@ add_setup(async function () {
await initExposureTest();
});
-add_task(async function exposureSponsoredOnEngagement() {
+add_task(async function exposureSponsoredOnLegacyEngagement() {
await doExposureTest({
prefs: [
["browser.urlbar.exposureResults", suggestResultType("adm_sponsored")],
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_potential_exposure.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_potential_exposure.js
new file mode 100644
index 0000000000..275e3968eb
--- /dev/null
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_potential_exposure.js
@@ -0,0 +1,438 @@
+/* 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/. */
+
+// Tests the `urlbar-potential-exposure` ping.
+
+const WAIT_FOR_PING_TIMEOUT_MS = 1000;
+
+// Avoid timeouts in verify mode, especially on Mac.
+requestLongerTimeout(3);
+
+add_setup(async function test_setup() {
+ Services.fog.testResetFOG();
+
+ // Add a mock engine so we don't hit the network.
+ await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
+
+ registerCleanupFunction(() => {
+ Services.fog.testResetFOG();
+ });
+});
+
+add_task(async function oneKeyword_noMatch_1() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam"],
+ expectedEvents: [],
+ });
+});
+
+add_task(async function oneKeyword_noMatch_2() {
+ await doTest({
+ keywords: ["exam"],
+ searchStrings: ["example"],
+ expectedEvents: [],
+ });
+});
+
+add_task(async function oneKeyword_oneMatch_terminal_1() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_oneMatch_terminal_2() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam", "example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_oneMatch_nonterminal_1() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example", "exam"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function oneKeyword_oneMatch_nonterminal_2() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["ex", "example", "exam"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_terminal_1() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example", "example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_terminal_2() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example", "exampl", "example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_terminal_3() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam", "example", "example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_terminal_4() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam", "example", "exampl", "example"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_nonterminal_1() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example", "example", "exampl"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_nonterminal_2() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam", "example", "example", "exampl"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_nonterminal_3() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example", "exam", "example", "exampl"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function oneKeyword_dupeMatches_nonterminal_4() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["exam", "example", "exampl", "example", "exampl"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function manyKeywords_noMatch() {
+ await doTest({
+ keywords: ["foo", "bar", "baz"],
+ searchStrings: ["example"],
+ expectedEvents: [],
+ });
+});
+
+add_task(async function manyKeywords_oneMatch_terminal_1() {
+ await doTest({
+ keywords: ["foo", "bar", "baz"],
+ searchStrings: ["bar"],
+ expectedEvents: [{ extra: { keyword: "bar", terminal: true } }],
+ });
+});
+
+add_task(async function manyKeywords_oneMatch_terminal_2() {
+ await doTest({
+ keywords: ["foo", "bar", "baz"],
+ searchStrings: ["example", "bar"],
+ expectedEvents: [{ extra: { keyword: "bar", terminal: true } }],
+ });
+});
+
+add_task(async function manyKeywords_oneMatch_nonterminal_1() {
+ await doTest({
+ keywords: ["foo", "bar", "baz"],
+ searchStrings: ["bar", "example"],
+ expectedEvents: [{ extra: { keyword: "bar", terminal: false } }],
+ });
+});
+
+add_task(async function manyKeywords_oneMatch_nonterminal_2() {
+ await doTest({
+ keywords: ["foo", "bar", "baz"],
+ searchStrings: ["exam", "bar", "example"],
+ expectedEvents: [{ extra: { keyword: "bar", terminal: false } }],
+ });
+});
+
+add_task(async function manyKeywords_manyMatches_terminal_1() {
+ let keywords = ["foo", "bar", "baz"];
+ await doTest({
+ keywords,
+ searchStrings: keywords,
+ expectedEvents: keywords.map((keyword, i) => ({
+ extra: { keyword, terminal: i == keywords.length - 1 },
+ })),
+ });
+});
+
+add_task(async function manyKeywords_manyMatches_terminal_2() {
+ let keywords = ["foo", "bar", "baz"];
+ await doTest({
+ keywords,
+ searchStrings: ["exam", "foo", "exampl", "bar", "example", "baz"],
+ expectedEvents: keywords.map((keyword, i) => ({
+ extra: { keyword, terminal: i == keywords.length - 1 },
+ })),
+ });
+});
+
+add_task(async function manyKeywords_manyMatches_nonterminal_1() {
+ let keywords = ["foo", "bar", "baz"];
+ await doTest({
+ keywords,
+ searchStrings: ["foo", "bar", "baz", "example"],
+ expectedEvents: keywords.map(keyword => ({
+ extra: { keyword, terminal: false },
+ })),
+ });
+});
+
+add_task(async function manyKeywords_manyMatches_nonterminal_2() {
+ let keywords = ["foo", "bar", "baz"];
+ await doTest({
+ keywords,
+ searchStrings: ["exam", "foo", "exampl", "bar", "example", "baz", "exam"],
+ expectedEvents: keywords.map(keyword => ({
+ extra: { keyword, terminal: false },
+ })),
+ });
+});
+
+add_task(async function manyKeywords_dupeMatches_terminal() {
+ let keywords = ["foo", "bar", "baz"];
+ let searchStrings = [...keywords, ...keywords];
+ await doTest({
+ keywords,
+ searchStrings,
+ expectedEvents: keywords.map((keyword, i) => ({
+ extra: { keyword, terminal: i == keywords.length - 1 },
+ })),
+ });
+});
+
+add_task(async function manyKeywords_dupeMatches_nonterminal() {
+ let keywords = ["foo", "bar", "baz"];
+ let searchStrings = [...keywords, ...keywords, "example"];
+ await doTest({
+ keywords,
+ searchStrings,
+ expectedEvents: keywords.map(keyword => ({
+ extra: { keyword, terminal: false },
+ })),
+ });
+});
+
+add_task(async function searchStringNormalization_terminal() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: [" ExaMPLe "],
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+});
+
+add_task(async function searchStringNormalization_nonterminal() {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: [" ExaMPLe ", "foo"],
+ expectedEvents: [{ extra: { keyword: "example", terminal: false } }],
+ });
+});
+
+add_task(async function multiWordKeyword() {
+ await doTest({
+ keywords: ["this has multiple words"],
+ searchStrings: ["this has multiple words"],
+ expectedEvents: [
+ { extra: { keyword: "this has multiple words", terminal: true } },
+ ],
+ });
+});
+
+// Smoke test that ends a session with an engagement instead of an abandonment
+// as other tasks in this file do.
+add_task(async function engagement() {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await doTest({
+ keywords: ["example"],
+ searchStrings: ["example"],
+ endSession: () =>
+ // Hit the Enter key on the heuristic search result.
+ UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Enter")
+ ),
+ expectedEvents: [{ extra: { keyword: "example", terminal: true } }],
+ });
+ });
+});
+
+// Smoke test that uses Nimbus to set keywords instead of a pref as other tasks
+// in this file do.
+add_task(async function nimbus() {
+ let keywords = ["foo", "bar", "baz"];
+ await doTest({
+ useNimbus: true,
+ keywords,
+ searchStrings: keywords,
+ expectedEvents: keywords.map((keyword, i) => ({
+ extra: { keyword, terminal: i == keywords.length - 1 },
+ })),
+ });
+});
+
+// The ping should not be submitted for sessions in private windows.
+add_task(async function privateWindow() {
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ await doTest({
+ win: privateWin,
+ keywords: ["example"],
+ searchStrings: ["example"],
+ expectedEvents: [],
+ });
+ await BrowserTestUtils.closeWindow(privateWin);
+});
+
+add_task(async function invalidPotentialExposureKeywords_pref() {
+ await doTest({
+ keywords: "not an array of keywords",
+ searchStrings: ["example", "not an array of keywords"],
+ expectedEvents: [],
+ });
+});
+
+add_task(async function invalidPotentialExposureKeywords_nimbus() {
+ await doTest({
+ useNimbus: true,
+ keywords: "not an array of keywords",
+ searchStrings: ["example", "not an array of keywords"],
+ expectedEvents: [],
+ });
+});
+
+async function doTest({
+ keywords,
+ searchStrings,
+ expectedEvents,
+ endSession = null,
+ useNimbus = false,
+ win = window,
+}) {
+ endSession ||= () =>
+ UrlbarTestUtils.promisePopupClose(win, () => win.gURLBar.blur());
+
+ let nimbusCleanup;
+ let keywordsJson = JSON.stringify(keywords);
+ if (useNimbus) {
+ nimbusCleanup = await UrlbarTestUtils.initNimbusFeature({
+ potentialExposureKeywords: keywordsJson,
+ });
+ } else {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.potentialExposureKeywords", keywordsJson]],
+ });
+ }
+
+ let pingPromise = waitForPing();
+
+ for (let value of searchStrings) {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ value,
+ window: win,
+ });
+ }
+ await endSession();
+
+ // Wait `WAIT_FOR_PING_TIMEOUT_MS` for the ping to be submitted before
+ // reporting a timeout. Note that some tasks do not expect a ping to be
+ // submitted, and they rely on this timeout behavior.
+ info("Awaiting ping promise");
+ let events = null;
+ events = await Promise.race([
+ pingPromise,
+ new Promise(resolve =>
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(() => {
+ if (!events) {
+ info("Timed out waiting for ping");
+ }
+ resolve([]);
+ }, WAIT_FOR_PING_TIMEOUT_MS)
+ ),
+ ]);
+
+ assertEvents(events, expectedEvents);
+
+ if (nimbusCleanup) {
+ await nimbusCleanup();
+ } else {
+ await SpecialPowers.popPrefEnv();
+ }
+ Services.fog.testResetFOG();
+}
+
+function waitForPing() {
+ return new Promise(resolve => {
+ GleanPings.urlbarPotentialExposure.testBeforeNextSubmit(() => {
+ let events = Glean.urlbar.potentialExposure.testGetValue();
+ info("testBeforeNextSubmit got events: " + JSON.stringify(events));
+ resolve(events);
+ });
+ });
+}
+
+function assertEvents(actual, expected) {
+ info("Comparing events: " + JSON.stringify({ actual, expected }));
+
+ // Add some expected boilerplate properties to the expected events so that
+ // callers don't have to but so that we still check them.
+ expected = expected.map(e => ({
+ category: "urlbar",
+ name: "potential_exposure",
+ // `testGetValue()` stringifies booleans for some reason. Let callers
+ // specify booleans since booleans are correct, and stringify them here.
+ ...stringifyBooleans(e),
+ }));
+
+ // Filter out properties from the actual events that aren't defined in the
+ // expected events. Ignore unimportant properties like timestamps.
+ actual = actual.map((a, i) =>
+ Object.fromEntries(
+ Object.entries(a).filter(([key]) => expected[i]?.hasOwnProperty(key))
+ )
+ );
+
+ Assert.deepEqual(actual, expected, "Checking expected Glean events");
+}
+
+function stringifyBooleans(obj) {
+ let newObj = {};
+ for (let [key, value] of Object.entries(obj)) {
+ if (value && typeof value == "object") {
+ newObj[key] = stringifyBooleans(value);
+ } else if (typeof value == "boolean") {
+ newObj[key] = String(value);
+ } else {
+ newObj[key] = value;
+ }
+ }
+ return newObj;
+}
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_reenter.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_reenter.js
new file mode 100644
index 0000000000..51bdc84870
--- /dev/null
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_reenter.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test we don't re-enter record() (and record both an engagement and an
+// abandonment) when handling an engagement blurs the input field.
+
+const TEST_URL = "https://example.com/";
+
+add_task(async function () {
+ await setup();
+ let deferred = Promise.withResolvers();
+ const provider = new UrlbarTestUtils.TestProvider({
+ results: [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ url: TEST_URL,
+ helpUrl: "https://example.com/help",
+ helpL10n: {
+ id: "urlbar-result-menu-tip-get-help",
+ },
+ }
+ ),
+ ],
+ priority: 999,
+ onLegacyEngagement: () => {
+ info("Blur the address bar during the onLegacyEngagement notification");
+ gURLBar.blur();
+ // Run at the next tick to be sure spurious events would have happened.
+ TestUtils.waitForTick().then(() => {
+ deferred.resolve();
+ });
+ },
+ });
+ UrlbarProvidersManager.registerProvider(provider);
+ // This should cover at least engagement and abandonment.
+ let engagementSpy = sinon.spy(provider, "onLegacyEngagement");
+
+ let beforeRecordCall = false,
+ recordReentered = false;
+ let recordStub = sinon
+ .stub(gURLBar.controller.engagementEvent, "record")
+ .callsFake((...args) => {
+ recordReentered = beforeRecordCall;
+ beforeRecordCall = true;
+ recordStub.wrappedMethod.apply(gURLBar.controller.engagementEvent, args);
+ beforeRecordCall = false;
+ });
+
+ registerCleanupFunction(() => {
+ sinon.restore();
+ UrlbarProvidersManager.unregisterProvider(provider);
+ });
+
+ await doTest(async () => {
+ await openPopup("example");
+ await selectRowByURL(TEST_URL);
+ EventUtils.synthesizeKey("VK_RETURN");
+ await deferred.promise;
+
+ assertEngagementTelemetry([{ engagement_type: "enter" }]);
+ assertAbandonmentTelemetry([]);
+
+ Assert.ok(recordReentered, "`record()` was re-entered");
+ Assert.equal(
+ engagementSpy.callCount,
+ 1,
+ "`onLegacyEngagement` was invoked twice"
+ );
+ Assert.equal(
+ engagementSpy.args[0][0],
+ "engagement",
+ "`engagement` notified"
+ );
+ });
+});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
index 4317a50930..1373cc7e27 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
@@ -10,6 +10,7 @@ Services.scriptloader.loadSubScript(
ChromeUtils.defineESModuleGetters(this, {
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
});
const lazy = {};
@@ -210,12 +211,7 @@ async function doTest(testFn) {
await QuickSuggest.blockedSuggestions.clear();
await QuickSuggest.blockedSuggestions._test_readyPromise;
await updateTopSites(() => true);
-
- try {
- await BrowserTestUtils.withNewTab(gBrowser, testFn);
- } catch (e) {
- console.error(e);
- }
+ await BrowserTestUtils.withNewTab(gBrowser, testFn);
}
async function initGroupTest() {
diff --git a/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs b/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs
index 2ba9dce8be..1002b4e231 100644
--- a/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs
+++ b/browser/components/urlbar/tests/quicksuggest/QuickSuggestTestUtils.sys.mjs
@@ -490,6 +490,8 @@ class _QuickSuggestTestUtils {
* Whether the result is expected to be sponsored.
* @param {boolean} [options.isBestMatch]
* Whether the result is expected to be a best match.
+ * @param {boolean} [options.isManageable]
+ * Whether the result is expected to show Manage result menu item.
* @returns {result}
* The quick suggest result.
*/
@@ -500,6 +502,7 @@ class _QuickSuggestTestUtils {
index = -1,
isSponsored = true,
isBestMatch = false,
+ isManageable = true,
} = {}) {
this.Assert.ok(
url || originalUrl,
@@ -574,11 +577,19 @@ class _QuickSuggestTestUtils {
}
this.Assert.equal(
- result.payload.helpUrl,
- lazy.QuickSuggest.HELP_URL,
- "Result helpURL"
+ result.payload.isManageable,
+ isManageable,
+ "Result isManageable"
);
+ if (!isManageable) {
+ this.Assert.equal(
+ result.payload.helpUrl,
+ lazy.QuickSuggest.HELP_URL,
+ "Result helpURL"
+ );
+ }
+
this.Assert.ok(
row._buttons.get("menu"),
"The menu button should be present"
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
index 130afe8c53..98f6ba6117 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
@@ -164,3 +164,40 @@ add_tasks_with_rust(
await cleanUpNimbus();
}
);
+
+// Tests the "Manage" result menu for sponsored suggestion.
+add_tasks_with_rust(async function resultMenu_manage_sponsored() {
+ await BrowserTestUtils.withNewTab({ gBrowser }, async browser => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "fra",
+ });
+
+ const managePage = "about:preferences#search";
+ let onManagePageLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ managePage
+ );
+ // Click the command.
+ await UrlbarTestUtils.openResultMenuAndClickItem(window, "manage", {
+ resultIndex: 1,
+ });
+ await onManagePageLoaded;
+ Assert.equal(
+ browser.currentURI.spec,
+ managePage,
+ "The manage page is loaded"
+ );
+
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+});
+
+// Tests the "Manage" result menu for non-sponsored suggestion.
+add_tasks_with_rust(async function resultMenu_manage_nonSponsored() {
+ await doManageTest({
+ input: "nonspon",
+ index: 1,
+ });
+});
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_addons.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_addons.js
index b09345aa54..f34b479134 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_addons.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_addons.js
@@ -245,10 +245,15 @@ add_task(async function resultMenu_notInterested() {
});
// Tests the "Not relevant" result menu dismissal command.
-add_task(async function notRelevant() {
+add_task(async function resultMenu_notRelevant() {
await doDismissTest("not_relevant", false);
});
+// Tests the "Manage" result menu.
+add_task(async function resultMenu_manage() {
+ await doManageTest({ input: "only match the Merino suggestion", index: 1 });
+});
+
// Tests the row/group label.
add_task(async function rowLabel() {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_block.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_block.js
index c400cf72f6..3fa91e5a32 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_block.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_block.js
@@ -5,11 +5,6 @@
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
-});
-
const { TELEMETRY_SCALARS } = UrlbarProviderQuickSuggest;
const { TIMESTAMP_TEMPLATE } = QuickSuggest;
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_mdn.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_mdn.js
index b7da7533c4..33bd37703d 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_mdn.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_mdn.js
@@ -21,6 +21,9 @@ const REMOTE_SETTINGS_DATA = [
},
];
+// Avoid timeouts in verify mode. They're especially common on Mac.
+requestLongerTimeout(5);
+
add_setup(async function () {
await QuickSuggestTestUtils.ensureQuickSuggestInit({
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
@@ -28,35 +31,37 @@ add_setup(async function () {
});
add_tasks_with_rust(async function basic() {
- const suggestion = REMOTE_SETTINGS_DATA[0].attachment[0];
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: suggestion.keywords[0],
- });
- Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
-
- const { element, result } = await UrlbarTestUtils.getDetailsOfResultAt(
- window,
- 1
- );
- Assert.equal(
- result.providerName,
- UrlbarProviderQuickSuggest.name,
- "The result should be from the expected provider"
- );
- Assert.equal(
- result.payload.provider,
- UrlbarPrefs.get("quickSuggestRustEnabled") ? "Mdn" : "MDNSuggestions"
- );
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ const suggestion = REMOTE_SETTINGS_DATA[0].attachment[0];
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: suggestion.keywords[0],
+ });
+ Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
+
+ const { element, result } = await UrlbarTestUtils.getDetailsOfResultAt(
+ window,
+ 1
+ );
+ Assert.equal(
+ result.providerName,
+ UrlbarProviderQuickSuggest.name,
+ "The result should be from the expected provider"
+ );
+ Assert.equal(
+ result.payload.provider,
+ UrlbarPrefs.get("quickSuggestRustEnabled") ? "Mdn" : "MDNSuggestions"
+ );
- const onLoad = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- result.payload.url
- );
- EventUtils.synthesizeMouseAtCenter(element.row, {});
- await onLoad;
- Assert.ok(true, "Expected page is loaded");
+ const onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ result.payload.url
+ );
+ EventUtils.synthesizeMouseAtCenter(element.row, {});
+ await onLoad;
+ Assert.ok(true, "Expected page is loaded");
+ });
await PlacesUtils.history.clear();
});
@@ -111,7 +116,7 @@ add_tasks_with_rust(async function resultMenu_notInterested() {
});
// Tests the "Not relevant" result menu dismissal command.
-add_tasks_with_rust(async function notRelevant() {
+add_tasks_with_rust(async function resultMenu_notRelevant() {
await doDismissTest("not_relevant");
Assert.equal(UrlbarPrefs.get("suggest.mdn"), true);
@@ -123,6 +128,11 @@ add_tasks_with_rust(async function notRelevant() {
await QuickSuggest.blockedSuggestions.clear();
});
+// Tests the "Manage" result menu.
+add_tasks_with_rust(async function resultMenu_manage() {
+ await doManageTest({ input: "array", index: 1 });
+});
+
async function doDismissTest(command) {
const keyword = REMOTE_SETTINGS_DATA[0].attachment[0].keywords[0];
// Do a search.
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_pocket.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_pocket.js
index 0064b6a297..a40a35893b 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_pocket.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_pocket.js
@@ -4,12 +4,6 @@
"use strict";
// Browser tests for Pocket suggestions.
-//
-// TODO: Make this work with Rust enabled. Right now, running this test with
-// Rust hits the following error on ingest, which prevents ingest from finishing
-// successfully:
-//
-// 0:03.17 INFO Console message: [JavaScript Error: "1698289045697 urlbar ERROR QuickSuggest.SuggestBackendRust :: Ingest error: Error executing SQL: FOREIGN KEY constraint failed" {file: "resource://gre/modules/Log.sys.mjs" line: 722}]
// The expected index of the Pocket suggestion.
const EXPECTED_RESULT_INDEX = 1;
@@ -30,6 +24,8 @@ const REMOTE_SETTINGS_DATA = [
},
];
+requestLongerTimeout(5);
+
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
@@ -47,7 +43,7 @@ add_setup(async function () {
});
});
-add_task(async function basic() {
+add_tasks_with_rust(async function basic() {
await BrowserTestUtils.withNewTab("about:blank", async () => {
// Do a search.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
@@ -96,7 +92,7 @@ add_task(async function basic() {
});
// Tests the "Show less frequently" command.
-add_task(async function resultMenu_showLessFrequently() {
+add_tasks_with_rust(async function resultMenu_showLessFrequently() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.pocket.featureGate", true],
@@ -235,7 +231,7 @@ async function doShowLessFrequently({ input, expected, keepViewOpen = false }) {
}
// Tests the "Not interested" result menu dismissal command.
-add_task(async function resultMenu_notInterested() {
+add_tasks_with_rust(async function resultMenu_notInterested() {
await doDismissTest("not_interested");
// Re-enable suggestions and wait until PocketSuggestions syncs them from
@@ -245,7 +241,7 @@ add_task(async function resultMenu_notInterested() {
});
// Tests the "Not relevant" result menu dismissal command.
-add_task(async function notRelevant() {
+add_tasks_with_rust(async function notRelevant() {
await doDismissTest("not_relevant");
});
@@ -361,7 +357,7 @@ async function doDismissTest(command) {
}
// Tests row labels.
-add_task(async function rowLabel() {
+add_tasks_with_rust(async function rowLabel() {
const testCases = [
// high confidence keyword best match
{
@@ -389,7 +385,7 @@ add_task(async function rowLabel() {
});
// Tests visibility of "Show less frequently" menu.
-add_task(async function showLessFrequentlyMenuVisibility() {
+add_tasks_with_rust(async function showLessFrequentlyMenuVisibility() {
const testCases = [
// high confidence keyword best match
{
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js
index b7c2bdc25c..7197946171 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js
@@ -401,6 +401,11 @@ async function doDismiss({ menu, assert }) {
await UrlbarTestUtils.promisePopupClose(window);
}
+// Tests the "Manage" result menu.
+add_task(async function resultMenu_manage() {
+ await doManageTest({ input: "ramen", index: 1 });
+});
+
// Tests the row/group label.
add_task(async function rowLabel() {
let tests = [
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js
index 001c54458c..71c289e0ef 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js
@@ -7,11 +7,6 @@
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
-});
-
const { TELEMETRY_SCALARS } = UrlbarProviderQuickSuggest;
const MERINO_SUGGESTION = {
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_gleanEmptyStrings.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_gleanEmptyStrings.js
index 00cbe6c4e1..2c75b63a71 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_gleanEmptyStrings.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_gleanEmptyStrings.js
@@ -7,11 +7,6 @@
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
-});
-
const { TELEMETRY_SCALARS } = UrlbarProviderQuickSuggest;
const MERINO_RESULT = {
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_impressionEdgeCases.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_impressionEdgeCases.js
index 821c5cf470..eab48faaaf 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_impressionEdgeCases.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_impressionEdgeCases.js
@@ -8,8 +8,6 @@
"use strict";
ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
UrlbarView: "resource:///modules/UrlbarView.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
});
@@ -376,8 +374,11 @@ async function doEngagementWithoutAddingResultToView(
let getPriorityStub = sandbox.stub(UrlbarProviderQuickSuggest, "getPriority");
getPriorityStub.returns(Infinity);
- // Spy on `UrlbarProviderQuickSuggest.onEngagement()`.
- let onEngagementSpy = sandbox.spy(UrlbarProviderQuickSuggest, "onEngagement");
+ // Spy on `UrlbarProviderQuickSuggest.onLegacyEngagement()`.
+ let onLegacyEngagementSpy = sandbox.spy(
+ UrlbarProviderQuickSuggest,
+ "onLegacyEngagement"
+ );
let sandboxCleanup = () => {
getPriorityStub?.restore();
@@ -454,7 +455,7 @@ async function doEngagementWithoutAddingResultToView(
});
await loadPromise;
- let engagementCalls = onEngagementSpy.getCalls().filter(call => {
+ let engagementCalls = onLegacyEngagementSpy.getCalls().filter(call => {
let state = call.args[0];
return state == "engagement";
});
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_nonsponsored.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_nonsponsored.js
index 9a1aa06c02..f541801bae 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_nonsponsored.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_nonsponsored.js
@@ -7,11 +7,6 @@
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
-});
-
const { TELEMETRY_SCALARS } = UrlbarProviderQuickSuggest;
const REMOTE_SETTINGS_RESULT = {
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_sponsored.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_sponsored.js
index 7c477e8af7..b11a491c92 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_sponsored.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_sponsored.js
@@ -7,11 +7,6 @@
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.sys.mjs",
-});
-
const { TELEMETRY_SCALARS } = UrlbarProviderQuickSuggest;
const REMOTE_SETTINGS_RESULT = {
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/head.js b/browser/components/urlbar/tests/quicksuggest/browser/head.js
index cc5f449e94..a1bf0feabe 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/head.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/head.js
@@ -12,7 +12,7 @@ Services.scriptloader.loadSubScript(
ChromeUtils.defineESModuleGetters(this, {
CONTEXTUAL_SERVICES_PING_TYPES:
- "resource:///modules/PartnerLinkAttribution.jsm",
+ "resource:///modules/PartnerLinkAttribution.sys.mjs",
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
UrlbarProviderQuickSuggest:
@@ -522,6 +522,45 @@ async function doCommandTest({
info("Finished command test: " + JSON.stringify({ commandOrArray }));
}
+/*
+ * Do test the "Manage" result menu item.
+ *
+ * @param {object} options
+ * Options
+ * @param {number} options.index
+ * The index of the suggestion that will be checked in the results list.
+ * @param {number} options.input
+ * The input value on the urlbar.
+ */
+async function doManageTest({ index, input }) {
+ await BrowserTestUtils.withNewTab({ gBrowser }, async browser => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: input,
+ });
+
+ const managePage = "about:preferences#search";
+ let onManagePageLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ managePage
+ );
+ // Click the command.
+ await UrlbarTestUtils.openResultMenuAndClickItem(window, "manage", {
+ resultIndex: index,
+ });
+ await onManagePageLoaded;
+
+ Assert.equal(
+ browser.currentURI.spec,
+ managePage,
+ "The manage page is loaded"
+ );
+
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+}
+
/**
* Gets a row in the view, which is assumed to be open, and asserts that it's a
* particular quick suggest row. If it is, the row is returned. If it's not,
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/head.js b/browser/components/urlbar/tests/quicksuggest/unit/head.js
index 73bedf468e..5808e06bdf 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/head.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/head.js
@@ -182,14 +182,11 @@ function makeWikipediaResult({
qsSuggestion: keyword,
sponsoredAdvertiser: "Wikipedia",
sponsoredIabCategory: "5 - Education",
- helpUrl: QuickSuggest.HELP_URL,
- helpL10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
- },
isBlockable: true,
blockL10n: {
id: "urlbar-result-menu-dismiss-firefox-suggest",
},
+ isManageable: true,
telemetryType: "adm_nonsponsored",
},
};
@@ -256,14 +253,11 @@ function makeAmpResult({
sponsoredBlockId: blockId,
sponsoredAdvertiser: advertiser,
sponsoredIabCategory: iabCategory,
- helpUrl: QuickSuggest.HELP_URL,
- helpL10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
- },
isBlockable: true,
blockL10n: {
id: "urlbar-result-menu-dismiss-firefox-suggest",
},
+ isManageable: true,
telemetryType: "adm_sponsored",
descriptionL10n: { id: "urlbar-result-action-sponsored" },
},
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_impressionCaps.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_impressionCaps.js
index 1c00cb5320..ecb7c3dd09 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_impressionCaps.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_impressionCaps.js
@@ -3884,7 +3884,7 @@ async function checkSearch({ name, searchString, expectedResults }) {
removeResult() {},
},
});
- UrlbarProviderQuickSuggest.onEngagement(
+ UrlbarProviderQuickSuggest.onLegacyEngagement(
"engagement",
context,
{
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merinoSessions.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merinoSessions.js
index 61b1b9186f..c98fc5b6b4 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merinoSessions.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merinoSessions.js
@@ -149,7 +149,7 @@ add_task(async function canceledQueries() {
});
function endEngagement({ controller, context = null, state = "engagement" }) {
- UrlbarProviderQuickSuggest.onEngagement(
+ UrlbarProviderQuickSuggest.onLegacyEngagement(
state,
context ||
createContext("endEngagement", {
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js b/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js
index cd794f435b..8479b97210 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_weather.js
@@ -723,7 +723,7 @@ add_tasks_with_rust(async function block() {
let result = context.results[0];
let provider = UrlbarProvidersManager.getProvider(result.providerName);
Assert.ok(provider, "Sanity check: Result provider found");
- provider.onEngagement(
+ provider.onLegacyEngagement(
"engagement",
context,
{
diff --git a/browser/components/urlbar/tests/unit/test_exposure.js b/browser/components/urlbar/tests/unit/test_exposure.js
index e3ce0b8479..3e63e668d7 100644
--- a/browser/components/urlbar/tests/unit/test_exposure.js
+++ b/browser/components/urlbar/tests/unit/test_exposure.js
@@ -3,7 +3,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
ChromeUtils.defineESModuleGetters(this, {
- QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
UrlbarProviderQuickSuggest:
"resource:///modules/UrlbarProviderQuickSuggest.sys.mjs",
});
@@ -177,14 +176,11 @@ function makeAmpResult({
sponsoredBlockId: blockId,
sponsoredAdvertiser: advertiser,
sponsoredIabCategory: iabCategory,
- helpUrl: QuickSuggest.HELP_URL,
- helpL10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
- },
isBlockable: true,
blockL10n: {
id: "urlbar-result-menu-dismiss-firefox-suggest",
},
+ isManageable: true,
telemetryType: "adm_sponsored",
descriptionL10n: { id: "urlbar-result-action-sponsored" },
},
@@ -240,14 +236,11 @@ function makeWikipediaResult({
qsSuggestion: keyword,
sponsoredAdvertiser: "Wikipedia",
sponsoredIabCategory: "5 - Education",
- helpUrl: QuickSuggest.HELP_URL,
- helpL10n: {
- id: "urlbar-result-menu-learn-more-about-firefox-suggest",
- },
isBlockable: true,
blockL10n: {
id: "urlbar-result-menu-dismiss-firefox-suggest",
},
+ isManageable: true,
telemetryType: "adm_nonsponsored",
},
};
diff --git a/browser/components/urlbar/tests/unit/test_l10nCache.js b/browser/components/urlbar/tests/unit/test_l10nCache.js
index e92c75fa01..bd93cc50d6 100644
--- a/browser/components/urlbar/tests/unit/test_l10nCache.js
+++ b/browser/components/urlbar/tests/unit/test_l10nCache.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests L10nCache in UrlbarUtils.jsm.
+// Tests L10nCache in UrlbarUtils.sys.mjs.
"use strict";