summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/urlbar/ActionsProvider.sys.mjs110
-rw-r--r--browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs121
-rw-r--r--browser/components/urlbar/ActionsProviderQuickActions.sys.mjs152
-rw-r--r--browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs73
-rw-r--r--browser/components/urlbar/UrlbarController.sys.mjs7
-rw-r--r--browser/components/urlbar/UrlbarInput.sys.mjs170
-rw-r--r--browser/components/urlbar/UrlbarPrefs.sys.mjs15
-rw-r--r--browser/components/urlbar/UrlbarProviderAutofill.sys.mjs70
-rw-r--r--browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs280
-rw-r--r--browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs20
-rw-r--r--browser/components/urlbar/UrlbarProviderPlaces.sys.mjs22
-rw-r--r--browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs357
-rw-r--r--browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs9
-rw-r--r--browser/components/urlbar/UrlbarProviderWeather.sys.mjs6
-rw-r--r--browser/components/urlbar/UrlbarProvidersManager.sys.mjs48
-rw-r--r--browser/components/urlbar/UrlbarTokenizer.sys.mjs17
-rw-r--r--browser/components/urlbar/UrlbarUtils.sys.mjs49
-rw-r--r--browser/components/urlbar/UrlbarValueFormatter.sys.mjs6
-rw-r--r--browser/components/urlbar/UrlbarView.sys.mjs56
-rw-r--r--browser/components/urlbar/docs/firefox-suggest-telemetry.rst8
-rw-r--r--browser/components/urlbar/metrics.yaml4
-rw-r--r--browser/components/urlbar/moz.build5
-rw-r--r--browser/components/urlbar/private/SuggestBackendRust.sys.mjs5
-rw-r--r--browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs16
-rw-r--r--browser/components/urlbar/tests/browser/browser.toml9
-rw-r--r--browser/components/urlbar/tests/browser/browser_UrlbarInput_untrimOnUserInteraction.js124
-rw-r--r--browser/components/urlbar/tests/browser/browser_autocomplete_edit_completed.js1
-rw-r--r--browser/components/urlbar/tests/browser/browser_contextualsearch.js73
-rw-r--r--browser/components/urlbar/tests/browser/browser_decode.js2
-rw-r--r--browser/components/urlbar/tests/browser/browser_keepStateAcrossTabSwitches.js195
-rw-r--r--browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js48
-rw-r--r--browser/components/urlbar/tests/browser/browser_oneOffs.js6
-rw-r--r--browser/components/urlbar/tests/browser/browser_quickactions.js530
-rw-r--r--browser/components/urlbar/tests/browser/browser_quickactions_commands.js154
-rw-r--r--browser/components/urlbar/tests/browser/browser_quickactions_devtools.js23
-rw-r--r--browser/components/urlbar/tests/browser/browser_quickactions_screenshot.js68
-rw-r--r--browser/components/urlbar/tests/browser/browser_quickactions_tab_refocus.js10
-rw-r--r--browser/components/urlbar/tests/browser/browser_secondaryActions.js141
-rw-r--r--browser/components/urlbar/tests/browser/browser_strip_on_share.js28
-rw-r--r--browser/components/urlbar/tests/browser/browser_urlbar_telemetry_quickactions.js133
-rw-r--r--browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js58
-rw-r--r--browser/components/urlbar/tests/browser/head.js71
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js24
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js7
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js5
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js26
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js2
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js12
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js75
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js3
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js13
-rw-r--r--browser/components/urlbar/tests/engagementTelemetry/browser/head.js4
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js84
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js18
-rw-r--r--browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_weather.js18
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_dynamicWikipedia.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merino.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js5
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_topPicks.js5
-rw-r--r--browser/components/urlbar/tests/unit/test_autofill_origins.js65
-rw-r--r--browser/components/urlbar/tests/unit/test_quickactions.js109
-rw-r--r--browser/components/urlbar/tests/unit/test_tokenizer.js6
62 files changed, 1730 insertions, 2061 deletions
diff --git a/browser/components/urlbar/ActionsProvider.sys.mjs b/browser/components/urlbar/ActionsProvider.sys.mjs
new file mode 100644
index 0000000000..9cd99969a2
--- /dev/null
+++ b/browser/components/urlbar/ActionsProvider.sys.mjs
@@ -0,0 +1,110 @@
+/* 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/. */
+
+/**
+ * A provider that matches the urlbar input to built in actions.
+ */
+export class ActionsProvider {
+ /**
+ * Unique name for the provider.
+ *
+ * @abstract
+ */
+ get name() {
+ return "ActionsProviderBase";
+ }
+
+ /**
+ * Whether this provider should be invoked for the given context.
+ * If this method returns false, the providers manager won't start a query
+ * with this provider, to save on resources.
+ *
+ * @param {UrlbarQueryContext} _queryContext The query context object.
+ * @returns {boolean} Whether this provider should be invoked for the search.
+ * @abstract
+ */
+ isActive(_queryContext) {
+ throw new Error("Not implemented.");
+ }
+
+ /**
+ * Query for actions based on the current users input.
+ *
+ * @param {UrlbarQueryContext} _queryContext The query context object.
+ * @param {UrlbarController} _controller The urlbar controller.
+ * @returns {ActionsResult}
+ * @abstract
+ */
+ async queryAction(_queryContext, _controller) {
+ throw new Error("Not implemented.");
+ }
+
+ /**
+ * Pick an action.
+ *
+ * @param {UrlbarQueryContext} _queryContext The query context object.
+ * @param {UrlbarController} _controller The urlbar controller.
+ * @param {DOMElement} _element The element that was selected.
+ * @abstract
+ */
+ pickAction(_queryContext, _controller, _element) {
+ throw new Error("Not implemented.");
+ }
+}
+
+/**
+ * Class used to create an Actions Result.
+ */
+export class ActionsResult {
+ providerName;
+
+ #key;
+ #l10nId;
+ #l10nArgs;
+ #icon;
+ #dataset;
+
+ /**
+ * @param {object} options
+ * An option object.
+ * @param { string } options.key
+ * A string key used to distinguish between different actions.
+ * @param { string } options.l10nId
+ * The id of the l10n string displayed in the action button.
+ * @param { string } options.l10nArgs
+ * Arguments passed to construct the above string
+ * @param { string } options.icon
+ * The icon displayed in the button.
+ * @param {object} options.dataset
+ * An object of properties we set on the action button that
+ * can be used to pass data when it is selected.
+ */
+ constructor({ key, l10nId, l10nArgs, icon, dataset }) {
+ this.#key = key;
+ this.#l10nId = l10nId;
+ this.#l10nArgs = l10nArgs;
+ this.#icon = icon;
+ this.#dataset = dataset;
+ }
+
+ get key() {
+ return this.#key;
+ }
+
+ get l10nId() {
+ return this.#l10nId;
+ }
+
+ get l10nArgs() {
+ return this.#l10nArgs;
+ }
+
+ get icon() {
+ return this.#icon;
+ }
+
+ get dataset() {
+ return this.#dataset;
+ }
+}
diff --git a/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs b/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
new file mode 100644
index 0000000000..58af7c94c5
--- /dev/null
+++ b/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
@@ -0,0 +1,121 @@
+/* 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/. */
+
+import { UrlbarUtils } from "resource:///modules/UrlbarUtils.sys.mjs";
+
+import {
+ ActionsProvider,
+ ActionsResult,
+} from "resource:///modules/ActionsProvider.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ OpenSearchEngine: "resource://gre/modules/OpenSearchEngine.sys.mjs",
+ loadAndParseOpenSearchEngine:
+ "resource://gre/modules/OpenSearchLoader.sys.mjs",
+ UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
+ UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
+});
+
+const ENABLED_PREF = "contextualSearch.enabled";
+
+/**
+ * A provider that returns an option for using the search engine provided
+ * by the active view if it utilizes OpenSearch.
+ */
+class ProviderContextualSearch extends ActionsProvider {
+ constructor() {
+ super();
+ this.engines = new Map();
+ }
+
+ get name() {
+ return "ActionsProviderContextualSearch";
+ }
+
+ isActive(queryContext) {
+ return (
+ queryContext.trimmedSearchString &&
+ lazy.UrlbarPrefs.get(ENABLED_PREF) &&
+ !queryContext.searchMode
+ );
+ }
+
+ async queryAction(queryContext, controller) {
+ let instance = this.queryInstance;
+ const hostname = URL.parse(queryContext.currentPage)?.hostname;
+
+ // This happens on about pages, which won't have associated engines
+ if (!hostname) {
+ return null;
+ }
+
+ let engine = await this.fetchEngine(controller);
+ let icon = engine?.icon || (await engine?.getIconURL?.());
+ let defaultEngine = lazy.UrlbarSearchUtils.getDefaultEngine();
+
+ if (
+ !engine ||
+ engine.name === defaultEngine?.name ||
+ instance != this.queryInstance
+ ) {
+ return null;
+ }
+
+ return new ActionsResult({
+ key: "contextual-search",
+ l10nId: "urlbar-result-search-with",
+ l10nArgs: { engine: engine.name || engine.title },
+ icon,
+ });
+ }
+
+ async fetchEngine(controller) {
+ let browser = controller.browserWindow.gBrowser.selectedBrowser;
+ let hostname = browser?.currentURI.host;
+
+ if (this.engines.has(hostname)) {
+ return this.engines.get(hostname);
+ }
+
+ // Strip www. to allow for partial matches when looking for an engine.
+ const [host] = UrlbarUtils.stripPrefixAndTrim(hostname, {
+ stripWww: true,
+ });
+ let engines = await lazy.UrlbarSearchUtils.enginesForDomainPrefix(host, {
+ matchAllDomainLevels: true,
+ });
+ return engines[0] ?? browser?.engines?.[0];
+ }
+
+ async pickAction(queryContext, controller, element) {
+ // If we have an engine to add, first create a new OpenSearchEngine, then
+ // get and open a url to execute a search for the term in the url bar.
+ let engine = await this.fetchEngine(controller);
+
+ if (engine.uri) {
+ let engineData = await lazy.loadAndParseOpenSearchEngine(
+ Services.io.newURI(engine.uri)
+ );
+ engine = new lazy.OpenSearchEngine({ engineData });
+ engine._setIcon(engine.icon, false);
+ }
+
+ const [url] = UrlbarUtils.getSearchQueryUrl(
+ engine,
+ queryContext.searchString
+ );
+ element.ownerGlobal.gBrowser.fixupAndLoadURIString(url, {
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ });
+ element.ownerGlobal.gBrowser.selectedBrowser.focus();
+ }
+
+ resetForTesting() {
+ this.engines = new Map();
+ }
+}
+
+export var ActionsProviderContextualSearch = new ProviderContextualSearch();
diff --git a/browser/components/urlbar/ActionsProviderQuickActions.sys.mjs b/browser/components/urlbar/ActionsProviderQuickActions.sys.mjs
new file mode 100644
index 0000000000..a693e7686a
--- /dev/null
+++ b/browser/components/urlbar/ActionsProviderQuickActions.sys.mjs
@@ -0,0 +1,152 @@
+/* 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/. */
+
+import {
+ ActionsProvider,
+ ActionsResult,
+} from "resource:///modules/ActionsProvider.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ QuickActionsLoaderDefault:
+ "resource:///modules/QuickActionsLoaderDefault.sys.mjs",
+ UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
+});
+
+// These prefs are relative to the `browser.urlbar` branch.
+const ENABLED_PREF = "quickactions.enabled";
+const MATCH_IN_PHRASE_PREF = "quickactions.matchInPhrase";
+const MIN_SEARCH_PREF = "quickactions.minimumSearchString";
+
+/**
+ * A provider that matches the urlbar input to built in actions.
+ */
+class ProviderQuickActions extends ActionsProvider {
+ get name() {
+ return "ActionsProviderQuickActions";
+ }
+
+ isActive(queryContext) {
+ return (
+ lazy.UrlbarPrefs.get(ENABLED_PREF) &&
+ !queryContext.searchMode &&
+ queryContext.trimmedSearchString.length < 50 &&
+ queryContext.trimmedSearchString.length >
+ lazy.UrlbarPrefs.get(MIN_SEARCH_PREF)
+ );
+ }
+
+ async queryAction(queryContext) {
+ await lazy.QuickActionsLoaderDefault.ensureLoaded();
+ let input = queryContext.trimmedLowerCaseSearchString;
+ let results = [...(this.#prefixes.get(input) ?? [])];
+
+ if (lazy.UrlbarPrefs.get(MATCH_IN_PHRASE_PREF)) {
+ for (let [keyword, key] of this.#keywords) {
+ if (input.includes(keyword)) {
+ results.push(key);
+ }
+ }
+ }
+
+ // Remove invisible actions.
+ results = results.filter(key => {
+ const action = this.#actions.get(key);
+ return action.isVisible?.() ?? true;
+ });
+
+ if (!results.length) {
+ return null;
+ }
+
+ let action = this.#actions.get(results[0]);
+ return new ActionsResult({
+ key: results[0],
+ l10nId: action.label,
+ icon: action.icon,
+ dataset: {
+ action: results[0],
+ inputLength: queryContext.trimmedSearchString.length,
+ },
+ });
+ }
+
+ pickAction(_queryContext, _controller, element) {
+ let action = element.dataset.action;
+ let inputLength = Math.min(element.dataset.inputLength, 10);
+ Services.telemetry.keyedScalarAdd(
+ `quickaction.picked`,
+ `${action}-${inputLength}`,
+ 1
+ );
+ let options = this.#actions.get(action).onPick();
+ if (options?.focusContent) {
+ element.ownerGlobal.gBrowser.selectedBrowser.focus();
+ }
+ }
+
+ /**
+ * Adds a new QuickAction.
+ *
+ * @param {string} key A key to identify this action.
+ * @param {string} definition An object that describes the action.
+ */
+ addAction(key, definition) {
+ this.#actions.set(key, definition);
+ definition.commands.forEach(cmd => this.#keywords.set(cmd, key));
+ this.#loopOverPrefixes(definition.commands, prefix => {
+ let result = this.#prefixes.get(prefix);
+ if (result) {
+ if (!result.includes(key)) {
+ result.push(key);
+ }
+ } else {
+ result = [key];
+ }
+ this.#prefixes.set(prefix, result);
+ });
+ }
+
+ /**
+ * Removes an action.
+ *
+ * @param {string} key A key to identify this action.
+ */
+ removeAction(key) {
+ let definition = this.#actions.get(key);
+ this.#actions.delete(key);
+ definition.commands.forEach(cmd => this.#keywords.delete(cmd));
+ this.#loopOverPrefixes(definition.commands, prefix => {
+ let result = this.#prefixes.get(prefix);
+ if (result) {
+ result = result.filter(val => val != key);
+ }
+ this.#prefixes.set(prefix, result);
+ });
+ }
+
+ // A map from keywords to an action.
+ #keywords = new Map();
+
+ // A map of all prefixes to an array of actions.
+ #prefixes = new Map();
+
+ // The actions that have been added.
+ #actions = new Map();
+
+ #loopOverPrefixes(commands, fun) {
+ for (const command of commands) {
+ // Loop over all the prefixes of the word, ie
+ // "", "w", "wo", "wor", stopping just before the full
+ // word itself which will be matched by the whole
+ // phrase matching.
+ for (let i = 1; i <= command.length; i++) {
+ let prefix = command.substring(0, command.length - i);
+ fun(prefix);
+ }
+ }
+ }
+}
+
+export var ActionsProviderQuickActions = new ProviderQuickActions();
diff --git a/browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs b/browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs
index 0ab9c4c83e..9a7b7a6a34 100644
--- a/browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs
+++ b/browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs
@@ -8,12 +8,10 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
- ClientEnvironment: "resource://normandy/lib/ClientEnvironment.sys.mjs",
DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.sys.mjs",
ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs",
- UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
+ ActionsProviderQuickActions:
+ "resource:///modules/ActionsProviderQuickActions.sys.mjs",
});
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
@@ -26,7 +24,6 @@ if (AppConstants.MOZ_UPDATER) {
"nsIApplicationUpdateService"
);
}
-
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"SCREENSHOT_BROWSER_COMPONENT",
@@ -54,7 +51,7 @@ let openUrl = url => {
let openAddonsUrl = url => {
return () => {
let window = lazy.BrowserWindowTracker.getTopWindow();
- window.BrowserOpenAddonsMgr(url, { selectTabByViewId: true });
+ window.BrowserAddonUI.openAddonsMgr(url, { selectTabByViewId: true });
};
};
@@ -97,24 +94,22 @@ const DEFAULT_ACTIONS = {
},
},
downloads: {
- l10nCommands: ["quickactions-cmd-downloads", "quickactions-downloads2"],
+ l10nCommands: ["quickactions-cmd-downloads"],
icon: "chrome://browser/skin/downloads/downloads.svg",
label: "quickactions-downloads2",
onPick: openUrlFun("about:downloads"),
},
extensions: {
- l10nCommands: ["quickactions-cmd-extensions", "quickactions-extensions"],
+ l10nCommands: ["quickactions-cmd-extensions"],
icon: "chrome://mozapps/skin/extensions/category-extensions.svg",
label: "quickactions-extensions",
onPick: openAddonsUrl("addons://list/extension"),
},
inspect: {
- l10nCommands: ["quickactions-cmd-inspector", "quickactions-inspector2"],
+ l10nCommands: ["quickactions-cmd-inspector"],
icon: "chrome://devtools/skin/images/open-inspector.svg",
label: "quickactions-inspector2",
- isVisible: () =>
- lazy.DevToolsShim.isEnabled() || lazy.DevToolsShim.isDevToolsUser(),
- isActive: () => {
+ isVisible: () => {
// The inspect action is available if:
// 1. DevTools is enabled.
// 2. The user can be considered as a DevTools user.
@@ -132,18 +127,18 @@ const DEFAULT_ACTIONS = {
onPick: openInspector,
},
logins: {
- l10nCommands: ["quickactions-cmd-logins", "quickactions-logins2"],
+ l10nCommands: ["quickactions-cmd-logins"],
label: "quickactions-logins2",
onPick: openUrlFun("about:logins"),
},
plugins: {
- l10nCommands: ["quickactions-cmd-plugins", "quickactions-plugins"],
+ l10nCommands: ["quickactions-cmd-plugins"],
icon: "chrome://mozapps/skin/extensions/category-extensions.svg",
label: "quickactions-plugins",
onPick: openAddonsUrl("addons://list/plugin"),
},
print: {
- l10nCommands: ["quickactions-cmd-print", "quickactions-print2"],
+ l10nCommands: ["quickactions-cmd-print"],
label: "quickactions-print2",
icon: "chrome://global/skin/icons/print.svg",
onPick: () => {
@@ -153,7 +148,7 @@ const DEFAULT_ACTIONS = {
},
},
private: {
- l10nCommands: ["quickactions-cmd-private", "quickactions-private2"],
+ l10nCommands: ["quickactions-cmd-private"],
label: "quickactions-private2",
icon: "chrome://global/skin/icons/indicator-private-browsing.svg",
onPick: () => {
@@ -163,7 +158,7 @@ const DEFAULT_ACTIONS = {
},
},
refresh: {
- l10nCommands: ["quickactions-cmd-refresh", "quickactions-refresh"],
+ l10nCommands: ["quickactions-cmd-refresh"],
label: "quickactions-refresh",
onPick: () => {
lazy.ResetProfile.openConfirmationDialog(
@@ -172,7 +167,7 @@ const DEFAULT_ACTIONS = {
},
},
restart: {
- l10nCommands: ["quickactions-cmd-restart", "quickactions-restart"],
+ l10nCommands: ["quickactions-cmd-restart"],
label: "quickactions-restart",
onPick: restartBrowser,
},
@@ -197,10 +192,10 @@ const DEFAULT_ACTIONS = {
},
},
screenshot: {
- l10nCommands: ["quickactions-cmd-screenshot", "quickactions-screenshot3"],
+ l10nCommands: ["quickactions-cmd-screenshot"],
label: "quickactions-screenshot3",
icon: "chrome://browser/skin/screenshot.svg",
- isActive: () => {
+ isVisible: () => {
return !lazy.BrowserWindowTracker.getTopWindow().gScreenshots.shouldScreenshotsButtonBeDisabled();
},
onPick: () => {
@@ -221,21 +216,21 @@ const DEFAULT_ACTIONS = {
},
},
settings: {
- l10nCommands: ["quickactions-cmd-settings", "quickactions-settings2"],
+ l10nCommands: ["quickactions-cmd-settings"],
icon: "chrome://global/skin/icons/settings.svg",
label: "quickactions-settings2",
onPick: openUrlFun("about:preferences"),
},
themes: {
- l10nCommands: ["quickactions-cmd-themes", "quickactions-themes"],
+ l10nCommands: ["quickactions-cmd-themes"],
icon: "chrome://mozapps/skin/extensions/category-extensions.svg",
label: "quickactions-themes",
onPick: openAddonsUrl("addons://list/theme"),
},
update: {
- l10nCommands: ["quickactions-cmd-update", "quickactions-update"],
+ l10nCommands: ["quickactions-cmd-update"],
label: "quickactions-update",
- isActive: () => {
+ isVisible: () => {
if (!AppConstants.MOZ_UPDATER) {
return false;
}
@@ -246,10 +241,10 @@ const DEFAULT_ACTIONS = {
onPick: restartBrowser,
},
viewsource: {
- l10nCommands: ["quickactions-cmd-viewsource", "quickactions-viewsource2"],
+ l10nCommands: ["quickactions-cmd-viewsource"],
icon: "chrome://global/skin/icons/settings.svg",
label: "quickactions-viewsource2",
- isActive: () => currentBrowser()?.currentURI.scheme !== "view-source",
+ isVisible: () => currentBrowser()?.currentURI.scheme !== "view-source",
onPick: () => openUrl("view-source:" + currentBrowser().currentURI.spec),
},
};
@@ -287,18 +282,6 @@ function restartBrowser() {
}
}
-function random(seed) {
- let x = Math.sin(seed) * 10000;
- return x - Math.floor(x);
-}
-
-function shuffle(array, seed) {
- for (let i = array.length - 1; i > 0; i--) {
- const j = Math.floor(random(seed) * (i + 1));
- [array[i], array[j]] = [array[j], array[i]];
- }
-}
-
/**
* Loads the default QuickActions.
*/
@@ -308,18 +291,6 @@ export class QuickActionsLoaderDefault {
static async load() {
let keys = Object.keys(DEFAULT_ACTIONS);
- if (lazy.UrlbarPrefs.get("quickactions.randomOrderActions")) {
- // We insert the actions in a random order which means they will be returned
- // in a random but consistent order (the order of results for "view" and "views"
- // should be the same).
- // We use the Nimbus randomizationId as the seed as the order should not change
- // for the user between restarts, it should be random between users but a user should
- // see actions the same order.
- let seed = [...lazy.ClientEnvironment.randomizationId]
- .map(x => x.charCodeAt(0))
- .reduce((sum, a) => sum + a, 0);
- shuffle(keys, seed);
- }
for (const key of keys) {
let actionData = DEFAULT_ACTIONS[key];
let messages = await lazy.gFluentStrings.formatMessages(
@@ -328,7 +299,7 @@ export class QuickActionsLoaderDefault {
actionData.commands = messages
.map(({ value }) => value.split(",").map(x => x.trim().toLowerCase()))
.flat();
- lazy.UrlbarProviderQuickActions.addAction(key, actionData);
+ lazy.ActionsProviderQuickActions.addAction(key, actionData);
}
}
static async ensureLoaded() {
diff --git a/browser/components/urlbar/UrlbarController.sys.mjs b/browser/components/urlbar/UrlbarController.sys.mjs
index 7e4d0ff1c5..5172c14943 100644
--- a/browser/components/urlbar/UrlbarController.sys.mjs
+++ b/browser/components/urlbar/UrlbarController.sys.mjs
@@ -1001,7 +1001,6 @@ class TelemetryEvent {
searchWords,
searchSource,
searchMode,
- selectedElement,
selIndex,
selType,
}
@@ -1045,11 +1044,7 @@ class TelemetryEvent {
currentResults[selIndex],
selType
);
- const selected_result_subtype =
- lazy.UrlbarUtils.searchEngagementTelemetrySubtype(
- currentResults[selIndex],
- selectedElement
- );
+ const selected_result_subtype = "";
if (selected_result === "input_field" && !this._controller.view?.isOpen) {
numResults = 0;
diff --git a/browser/components/urlbar/UrlbarInput.sys.mjs b/browser/components/urlbar/UrlbarInput.sys.mjs
index a96e862cff..2ee463dda8 100644
--- a/browser/components/urlbar/UrlbarInput.sys.mjs
+++ b/browser/components/urlbar/UrlbarInput.sys.mjs
@@ -11,6 +11,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.sys.mjs",
BrowserUIUtils: "resource:///modules/BrowserUIUtils.sys.mjs",
+ BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
ExtensionSearchHandler:
"resource://gre/modules/ExtensionSearchHandler.sys.mjs",
ObjectUtils: "resource://gre/modules/ObjectUtils.sys.mjs",
@@ -24,6 +25,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
UrlbarQueryContext: "resource:///modules/UrlbarUtils.sys.mjs",
UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.sys.mjs",
+ UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.sys.mjs",
UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.sys.mjs",
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
@@ -330,8 +332,6 @@ export class UrlbarInput {
}
setSelectionRange(selectionStart, selectionEnd) {
- this.focus();
-
let beforeSelect = new CustomEvent("beforeselect", {
bubbles: true,
cancelable: true,
@@ -348,6 +348,32 @@ export class UrlbarInput {
this._suppressPrimaryAdjustment = false;
}
+ saveSelectionStateForBrowser(browser) {
+ let state = this.#browserStates.get(browser);
+ if (!state) {
+ state = {};
+ this.#browserStates.set(browser, state);
+ }
+ state.selection = {
+ start: this.selectionStart,
+ end: this.selectionEnd,
+ // When restoring a URI from an empty value, we don't want to untrim it.
+ shouldUntrim: this.value && !this._protocolIsTrimmed,
+ };
+ }
+
+ restoreSelectionStateForBrowser(browser) {
+ // Address bar must be focused to untrim and for selection to make sense.
+ this.focus();
+ let state = this.#browserStates.get(browser);
+ if (state?.selection) {
+ if (state.selection.shouldUntrim) {
+ this.#maybeUntrimUrl();
+ }
+ this.setSelectionRange(state.selection.start, state.selection.end);
+ }
+ }
+
/**
* Sets the URI to display in the location bar.
*
@@ -900,6 +926,15 @@ export class UrlbarInput {
if (!result) {
return;
}
+ if (element?.dataset.action && element?.dataset.action != "tabswitch") {
+ this.view.close();
+ let provider = lazy.UrlbarProvidersManager.getActionProvider(
+ element.dataset.providerName
+ );
+ let { queryContext } = this.controller._lastQueryContextWrapper || {};
+ provider.pickAction(queryContext, this.controller, element);
+ return;
+ }
this.pickResult(result, event, element);
}
@@ -1053,7 +1088,13 @@ export class UrlbarInput {
break;
}
case lazy.UrlbarUtils.RESULT_TYPE.TAB_SWITCH: {
- if (this.hasAttribute("action-override")) {
+ // Behaviour is reversed with SecondaryActions, default behaviour is to navigate
+ // and button is provided to switch to tab.
+ if (
+ this.hasAttribute("action-override") ||
+ (lazy.UrlbarPrefs.get("secondaryActions.featureGate") &&
+ element?.dataset.action !== "tabswitch")
+ ) {
where = "current";
break;
}
@@ -2268,22 +2309,32 @@ export class UrlbarInput {
* @param {string} val The new value to set.
* @param {object} [options] Options for setting.
* @param {boolean} [options.allowTrim] Whether the value can be trimmed.
+ * @param {string} [options.untrimmedValue] Override for this._untrimmedValue.
+ * @param {boolean} [options.valueIsTyped] Override for this.valueIsTypede.
+ *
*
* @returns {string} The set value.
*/
- _setValue(val, { allowTrim = false } = {}) {
+ _setValue(
+ val,
+ { allowTrim = false, untrimmedValue = null, valueIsTyped = false } = {}
+ ) {
// Don't expose internal about:reader URLs to the user.
let originalUrl = lazy.ReaderMode.getOriginalUrlObjectForDisplay(val);
if (originalUrl) {
val = originalUrl.displaySpec;
}
- this._untrimmedValue = val;
-
+ this._untrimmedValue = untrimmedValue ?? val;
+ this._protocolIsTrimmed = false;
if (allowTrim) {
+ let oldVal = val;
val = this._trimValue(val);
+ this._protocolIsTrimmed =
+ oldVal.startsWith(lazy.BrowserUIUtils.trimURLProtocol) &&
+ !val.startsWith(lazy.BrowserUIUtils.trimURLProtocol);
}
- this.valueIsTyped = false;
+ this.valueIsTyped = valueIsTyped;
this._resultForCurrentValue = null;
this.inputField.value = val;
this.formatValue();
@@ -2400,6 +2451,7 @@ export class UrlbarInput {
selectionEnd: autofillValue.length,
type: this._autofillPlaceholder.type,
adaptiveHistoryInput: this._autofillPlaceholder.adaptiveHistoryInput,
+ untrimmedValue: this._autofillPlaceholder.untrimmedValue,
});
}
@@ -2725,6 +2777,8 @@ export class UrlbarInput {
* @param {string} options.adaptiveHistoryInput
* If the autofill type is "adaptive", this is the matching `input` value
* from adaptive history.
+ * @param {string} options.untrimmedValue
+ * Untrimmed value including a protocol.
*/
_autofillValue({
value,
@@ -2732,10 +2786,11 @@ export class UrlbarInput {
selectionEnd,
type,
adaptiveHistoryInput,
+ untrimmedValue,
}) {
// The autofilled value may be a URL that includes a scheme at the
// beginning. Do not allow it to be trimmed.
- this._setValue(value);
+ this._setValue(value, { untrimmedValue });
this.inputField.setSelectionRange(selectionStart, selectionEnd);
this._autofillPlaceholder = {
value,
@@ -2743,6 +2798,7 @@ export class UrlbarInput {
adaptiveHistoryInput,
selectionStart,
selectionEnd,
+ untrimmedValue,
};
}
@@ -2993,7 +3049,7 @@ export class UrlbarInput {
// pressed, open in current tab to allow ctrl-enter to canonize URL.
where = "current";
} else {
- where = this.window.whereToOpenLink(event, false, false);
+ where = lazy.BrowserUtils.whereToOpenLink(event, false, false);
}
if (lazy.UrlbarPrefs.get("openintab")) {
if (where == "current") {
@@ -3059,7 +3115,12 @@ export class UrlbarInput {
// Error check occurs during isClipboardURIValid
uri = Services.io.newURI(copyString);
- strippedURI = lazy.QueryStringStripper.stripForCopyOrShare(uri);
+ try {
+ strippedURI = lazy.QueryStringStripper.stripForCopyOrShare(uri);
+ } catch (e) {
+ console.warn(`stripForCopyOrShare: ${e.message}`);
+ return uri;
+ }
if (strippedURI) {
return this.makeURIReadable(strippedURI);
@@ -3087,6 +3148,59 @@ export class UrlbarInput {
return true;
}
+ /**
+ * Restores the untrimmed value in the urlbar.
+ */
+ #maybeUntrimUrl() {
+ // Check if we can untrim the current value.
+ if (
+ !lazy.UrlbarPrefs.get("untrimOnUserInteraction.featureGate") ||
+ !this._protocolIsTrimmed ||
+ !this.focused ||
+ this.#allTextSelected
+ ) {
+ return;
+ }
+
+ let selectionStart = this.selectionStart;
+ let selectionEnd = this.selectionEnd;
+
+ // Correct the selection taking the trimmed protocol into account.
+ let offset = lazy.BrowserUIUtils.trimURLProtocol.length;
+
+ // In case of autofill, we may have to adjust its boundaries.
+ if (this._autofillPlaceholder) {
+ this._autofillPlaceholder.selectionStart += offset;
+ this._autofillPlaceholder.selectionEnd += offset;
+ }
+
+ if (selectionStart == selectionEnd) {
+ // When cursor is at the end of the string, untrimming may
+ // reintroduced a trailing slash and we want to move past it.
+ if (selectionEnd == this.value.length) {
+ offset += 1;
+ }
+ selectionStart = selectionEnd += offset;
+ } else {
+ // If there's a selection, we must calculate both the initial
+ // protocol and the eventual trailing slash.
+ if (selectionStart != 0) {
+ selectionStart += offset;
+ }
+ if (selectionEnd == this.value.length) {
+ offset += 1;
+ }
+ selectionEnd += offset;
+ }
+
+ this._setValue(this._untrimmedValue, {
+ allowTrim: false,
+ valueIsTyped: this.valueIsTyped,
+ });
+
+ this.setSelectionRange(selectionStart, selectionEnd);
+ }
+
// The strip-on-share feature will strip known tracking/decorational
// query params from the URI and copy the stripped version to the clipboard.
_initStripOnShare() {
@@ -3331,7 +3445,7 @@ export class UrlbarInput {
if (
!this._preventClickSelectsAll &&
this._compositionState != lazy.UrlbarUtils.COMPOSITION.COMPOSING &&
- this.document.activeElement == this.inputField &&
+ this.focused &&
this.inputField.selectionStart == this.inputField.selectionEnd
) {
this.select();
@@ -3381,14 +3495,17 @@ export class UrlbarInput {
// If we were autofilling, remove the autofilled portion, by restoring
// the value to the last typed one.
this.value = this.window.gBrowser.userTypedValue;
- } else if (this.value == this._focusUntrimmedValue) {
+ } else if (
+ this.value == this._untrimmedValue &&
+ !this.window.gBrowser.userTypedValue &&
+ !this.focused
+ ) {
// If the value was untrimmed by _on_focus and didn't change, trim it.
- this.value = this._focusUntrimmedValue;
+ this.value = this._untrimmedValue;
} else {
// We're not updating the value, so just format it.
this.formatValue();
}
- this._focusUntrimmedValue = null;
this._revertOnBlurValue = null;
this._resetSearchState();
@@ -3446,6 +3563,7 @@ export class UrlbarInput {
event.target.id == SEARCH_BUTTON_ID
) {
this._maybeSelectAll();
+ this.#maybeUntrimUrl();
}
if (event.target == this._searchModeIndicatorClose && event.button != 2) {
@@ -3487,7 +3605,7 @@ export class UrlbarInput {
// This is necessary when a protocol was typed, but the whole url has
// invalid parts, like the origin, then editing and confirming the trimmed
// value would execute a search instead of visiting the typed url.
- if (this.value != this._untrimmedValue) {
+ if (this._protocolIsTrimmed) {
let untrim = false;
let fixedURI = this._getURIFixupInfo(this.value)?.preferredURI;
if (fixedURI) {
@@ -3508,8 +3626,7 @@ export class UrlbarInput {
}
}
if (untrim) {
- this._focusUntrimmedValue = this._untrimmedValue;
- this._setValue(this._focusUntrimmedValue);
+ this._setValue(this._untrimmedValue);
}
}
@@ -3639,6 +3756,7 @@ export class UrlbarInput {
let value = this.value;
this.valueIsTyped = true;
this._untrimmedValue = value;
+ this._protocolIsTrimmed = false;
this._resultForCurrentValue = null;
this.window.gBrowser.userTypedValue = value;
@@ -3935,6 +4053,7 @@ export class UrlbarInput {
}
_on_keydown(event) {
+ this.#allTextSelectedOnKeyDown = this.#allTextSelected;
if (event.keyCode === KeyEvent.DOM_VK_RETURN) {
if (this._keyDownEnterDeferred) {
this._keyDownEnterDeferred.reject();
@@ -3962,6 +4081,9 @@ export class UrlbarInput {
}
async _on_keyup(event) {
+ if (this.#allTextSelectedOnKeyDown) {
+ this.#maybeUntrimUrl();
+ }
if (event.keyCode === KeyEvent.DOM_VK_CONTROL) {
this._isKeyDownWithCtrl = false;
}
@@ -4066,8 +4188,7 @@ export class UrlbarInput {
// Only customize the drag data if the entire value is selected and it's a
// loaded URI. Use default behavior otherwise.
if (
- this.selectionStart != 0 ||
- this.selectionEnd != this.inputField.textLength ||
+ !this.#allTextSelected ||
this.getAttribute("pageproxystate") != "valid"
) {
return;
@@ -4128,6 +4249,11 @@ export class UrlbarInput {
this._initStripOnShare();
}
+ #allTextSelectedOnKeyDown = false;
+ get #allTextSelected() {
+ return this.selectionStart == 0 && this.selectionEnd == this.value.length;
+ }
+
/**
* @param {string} value A untrimmed address bar input.
* @returns {boolean}
@@ -4148,6 +4274,12 @@ export class UrlbarInput {
.nonWebControlledBlankURI
);
}
+
+ /**
+ * Tracks a state object per browser.
+ * TODO: Merge _searchModesByBrowser into this.
+ */
+ #browserStates = new WeakMap();
}
/**
diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs
index cd8a6b0f4c..264d86a3f4 100644
--- a/browser/components/urlbar/UrlbarPrefs.sys.mjs
+++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs
@@ -69,7 +69,7 @@ const PREF_URLBAR_DEFAULTS = new Map([
// Whether to show a link for using the search functionality provided by the
// active view if the the view utilizes OpenSearch.
- ["contextualSearch.enabled", false],
+ ["contextualSearch.enabled", true],
// Whether using `ctrl` when hitting return/enter in the URL bar
// (or clicking 'go') should prefix 'www.' and suffix
@@ -178,7 +178,7 @@ const PREF_URLBAR_DEFAULTS = new Map([
// If disabled, QuickActions will not be included in either the default search
// mode or the QuickActions search mode.
- ["quickactions.enabled", false],
+ ["quickactions.enabled", true],
// Whether we will match QuickActions within a phrase and not only a prefix.
["quickactions.matchInPhrase", true],
@@ -188,9 +188,6 @@ const PREF_URLBAR_DEFAULTS = new Map([
// zero prefix state.
["quickactions.minimumSearchString", 3],
- // Show multiple actions in a random order.
- ["quickactions.randomOrderActions", false],
-
// Whether we show the Actions section in about:preferences.
["quickactions.showPrefs", false],
@@ -322,11 +319,13 @@ const PREF_URLBAR_DEFAULTS = new Map([
// homepage is opened.
["searchTips.test.ignoreShowLimits", false],
+ // Feature gate pref for secondary actions being shown in the urlbar.
+ ["secondaryActions.featureGate", false],
+
// Whether to show each local search shortcut button in the view.
["shortcuts.bookmarks", true],
["shortcuts.tabs", true],
["shortcuts.history", true],
- ["shortcuts.quickactions", false],
// Boolean to determine if the providers defined in `exposureResults`
// should be displayed in search results. This can be set by a
@@ -464,6 +463,10 @@ const PREF_URLBAR_DEFAULTS = new Map([
// The index where we show unit conversion results.
["unitConversion.suggestedIndex", 1],
+ // Untrim url, when urlbar is focused.
+ // Note: This pref will be removed once the feature is stable.
+ ["untrimOnUserInteraction.featureGate", false],
+
// Controls the empty search behavior in Search Mode:
// 0 - Show nothing
// 1 - Show search history
diff --git a/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs b/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
index 7470df0fea..2ed1ee4444 100644
--- a/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderAutofill.sys.mjs
@@ -68,7 +68,7 @@ const SQL_AUTOFILL_FRECENCY_THRESHOLD = `host_frecency >= (
)`;
function originQuery(where) {
- // `frecency`, `bookmarked` and `visited` are partitioned by the fixed host,
+ // `frecency`, `n_bookmarks` and `visited` are partitioned by the fixed host,
// without `www.`. `host_prefix` instead is partitioned by full host, because
// we assume a prefix may not work regardless of `www.`.
let selectVisited = where.includes("visited")
@@ -78,7 +78,7 @@ function originQuery(where) {
: "0";
let selectTitle;
let joinBookmarks;
- if (where.includes("bookmarked")) {
+ if (where.includes("n_bookmarks")) {
selectTitle = "ifnull(b.title, iif(h.frecency <> 0, h.title, NULL))";
joinBookmarks = "LEFT JOIN moz_bookmarks b ON b.fk = h.id";
} else {
@@ -87,7 +87,7 @@ function originQuery(where) {
}
return `/* do not warn (bug no): cannot use an index to sort */
${SQL_AUTOFILL_WITH},
- origins(id, prefix, host_prefix, host, fixed, host_frecency, frecency, bookmarked, visited) AS (
+ origins(id, prefix, host_prefix, host, fixed, host_frecency, frecency, n_bookmarks, visited) AS (
SELECT
id,
prefix,
@@ -96,11 +96,11 @@ function originQuery(where) {
),
host,
fixup_url(host),
- IFNULL(total(${ORIGIN_FRECENCY_FIELD}) OVER (PARTITION BY fixup_url(host)), 0.0),
+ total(${ORIGIN_FRECENCY_FIELD}) OVER (PARTITION BY fixup_url(host)),
${ORIGIN_FRECENCY_FIELD},
- MAX(EXISTS(
- SELECT 1 FROM moz_places WHERE origin_id = o.id AND foreign_count > 0
- )) OVER (PARTITION BY fixup_url(host)),
+ total(
+ (SELECT total(foreign_count) FROM moz_places WHERE origin_id = o.id)
+ ) OVER (PARTITION BY fixup_url(host)),
${selectVisited}
FROM moz_origins o
WHERE prefix NOT IN ('about:', 'place:')
@@ -112,7 +112,7 @@ function originQuery(where) {
ifnull(:prefix, host_prefix) || host || '/'
FROM origins
${where}
- ORDER BY frecency DESC, prefix = "https://" DESC, id DESC
+ ORDER BY frecency DESC, n_bookmarks DESC, prefix = "https://" DESC, id DESC
LIMIT 1
),
matched_place(host_fixed, url, id, title, frecency) AS (
@@ -157,11 +157,11 @@ function urlQuery(where1, where2, isBookmarkContained) {
joinBookmarks = "";
}
return `/* do not warn (bug no): cannot use an index to sort */
- WITH matched_url(url, title, frecency, bookmarked, visited, stripped_url, is_exact_match, id) AS (
+ WITH matched_url(url, title, frecency, n_bookmarks, visited, stripped_url, is_exact_match, id) AS (
SELECT url,
title,
frecency,
- foreign_count > 0 AS bookmarked,
+ foreign_count AS n_bookmarks,
visit_count > 0 AS visited,
strip_prefix_and_userinfo(url) AS stripped_url,
strip_prefix_and_userinfo(url) = strip_prefix_and_userinfo(:strippedURL) AS is_exact_match,
@@ -173,7 +173,7 @@ function urlQuery(where1, where2, isBookmarkContained) {
SELECT url,
title,
frecency,
- foreign_count > 0 AS bookmarked,
+ foreign_count AS n_bookmarks,
visit_count > 0 AS visited,
strip_prefix_and_userinfo(url) AS stripped_url,
strip_prefix_and_userinfo(url) = 'www.' || strip_prefix_and_userinfo(:strippedURL) AS is_exact_match,
@@ -196,12 +196,12 @@ function urlQuery(where1, where2, isBookmarkContained) {
// Queries
const QUERY_ORIGIN_HISTORY_BOOKMARK = originQuery(
- `WHERE bookmarked OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD}`
+ `WHERE n_bookmarks > 0 OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD}`
);
const QUERY_ORIGIN_PREFIX_HISTORY_BOOKMARK = originQuery(
`WHERE prefix BETWEEN :prefix AND :prefix || X'FFFF'
- AND (bookmarked OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD})`
+ AND (n_bookmarks > 0 OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD})`
);
const QUERY_ORIGIN_HISTORY = originQuery(
@@ -213,38 +213,38 @@ const QUERY_ORIGIN_PREFIX_HISTORY = originQuery(
AND visited AND ${SQL_AUTOFILL_FRECENCY_THRESHOLD}`
);
-const QUERY_ORIGIN_BOOKMARK = originQuery(`WHERE bookmarked`);
+const QUERY_ORIGIN_BOOKMARK = originQuery(`WHERE n_bookmarks > 0`);
const QUERY_ORIGIN_PREFIX_BOOKMARK = originQuery(
- `WHERE prefix BETWEEN :prefix AND :prefix || X'FFFF' AND bookmarked`
+ `WHERE prefix BETWEEN :prefix AND :prefix || X'FFFF' AND n_bookmarks > 0`
);
const QUERY_URL_HISTORY_BOOKMARK = urlQuery(
- `AND (bookmarked OR frecency > 20)
+ `AND (n_bookmarks > 0 OR frecency > 20)
AND stripped_url COLLATE NOCASE
BETWEEN :strippedURL AND :strippedURL || X'FFFF'`,
- `AND (bookmarked OR frecency > 20)
+ `AND (n_bookmarks > 0 OR frecency > 20)
AND stripped_url COLLATE NOCASE
BETWEEN 'www.' || :strippedURL AND 'www.' || :strippedURL || X'FFFF'`,
true
);
const QUERY_URL_PREFIX_HISTORY_BOOKMARK = urlQuery(
- `AND (bookmarked OR frecency > 20)
+ `AND (n_bookmarks > 0 OR frecency > 20)
AND url COLLATE NOCASE
BETWEEN :prefix || :strippedURL AND :prefix || :strippedURL || X'FFFF'`,
- `AND (bookmarked OR frecency > 20)
+ `AND (n_bookmarks > 0 OR frecency > 20)
AND url COLLATE NOCASE
BETWEEN :prefix || 'www.' || :strippedURL AND :prefix || 'www.' || :strippedURL || X'FFFF'`,
true
);
const QUERY_URL_HISTORY = urlQuery(
- `AND (visited OR NOT bookmarked)
+ `AND (visited OR n_bookmarks = 0)
AND frecency > 20
AND stripped_url COLLATE NOCASE
BETWEEN :strippedURL AND :strippedURL || X'FFFF'`,
- `AND (visited OR NOT bookmarked)
+ `AND (visited OR n_bookmarks = 0)
AND frecency > 20
AND stripped_url COLLATE NOCASE
BETWEEN 'www.' || :strippedURL AND 'www.' || :strippedURL || X'FFFF'`,
@@ -252,11 +252,11 @@ const QUERY_URL_HISTORY = urlQuery(
);
const QUERY_URL_PREFIX_HISTORY = urlQuery(
- `AND (visited OR NOT bookmarked)
+ `AND (visited OR n_bookmarks = 0)
AND frecency > 20
AND url COLLATE NOCASE
BETWEEN :prefix || :strippedURL AND :prefix || :strippedURL || X'FFFF'`,
- `AND (visited OR NOT bookmarked)
+ `AND (visited OR n_bookmarks = 0)
AND frecency > 20
AND url COLLATE NOCASE
BETWEEN :prefix || 'www.' || :strippedURL AND :prefix || 'www.' || :strippedURL || X'FFFF'`,
@@ -264,20 +264,20 @@ const QUERY_URL_PREFIX_HISTORY = urlQuery(
);
const QUERY_URL_BOOKMARK = urlQuery(
- `AND bookmarked
+ `AND n_bookmarks > 0
AND stripped_url COLLATE NOCASE
BETWEEN :strippedURL AND :strippedURL || X'FFFF'`,
- `AND bookmarked
+ `AND n_bookmarks > 0
AND stripped_url COLLATE NOCASE
BETWEEN 'www.' || :strippedURL AND 'www.' || :strippedURL || X'FFFF'`,
true
);
const QUERY_URL_PREFIX_BOOKMARK = urlQuery(
- `AND bookmarked
+ `AND n_bookmarks > 0
AND url COLLATE NOCASE
BETWEEN :prefix || :strippedURL AND :prefix || :strippedURL || X'FFFF'`,
- `AND bookmarked
+ `AND n_bookmarks > 0
AND url COLLATE NOCASE
BETWEEN :prefix || 'www.' || :strippedURL AND :prefix || 'www.' || :strippedURL || X'FFFF'`,
true
@@ -452,17 +452,19 @@ class ProviderAutofill extends UrlbarProvider {
sources.includes(UrlbarUtils.RESULT_SOURCE.HISTORY) &&
sources.includes(UrlbarUtils.RESULT_SOURCE.BOOKMARKS)
) {
- conditions.push(`(bookmarked OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD})`);
+ conditions.push(
+ `(n_bookmarks > 0 OR ${SQL_AUTOFILL_FRECENCY_THRESHOLD})`
+ );
} else if (sources.includes(UrlbarUtils.RESULT_SOURCE.HISTORY)) {
conditions.push(`visited AND ${SQL_AUTOFILL_FRECENCY_THRESHOLD}`);
} else if (sources.includes(UrlbarUtils.RESULT_SOURCE.BOOKMARKS)) {
- conditions.push("bookmarked");
+ conditions.push("n_bookmarks > 0");
}
let rows = await db.executeCached(
`
${SQL_AUTOFILL_WITH},
- origins(id, prefix, host_prefix, host, fixed, host_frecency, frecency, bookmarked, visited) AS (
+ origins(id, prefix, host_prefix, host, fixed, host_frecency, frecency, n_bookmarks, visited) AS (
SELECT
id,
prefix,
@@ -471,11 +473,11 @@ class ProviderAutofill extends UrlbarProvider {
),
host,
fixup_url(host),
- IFNULL(total(${ORIGIN_FRECENCY_FIELD}) OVER (PARTITION BY fixup_url(host)), 0.0),
+ total(${ORIGIN_FRECENCY_FIELD}) OVER (PARTITION BY fixup_url(host)),
${ORIGIN_FRECENCY_FIELD},
- MAX(EXISTS(
- SELECT 1 FROM moz_places WHERE origin_id = o.id AND foreign_count > 0
- )) OVER (PARTITION BY fixup_url(host)),
+ total(
+ (SELECT total(foreign_count) FROM moz_places WHERE origin_id = o.id)
+ ) OVER (PARTITION BY fixup_url(host)),
MAX(EXISTS(
SELECT 1 FROM moz_places WHERE origin_id = o.id AND visit_count > 0
)) OVER (PARTITION BY fixup_url(host))
diff --git a/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs b/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs
deleted file mode 100644
index 5714f11e72..0000000000
--- a/browser/components/urlbar/UrlbarProviderContextualSearch.sys.mjs
+++ /dev/null
@@ -1,280 +0,0 @@
-/* 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/. */
-
-import {
- UrlbarProvider,
- UrlbarUtils,
-} from "resource:///modules/UrlbarUtils.sys.mjs";
-
-const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
- BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
- OpenSearchEngine: "resource://gre/modules/OpenSearchEngine.sys.mjs",
- loadAndParseOpenSearchEngine:
- "resource://gre/modules/OpenSearchLoader.sys.mjs",
- UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
- UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
- UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
- UrlbarView: "resource:///modules/UrlbarView.sys.mjs",
-});
-
-const DYNAMIC_RESULT_TYPE = "contextualSearch";
-
-const ENABLED_PREF = "contextualSearch.enabled";
-
-const VIEW_TEMPLATE = {
- attributes: {
- selectable: true,
- },
- children: [
- {
- name: "no-wrap",
- tag: "span",
- classList: ["urlbarView-no-wrap", "urlbarView-overflowable"],
- children: [
- {
- name: "icon",
- tag: "img",
- classList: ["urlbarView-favicon"],
- },
- {
- name: "search",
- tag: "span",
- classList: ["urlbarView-title", "urlbarView-overflowable"],
- },
- {
- name: "separator",
- tag: "span",
- classList: ["urlbarView-title-separator"],
- },
- {
- name: "description",
- tag: "span",
- },
- ],
- },
- ],
-};
-
-/**
- * A provider that returns an option for using the search engine provided
- * by the active view if it utilizes OpenSearch.
- */
-class ProviderContextualSearch extends UrlbarProvider {
- constructor() {
- super();
- this.engines = new Map();
- lazy.UrlbarResult.addDynamicResultType(DYNAMIC_RESULT_TYPE);
- lazy.UrlbarView.addDynamicViewTemplate(DYNAMIC_RESULT_TYPE, VIEW_TEMPLATE);
- }
-
- /**
- * Unique name for the provider, used by the context to filter on providers.
- * Not using a unique name will cause the newest registration to win.
- *
- * @returns {string}
- */
- get name() {
- return "UrlbarProviderContextualSearch";
- }
-
- /**
- * The type of the provider.
- *
- * @returns {UrlbarUtils.PROVIDER_TYPE}
- */
- get type() {
- return UrlbarUtils.PROVIDER_TYPE.PROFILE;
- }
-
- /**
- * Whether this provider should be invoked for the given context.
- * If this method returns false, the providers manager won't start a query
- * with this provider, to save on resources.
- *
- * @param {UrlbarQueryContext} queryContext The query context object
- * @returns {boolean} Whether this provider should be invoked for the search.
- */
- isActive(queryContext) {
- return (
- queryContext.trimmedSearchString &&
- !queryContext.searchMode &&
- lazy.UrlbarPrefs.get(ENABLED_PREF)
- );
- }
-
- /**
- * Starts querying. Extended classes should return a Promise resolved when the
- * provider is done searching AND returning results.
- *
- * @param {UrlbarQueryContext} queryContext The query context object
- * @param {Function} addCallback Callback invoked by the provider to add a new
- * result. A UrlbarResult should be passed to it.
- */
- async startQuery(queryContext, addCallback) {
- let engine;
- const hostname =
- queryContext?.currentPage && new URL(queryContext.currentPage).hostname;
-
- // This happens on about pages, which won't have associated engines
- if (!hostname) {
- return;
- }
-
- // First check to see if there's a cached search engine for the host.
- // If not, check to see if an installed engine matches the current view.
- if (this.engines.has(hostname)) {
- engine = this.engines.get(hostname);
- } else {
- // Strip www. to allow for partial matches when looking for an engine.
- const [host] = UrlbarUtils.stripPrefixAndTrim(hostname, {
- stripWww: true,
- });
- engine = (
- await lazy.UrlbarSearchUtils.enginesForDomainPrefix(host, {
- matchAllDomainLevels: true,
- })
- )[0];
- }
-
- if (engine) {
- let instance = this.queryInstance;
- let icon = await engine.getIconURL();
- if (instance != this.queryInstance) {
- return;
- }
-
- this.engines.set(hostname, engine);
- // Check to see if the engine that was found is the default engine.
- // The default engine will often be used to populate the heuristic result,
- // and we want to avoid ending up with two nearly identical search results.
- let defaultEngine = lazy.UrlbarSearchUtils.getDefaultEngine();
- if (engine.name === defaultEngine?.name) {
- return;
- }
- const [url] = UrlbarUtils.getSearchQueryUrl(
- engine,
- queryContext.searchString
- );
- let result = this.makeResult({
- url,
- engine: engine.name,
- icon,
- input: queryContext.searchString,
- shouldNavigate: true,
- });
- addCallback(this, result);
- return;
- }
-
- // If the current view has engines that haven't been added, return a result
- // that will first add an engine, then use it to search.
- let window = lazy.BrowserWindowTracker.getTopWindow();
- let engineToAdd = window?.gBrowser.selectedBrowser?.engines?.[0];
-
- if (engineToAdd) {
- let result = this.makeResult({
- hostname,
- url: engineToAdd.uri,
- engine: engineToAdd.title,
- icon: engineToAdd.icon,
- input: queryContext.searchString,
- shouldAddEngine: true,
- });
- addCallback(this, result);
- }
- }
-
- makeResult({
- engine,
- icon,
- url,
- input,
- hostname,
- shouldNavigate = false,
- shouldAddEngine = false,
- }) {
- let result = new lazy.UrlbarResult(
- UrlbarUtils.RESULT_TYPE.DYNAMIC,
- UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
- {
- engine,
- icon,
- url,
- input,
- hostname,
- shouldAddEngine,
- shouldNavigate,
- dynamicType: DYNAMIC_RESULT_TYPE,
- }
- );
- result.suggestedIndex = -1;
- return result;
- }
-
- /**
- * This is called when the urlbar view updates the view of one of the results
- * of the provider. It should return an object describing the view update.
- * See the base UrlbarProvider class for more.
- *
- * @param {UrlbarResult} result The result whose view will be updated.
- * @returns {object} An object describing the view update.
- */
- getViewUpdate(result) {
- return {
- icon: {
- attributes: {
- src: result.payload.icon || UrlbarUtils.ICON.SEARCH_GLASS,
- },
- },
- search: {
- textContent: result.payload.input,
- attributes: {
- title: result.payload.input,
- },
- },
- description: {
- l10n: {
- id: "urlbar-result-action-search-w-engine",
- args: {
- engine: result.payload.engine,
- },
- },
- },
- };
- }
-
- onLegacyEngagement(state, queryContext, details, controller) {
- let { result } = details;
- if (result?.providerName == this.name) {
- this.#pickResult(result, controller.browserWindow);
- }
- }
-
- async #pickResult(result, window) {
- // If we have an engine to add, first create a new OpenSearchEngine, then
- // get and open a url to execute a search for the term in the url bar.
- // In cases where we don't have to create a new engine, navigation is
- // handled automatically by providing `shouldNavigate: true` in the result.
- if (result.payload.shouldAddEngine) {
- let engineData = await lazy.loadAndParseOpenSearchEngine(
- Services.io.newURI(result.payload.url)
- );
- let newEngine = new lazy.OpenSearchEngine({ engineData });
- newEngine._setIcon(result.payload.icon, false);
- this.engines.set(result.payload.hostname, newEngine);
- const [url] = UrlbarUtils.getSearchQueryUrl(
- newEngine,
- result.payload.input
- );
- window.gBrowser.fixupAndLoadURIString(url, {
- triggeringPrincipal:
- Services.scriptSecurityManager.getSystemPrincipal(),
- });
- }
- }
-}
-
-export var UrlbarProviderContextualSearch = new ProviderContextualSearch();
diff --git a/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs b/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
index 17b6a4c9b0..a259d639cc 100644
--- a/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs
@@ -144,15 +144,25 @@ class ProviderInputHistory extends UrlbarProvider {
// Don't suggest switching to the current page.
continue;
}
- let result = new lazy.UrlbarResult(
- UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
- UrlbarUtils.RESULT_SOURCE.TABS,
- ...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
+ let payload = lazy.UrlbarResult.payloadAndSimpleHighlights(
+ queryContext.tokens,
+ {
url: [url, UrlbarUtils.HIGHLIGHT.TYPED],
title: [resultTitle, UrlbarUtils.HIGHLIGHT.TYPED],
icon: UrlbarUtils.getIconForUrl(url),
userContextId: row.getResultByName("userContextId") || 0,
- })
+ }
+ );
+ if (lazy.UrlbarPrefs.get("secondaryActions.featureGate")) {
+ payload[0].action = {
+ key: "tabswitch",
+ l10nId: "urlbar-result-action-switch-tab",
+ };
+ }
+ let result = new lazy.UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
+ UrlbarUtils.RESULT_SOURCE.TABS,
+ ...payload
);
addCallback(this, result);
continue;
diff --git a/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs b/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
index c94ebee80a..0787d4c209 100644
--- a/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderPlaces.sys.mjs
@@ -330,17 +330,25 @@ function makeUrlbarResult(tokens, info) {
action.params.searchSuggestion.toLocaleLowerCase(),
})
);
- case "switchtab":
+ case "switchtab": {
+ let payload = lazy.UrlbarResult.payloadAndSimpleHighlights(tokens, {
+ url: [action.params.url, UrlbarUtils.HIGHLIGHT.TYPED],
+ title: [info.comment, UrlbarUtils.HIGHLIGHT.TYPED],
+ icon: info.icon,
+ userContextId: info.userContextId,
+ });
+ if (lazy.UrlbarPrefs.get("secondaryActions.featureGate")) {
+ payload[0].action = {
+ key: "tabswitch",
+ l10nId: "urlbar-result-action-switch-tab",
+ };
+ }
return new lazy.UrlbarResult(
UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
UrlbarUtils.RESULT_SOURCE.TABS,
- ...lazy.UrlbarResult.payloadAndSimpleHighlights(tokens, {
- url: [action.params.url, UrlbarUtils.HIGHLIGHT.TYPED],
- title: [info.comment, UrlbarUtils.HIGHLIGHT.TYPED],
- icon: info.icon,
- userContextId: info.userContextId,
- })
+ ...payload
);
+ }
case "visiturl":
return new lazy.UrlbarResult(
UrlbarUtils.RESULT_TYPE.URL,
diff --git a/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs b/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs
deleted file mode 100644
index 29370cbaaf..0000000000
--- a/browser/components/urlbar/UrlbarProviderQuickActions.sys.mjs
+++ /dev/null
@@ -1,357 +0,0 @@
-/* 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/. */
-
-import {
- UrlbarProvider,
- UrlbarUtils,
-} from "resource:///modules/UrlbarUtils.sys.mjs";
-
-const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- QuickActionsLoaderDefault:
- "resource:///modules/QuickActionsLoaderDefault.sys.mjs",
- UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
- UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
-});
-
-// These prefs are relative to the `browser.urlbar` branch.
-const ENABLED_PREF = "quickactions.enabled";
-const SUGGEST_PREF = "suggest.quickactions";
-const MATCH_IN_PHRASE_PREF = "quickactions.matchInPhrase";
-const MIN_SEARCH_PREF = "quickactions.minimumSearchString";
-const DYNAMIC_TYPE_NAME = "quickactions";
-
-// When the urlbar is first focused and no search term has been
-// entered we show a limited number of results.
-const ACTIONS_SHOWN_FOCUS = 4;
-
-// Default icon shown for actions if no custom one is provided.
-const DEFAULT_ICON = "chrome://global/skin/icons/settings.svg";
-
-// The suggestion index of the actions row within the urlbar results.
-const SUGGESTED_INDEX = 1;
-
-/**
- * A provider that returns a suggested url to the user based on what
- * they have currently typed so they can navigate directly.
- */
-class ProviderQuickActions extends UrlbarProvider {
- constructor() {
- super();
- lazy.UrlbarResult.addDynamicResultType(DYNAMIC_TYPE_NAME);
- }
-
- /**
- * Returns the name of this provider.
- *
- * @returns {string} the name of this provider.
- */
- get name() {
- return DYNAMIC_TYPE_NAME;
- }
-
- /**
- * The type of the provider.
- *
- * @returns {UrlbarUtils.PROVIDER_TYPE}
- */
- get type() {
- return UrlbarUtils.PROVIDER_TYPE.PROFILE;
- }
-
- getPriority(context) {
- if (!context.searchString) {
- return 1;
- }
- return 0;
- }
-
- /**
- * Whether this provider should be invoked for the given context.
- * If this method returns false, the providers manager won't start a query
- * with this provider, to save on resources.
- *
- * @param {UrlbarQueryContext} queryContext The query context object
- * @returns {boolean} Whether this provider should be invoked for the search.
- */
- isActive(queryContext) {
- return (
- queryContext.trimmedSearchString.length < 50 &&
- lazy.UrlbarPrefs.get(ENABLED_PREF) &&
- ((lazy.UrlbarPrefs.get(SUGGEST_PREF) && !queryContext.searchMode) ||
- queryContext.searchMode?.source == UrlbarUtils.RESULT_SOURCE.ACTIONS)
- );
- }
-
- /**
- * Starts querying. Extended classes should return a Promise resolved when the
- * provider is done searching AND returning results.
- *
- * @param {UrlbarQueryContext} queryContext The query context object
- * @param {Function} addCallback Callback invoked by the provider to add a new
- * result. A UrlbarResult should be passed to it.
- * @returns {Promise}
- */
- async startQuery(queryContext, addCallback) {
- await lazy.QuickActionsLoaderDefault.ensureLoaded();
- let input = queryContext.trimmedLowerCaseSearchString;
-
- if (
- !queryContext.searchMode &&
- input.length < lazy.UrlbarPrefs.get(MIN_SEARCH_PREF)
- ) {
- return;
- }
-
- let results = [...(this.#prefixes.get(input) ?? [])];
-
- if (lazy.UrlbarPrefs.get(MATCH_IN_PHRASE_PREF)) {
- for (let [keyword, key] of this.#keywords) {
- if (input.includes(keyword)) {
- results.push(key);
- }
- }
- }
- // Ensure results are unique.
- results = [...new Set(results)];
-
- // Remove invisible actions.
- results = results.filter(key => {
- const action = this.#actions.get(key);
- return !action.isVisible || action.isVisible();
- });
-
- if (!results?.length) {
- return;
- }
-
- // If all actions are inactive, don't show anything.
- if (
- results.every(key => {
- const action = this.#actions.get(key);
- return action.isActive && !action.isActive();
- })
- ) {
- return;
- }
-
- // If we are in the Actions searchMode then we want to show all the actions
- // but not when we are in the normal url mode on first focus.
- if (
- results.length > ACTIONS_SHOWN_FOCUS &&
- !input &&
- !queryContext.searchMode
- ) {
- results.length = ACTIONS_SHOWN_FOCUS;
- }
-
- const result = new lazy.UrlbarResult(
- UrlbarUtils.RESULT_TYPE.DYNAMIC,
- UrlbarUtils.RESULT_SOURCE.ACTIONS,
- {
- results: results.map(key => ({ key })),
- dynamicType: DYNAMIC_TYPE_NAME,
- inputLength: input.length,
- inQuickActionsSearchMode:
- queryContext.searchMode?.source == UrlbarUtils.RESULT_SOURCE.ACTIONS,
- }
- );
- result.suggestedIndex = SUGGESTED_INDEX;
- addCallback(this, result);
- this.#resultFromLastQuery = result;
- }
-
- getViewTemplate(result) {
- return {
- children: [
- {
- name: "buttons",
- tag: "div",
- attributes: {
- "data-is-quickactions-searchmode":
- result.payload.inQuickActionsSearchMode,
- },
- children: result.payload.results.map(({ key }, i) => {
- let action = this.#actions.get(key);
- let inActive = "isActive" in action && !action.isActive();
- return {
- name: `button-${i}`,
- tag: "span",
- attributes: {
- "data-key": key,
- "data-input-length": result.payload.inputLength,
- class: "urlbarView-quickaction-button",
- role: inActive ? "" : "button",
- disabled: inActive,
- },
- children: [
- {
- name: `icon-${i}`,
- tag: "div",
- attributes: { class: "urlbarView-favicon" },
- children: [
- {
- name: `image-${i}`,
- tag: "img",
- attributes: {
- class: "urlbarView-favicon-img",
- src: action.icon || DEFAULT_ICON,
- },
- },
- ],
- },
- {
- name: `label-${i}`,
- tag: "span",
- attributes: { class: "urlbarView-label" },
- },
- ],
- };
- }),
- },
- ],
- };
- }
-
- getViewUpdate(result) {
- let viewUpdate = {};
- result.payload.results.forEach(({ key }, i) => {
- let action = this.#actions.get(key);
- viewUpdate[`label-${i}`] = {
- l10n: { id: action.label, cacheable: true },
- };
- });
- return viewUpdate;
- }
-
- #pickResult(result, itemPicked) {
- let { key, inputLength } = itemPicked.dataset;
- // We clamp the input length to limit the number of keys to
- // the number of actions * 10.
- inputLength = Math.min(inputLength, 10);
- Services.telemetry.keyedScalarAdd(
- `quickaction.picked`,
- `${key}-${inputLength}`,
- 1
- );
- let options = this.#actions.get(itemPicked.dataset.key).onPick() ?? {};
- if (options.focusContent) {
- itemPicked.ownerGlobal.gBrowser.selectedBrowser.focus();
- }
- }
-
- 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;
- }
-
- if (state == "engagement" && queryContext) {
- // Get the result that's visible in the view. `details.result` is the
- // engaged result, if any; if it's from this provider, then that's the
- // visible result. Otherwise fall back to #getVisibleResultFromLastQuery.
- let { result } = details;
- if (result?.providerName != this.name) {
- result = this.#getVisibleResultFromLastQuery(controller.view);
- }
-
- result?.payload.results.forEach(({ key }) => {
- Services.telemetry.keyedScalarAdd(
- `quickaction.impression`,
- `${key}-${queryContext.trimmedSearchString.length}`,
- 1
- );
- });
- }
-
- // Handle picks.
- if (details.result?.providerName == this.name) {
- this.#pickResult(details.result, details.element);
- }
-
- this.#resultFromLastQuery = null;
- }
-
- /**
- * Adds a new QuickAction.
- *
- * @param {string} key A key to identify this action.
- * @param {string} definition An object that describes the action.
- */
- addAction(key, definition) {
- this.#actions.set(key, definition);
- definition.commands.forEach(cmd => this.#keywords.set(cmd, key));
- this.#loopOverPrefixes(definition.commands, prefix => {
- let result = this.#prefixes.get(prefix);
- if (result) {
- if (!result.includes(key)) {
- result.push(key);
- }
- } else {
- result = [key];
- }
- this.#prefixes.set(prefix, result);
- });
- }
-
- /**
- * Removes an action.
- *
- * @param {string} key A key to identify this action.
- */
- removeAction(key) {
- let definition = this.#actions.get(key);
- this.#actions.delete(key);
- definition.commands.forEach(cmd => this.#keywords.delete(cmd));
- this.#loopOverPrefixes(definition.commands, prefix => {
- let result = this.#prefixes.get(prefix);
- if (result) {
- result = result.filter(val => val != key);
- }
- this.#prefixes.set(prefix, result);
- });
- }
-
- // A map from keywords to an action.
- #keywords = new Map();
-
- // A map of all prefixes to an array of actions.
- #prefixes = new Map();
-
- // The actions that have been added.
- #actions = new Map();
-
- // The result we added during the most recent query.
- #resultFromLastQuery = null;
-
- #loopOverPrefixes(commands, fun) {
- for (const command of commands) {
- // Loop over all the prefixes of the word, ie
- // "", "w", "wo", "wor", stopping just before the full
- // word itself which will be matched by the whole
- // phrase matching.
- for (let i = 1; i <= command.length; i++) {
- let prefix = command.substring(0, command.length - i);
- fun(prefix);
- }
- }
- }
-
- #getVisibleResultFromLastQuery(view) {
- let result = this.#resultFromLastQuery;
-
- if (
- result?.rowIndex >= 0 &&
- view?.visibleResults?.[result.rowIndex] == result
- ) {
- // The result was visible.
- return result;
- }
-
- // Find a visible result.
- return view?.visibleResults?.find(r => r.providerName == this.name);
- }
-}
-
-export var UrlbarProviderQuickActions = new ProviderQuickActions();
diff --git a/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs b/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
index fbc8cc8c3f..10244e06b5 100644
--- a/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderQuickSuggest.sys.mjs
@@ -45,7 +45,6 @@ const TELEMETRY_SCALARS = {
CLICK_NAV_SUPERCEDED: `${TELEMETRY_PREFIX}.click_nav_superceded`,
CLICK_NONSPONSORED: `${TELEMETRY_PREFIX}.click_nonsponsored`,
CLICK_SPONSORED: `${TELEMETRY_PREFIX}.click_sponsored`,
- HELP_DYNAMIC_WIKIPEDIA: `${TELEMETRY_PREFIX}.help_dynamic_wikipedia`,
HELP_NONSPONSORED: `${TELEMETRY_PREFIX}.help_nonsponsored`,
HELP_SPONSORED: `${TELEMETRY_PREFIX}.help_sponsored`,
IMPRESSION_DYNAMIC_WIKIPEDIA: `${TELEMETRY_PREFIX}.impression_dynamic_wikipedia`,
@@ -413,14 +412,11 @@ class ProviderQuickSuggest extends UrlbarProvider {
let payload = {
url: suggestion.url,
isSponsored: suggestion.is_sponsored,
- 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,
};
if (suggestion.full_keyword) {
@@ -592,9 +588,6 @@ class ProviderQuickSuggest extends UrlbarProvider {
scalars.push(TELEMETRY_SCALARS.CLICK_DYNAMIC_WIKIPEDIA);
} else {
switch (resultSelType) {
- case "help":
- scalars.push(TELEMETRY_SCALARS.HELP_DYNAMIC_WIKIPEDIA);
- break;
case "dismiss":
scalars.push(TELEMETRY_SCALARS.BLOCK_DYNAMIC_WIKIPEDIA);
break;
diff --git a/browser/components/urlbar/UrlbarProviderWeather.sys.mjs b/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
index 8e9b6b8f3e..6dc38e7aed 100644
--- a/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
+++ b/browser/components/urlbar/UrlbarProviderWeather.sys.mjs
@@ -20,7 +20,6 @@ const TELEMETRY_PREFIX = "contextual.services.quicksuggest";
const TELEMETRY_SCALARS = {
BLOCK: `${TELEMETRY_PREFIX}.block_weather`,
CLICK: `${TELEMETRY_PREFIX}.click_weather`,
- HELP: `${TELEMETRY_PREFIX}.help_weather`,
IMPRESSION: `${TELEMETRY_PREFIX}.impression_weather`,
};
@@ -233,7 +232,6 @@ class ProviderWeather extends UrlbarProvider {
*
* - "": The user didn't pick the row or any part of it
* - "weather": The user picked the main part of the row
- * - "help": The user picked the help button
* - "dismiss": The user dismissed the result
*
* An empty string means the user picked some other row to end the
@@ -265,10 +263,6 @@ class ProviderWeather extends UrlbarProvider {
clickScalars.push(TELEMETRY_SCALARS.CLICK);
eventObject = "click";
break;
- case "help":
- clickScalars.push(TELEMETRY_SCALARS.HELP);
- eventObject = "help";
- break;
case "dismiss":
clickScalars.push(TELEMETRY_SCALARS.BLOCK);
eventObject = "block";
diff --git a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
index ac70e03e1b..3fd9b0caf3 100644
--- a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
+++ b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs
@@ -40,8 +40,6 @@ var localProviderModules = {
"resource:///modules/UrlbarProviderCalculator.sys.mjs",
UrlbarProviderClipboard:
"resource:///modules/UrlbarProviderClipboard.sys.mjs",
- UrlbarProviderContextualSearch:
- "resource:///modules/UrlbarProviderContextualSearch.sys.mjs",
UrlbarProviderHeuristicFallback:
"resource:///modules/UrlbarProviderHeuristicFallback.sys.mjs",
UrlbarProviderHistoryUrlHeuristic:
@@ -54,8 +52,6 @@ var localProviderModules = {
UrlbarProviderPlaces: "resource:///modules/UrlbarProviderPlaces.sys.mjs",
UrlbarProviderPrivateSearch:
"resource:///modules/UrlbarProviderPrivateSearch.sys.mjs",
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
UrlbarProviderQuickSuggest:
"resource:///modules/UrlbarProviderQuickSuggest.sys.mjs",
UrlbarProviderQuickSuggestContextualOptIn:
@@ -84,6 +80,14 @@ var localMuxerModules = {
"resource:///modules/UrlbarMuxerUnifiedComplete.sys.mjs",
};
+import { ActionsProviderQuickActions } from "resource:///modules/ActionsProviderQuickActions.sys.mjs";
+import { ActionsProviderContextualSearch } from "resource:///modules/ActionsProviderContextualSearch.sys.mjs";
+
+let globalActionsProviders = [
+ ActionsProviderContextualSearch,
+ ActionsProviderQuickActions,
+];
+
const DEFAULT_MUXER = "UnifiedComplete";
/**
@@ -179,6 +183,17 @@ class ProvidersManager {
}
/**
+ * Returns the provider with the given name.
+ *
+ * @param {string} name
+ * The provider name.
+ * @returns {UrlbarProvider} The provider.
+ */
+ getActionProvider(name) {
+ return globalActionsProviders.find(p => p.name == name);
+ }
+
+ /**
* Registers a muxer object with the manager.
*
* @param {object} muxer
@@ -284,6 +299,12 @@ class ProvidersManager {
// history and bookmarks even if search engines are not available.
}
+ // All current global actions are currently memory lookups so it is safe to
+ // wait on them.
+ this.#globalAction = lazy.UrlbarPrefs.get("secondaryActions.featureGate")
+ ? await this.pickGlobalAction(queryContext, controller)
+ : null;
+
if (query.canceled) {
return;
}
@@ -357,6 +378,25 @@ class ProvidersManager {
);
}
}
+
+ #globalAction = null;
+
+ async pickGlobalAction(queryContext, controller) {
+ for (let provider of globalActionsProviders) {
+ if (provider.isActive(queryContext)) {
+ let action = await provider.queryAction(queryContext, controller);
+ if (action) {
+ action.providerName = provider.name;
+ return action;
+ }
+ }
+ }
+ return null;
+ }
+
+ getGlobalAction() {
+ return this.#globalAction;
+ }
}
export var UrlbarProvidersManager = new ProvidersManager();
diff --git a/browser/components/urlbar/UrlbarTokenizer.sys.mjs b/browser/components/urlbar/UrlbarTokenizer.sys.mjs
index c0b3a9c069..ee565ec1c9 100644
--- a/browser/components/urlbar/UrlbarTokenizer.sys.mjs
+++ b/browser/components/urlbar/UrlbarTokenizer.sys.mjs
@@ -246,7 +246,7 @@ export var UrlbarTokenizer = {
queryContext.tokens = [];
return queryContext;
}
- let unfiltered = splitString(queryContext.searchString);
+ let unfiltered = splitString(queryContext);
let tokens = filterTokens(unfiltered);
queryContext.tokens = tokens;
return queryContext;
@@ -276,13 +276,17 @@ const CHAR_TO_TYPE_MAP = new Map(
);
/**
- * Given a search string, splits it into string tokens.
+ * Given a queryContext object, splits its searchString into string tokens.
*
- * @param {string} searchString
- * The search string to split
+ * @param {UrlbarQueryContext} queryContext
+ * The query context object to tokenize.
+ * @param {string} queryContext.searchString
+ * The search string to split.
+ * @param {object} queryContext.searchMode
+ * A search mode object.
* @returns {Array} An array of string tokens.
*/
-function splitString(searchString) {
+function splitString({ searchString, searchMode }) {
// The first step is splitting on unicode whitespaces. We ignore whitespaces
// if the search string starts with "data:", to better support Web developers
// and compatiblity with other browsers.
@@ -327,7 +331,8 @@ function splitString(searchString) {
// allow for a typed question to yield only search results.
if (
CHAR_TO_TYPE_MAP.has(firstToken[0]) &&
- !UrlbarTokenizer.REGEXP_PERCENT_ENCODED_START.test(firstToken)
+ !UrlbarTokenizer.REGEXP_PERCENT_ENCODED_START.test(firstToken) &&
+ !searchMode
) {
tokens[0] = firstToken.substring(1);
tokens.splice(0, 0, firstToken[0]);
diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs
index 9fca8426a3..096d1c8f2d 100644
--- a/browser/components/urlbar/UrlbarUtils.sys.mjs
+++ b/browser/components/urlbar/UrlbarUtils.sys.mjs
@@ -113,8 +113,7 @@ export var UrlbarUtils = {
TABS: 4,
OTHER_LOCAL: 5,
OTHER_NETWORK: 6,
- ACTIONS: 7,
- ADDON: 8,
+ ADDON: 7,
},
// This defines icon locations that are commonly used in the UI.
@@ -228,13 +227,6 @@ export var UrlbarUtils = {
pref: "shortcuts.history",
telemetryLabel: "history",
},
- {
- source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
- restrict: lazy.UrlbarTokenizer.RESTRICT.ACTION,
- icon: "chrome://browser/skin/quickactions.svg",
- pref: "shortcuts.quickactions",
- telemetryLabel: "actions",
- },
];
},
@@ -1279,8 +1271,6 @@ export var UrlbarUtils = {
if (result.providerName == "TabToSearch") {
// This is the onboarding result.
return "tabtosearch";
- } else if (result.providerName == "quickactions") {
- return "quickaction";
} else if (result.providerName == "Weather") {
return "weather";
}
@@ -1435,14 +1425,10 @@ export var UrlbarUtils = {
switch (result.providerName) {
case "calculator":
return "calc";
- case "quickactions":
- return "action";
case "TabToSearch":
return "tab_to_search";
case "UnitConversion":
return "unit";
- case "UrlbarProviderContextualSearch":
- return "site_specific_contextual_search";
case "UrlbarProviderQuickSuggest":
return this._getQuickSuggestTelemetryType(result);
case "UrlbarProviderQuickSuggestContextualOptIn":
@@ -1535,28 +1521,6 @@ export var UrlbarUtils = {
return "unknown";
},
- /**
- * Extracts a subtype for search engagement telemetry from a result and the picked element.
- *
- * @param {UrlbarResult} result The result to analyze.
- * @param {DOMElement} element The picked view element. Nullable.
- * @returns {string} Subtype as string.
- */
- searchEngagementTelemetrySubtype(result, element) {
- if (!result) {
- return "";
- }
-
- if (
- result.providerName === "quickactions" &&
- element?.classList.contains("urlbarView-quickaction-button")
- ) {
- return element.dataset.key;
- }
-
- return "";
- },
-
_getQuickSuggestTelemetryType(result) {
if (result.payload.telemetryType == "weather") {
// Return "weather" without the usual source prefix for consistency with
@@ -1641,6 +1605,17 @@ UrlbarUtils.RESULT_PAYLOAD_SCHEMA = {
type: "object",
required: ["url"],
properties: {
+ action: {
+ type: "object",
+ properties: {
+ l10nId: {
+ type: "string",
+ },
+ key: {
+ type: "string",
+ },
+ },
+ },
displayUrl: {
type: "string",
},
diff --git a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
index b27bede750..2fa3d0137d 100644
--- a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
+++ b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
@@ -146,12 +146,14 @@ export class UrlbarValueFormatter {
// we can skip most of this.
if (
browser._urlMetaData &&
- browser._urlMetaData.inputValue == this.urlbarInput.untrimmedValue
+ browser._urlMetaData.inputValue == inputValue &&
+ browser._urlMetaData.untrimmedValue == this.urlbarInput.untrimmedValue
) {
return browser._urlMetaData.data;
}
browser._urlMetaData = {
- inputValue: this.urlbarInput.untrimmedValue,
+ inputValue,
+ untrimmedValue: this.urlbarInput.untrimmedValue,
data: null,
};
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
index 3d6ea46781..c5ea040f1f 100644
--- a/browser/components/urlbar/UrlbarView.sys.mjs
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
@@ -1635,6 +1635,38 @@ export class UrlbarView {
item.appendChild(button);
}
+ #createSecondaryAction(action, global = false) {
+ let actionContainer = this.#createElement("div");
+ actionContainer.classList.add("urlbarView-actions-container");
+
+ let button = this.#createElement("span");
+ button.classList.add("urlbarView-action-btn");
+ if (global) {
+ button.classList.add("urlbarView-global-action-btn");
+ }
+ button.setAttribute("role", "button");
+ if (action.icon) {
+ let icon = this.#createElement("img");
+ icon.src = action.icon;
+ button.appendChild(icon);
+ }
+ for (let key in action.dataset ?? {}) {
+ button.dataset[key] = action.dataset[key];
+ }
+ button.dataset.action = action.key;
+ button.dataset.providerName = action.providerName;
+
+ let label = this.#createElement("span");
+ if (action.l10nId) {
+ this.#setElementL10n(label, { id: action.l10nId, args: action.l10nArgs });
+ } else {
+ this.document.l10n.setAttributes(label, action.label, action.l10nArgs);
+ }
+ button.appendChild(label);
+ actionContainer.appendChild(button);
+ return actionContainer;
+ }
+
// eslint-disable-next-line complexity
#updateRow(item, result) {
let oldResult = item.result;
@@ -1707,6 +1739,26 @@ export class UrlbarView {
}
item._content.id = item.id + "-inner";
+ let isFirstChild = item === this.#rows.children[0];
+ let secAction =
+ result.heuristic || isFirstChild
+ ? lazy.UrlbarProvidersManager.getGlobalAction()
+ : result.payload.action;
+ let container = item.querySelector(".urlbarView-actions-container");
+ if (secAction && !container) {
+ item.appendChild(this.#createSecondaryAction(secAction, isFirstChild));
+ } else if (
+ secAction &&
+ secAction.key != container.firstChild.dataset.action
+ ) {
+ item.replaceChild(
+ this.#createSecondaryAction(secAction, isFirstChild),
+ container
+ );
+ } else if (!secAction && container) {
+ item.removeChild(container);
+ }
+
item.removeAttribute("feedback-acknowledgment");
if (
@@ -1800,6 +1852,10 @@ export class UrlbarView {
let isRowSelectable = true;
switch (result.type) {
case lazy.UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
+ // Hide chichlet when showing secondaryActions.
+ if (lazy.UrlbarPrefs.get("secondaryActions.featureGate")) {
+ break;
+ }
actionSetter = () => {
this.#setSwitchTabActionChiclet(result, action);
};
diff --git a/browser/components/urlbar/docs/firefox-suggest-telemetry.rst b/browser/components/urlbar/docs/firefox-suggest-telemetry.rst
index 8d9c7c20ff..e3d37605e1 100644
--- a/browser/components/urlbar/docs/firefox-suggest-telemetry.rst
+++ b/browser/components/urlbar/docs/firefox-suggest-telemetry.rst
@@ -501,7 +501,11 @@ Changelog
Firefox 109.0
Introduced. [Bug 1800993_]
+ Firefox 127.0
+ Removed. [Bug 1891602_]
+
.. _1800993: https://bugzilla.mozilla.org/show_bug.cgi?id=1800993
+.. _1891602: https://bugzilla.mozilla.org/show_bug.cgi?id=1891602
contextual.services.quicksuggest.help_nonsponsored
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -579,7 +583,11 @@ Changelog
Firefox 110.0
Introduced. [Bug 1804536_]
+ Firefox 127.0
+ Removed. [Bug 1891602_]
+
.. _1804536: https://bugzilla.mozilla.org/show_bug.cgi?id=1804536
+.. _1891602: https://bugzilla.mozilla.org/show_bug.cgi?id=1891602
contextual.services.quicksuggest.impression
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/browser/components/urlbar/metrics.yaml b/browser/components/urlbar/metrics.yaml
index 173ee08a10..5140391e4f 100644
--- a/browser/components/urlbar/metrics.yaml
+++ b/browser/components/urlbar/metrics.yaml
@@ -487,11 +487,13 @@ urlbar:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852058
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852058#c2
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1866204#c8
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1892377#c2
data_sensitivity:
- interaction
notification_emails:
- fx-search-telemetry@mozilla.com
- expires: 128
+ expires: 132
pref_max_results:
lifetime: application
diff --git a/browser/components/urlbar/moz.build b/browser/components/urlbar/moz.build
index e35ea11655..4b91bef331 100644
--- a/browser/components/urlbar/moz.build
+++ b/browser/components/urlbar/moz.build
@@ -12,6 +12,9 @@ DIRS += [
]
EXTRA_JS_MODULES += [
+ "ActionsProvider.sys.mjs",
+ "ActionsProviderContextualSearch.sys.mjs",
+ "ActionsProviderQuickActions.sys.mjs",
"MerinoClient.sys.mjs",
"QuickActionsLoaderDefault.sys.mjs",
"QuickSuggest.sys.mjs",
@@ -26,7 +29,6 @@ EXTRA_JS_MODULES += [
"UrlbarProviderBookmarkKeywords.sys.mjs",
"UrlbarProviderCalculator.sys.mjs",
"UrlbarProviderClipboard.sys.mjs",
- "UrlbarProviderContextualSearch.sys.mjs",
"UrlbarProviderHeuristicFallback.sys.mjs",
"UrlbarProviderHistoryUrlHeuristic.sys.mjs",
"UrlbarProviderInputHistory.sys.mjs",
@@ -35,7 +37,6 @@ EXTRA_JS_MODULES += [
"UrlbarProviderOpenTabs.sys.mjs",
"UrlbarProviderPlaces.sys.mjs",
"UrlbarProviderPrivateSearch.sys.mjs",
- "UrlbarProviderQuickActions.sys.mjs",
"UrlbarProviderQuickSuggest.sys.mjs",
"UrlbarProviderQuickSuggestContextualOptIn.sys.mjs",
"UrlbarProviderRecentSearches.sys.mjs",
diff --git a/browser/components/urlbar/private/SuggestBackendRust.sys.mjs b/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
index 3993149757..120e7b7d0c 100644
--- a/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
+++ b/browser/components/urlbar/private/SuggestBackendRust.sys.mjs
@@ -289,7 +289,10 @@ export class SuggestBackendRust extends BaseFeature {
if (instance != this.#ingestInstance) {
return;
}
- await (this.#ingestPromise = this.#ingestHelper());
+ this.#ingestPromise = new Promise(resolve => {
+ ChromeUtils.idleDispatch(() => this.#ingestHelper().finally(resolve));
+ });
+ await this.#ingestPromise;
}
async #ingestHelper() {
diff --git a/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs b/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
index f576f4ca19..793af24b41 100644
--- a/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs
@@ -1043,9 +1043,11 @@ export var UrlbarTestUtils = {
* Removes the scheme from an url according to user prefs.
*
* @param {string} url
- * The url that is supposed to be sanitizied.
- * @param {{removeSingleTrailingSlash: (boolean)}} options
- * removeSingleTrailingSlash: Remove trailing slash, when trimming enabled.
+ * The url that is supposed to be trimmed.
+ * @param {object} [options]
+ * Options for the trimming.
+ * @param {boolean} [options.removeSingleTrailingSlash]
+ * Remove trailing slash, when trimming enabled.
* @returns {string}
* The sanitized URL.
*/
@@ -1060,15 +1062,13 @@ export var UrlbarTestUtils = {
lazy.BrowserUIUtils.removeSingleTrailingSlashFromURL(sanitizedURL);
}
+ // Also remove emphasis markers if present.
if (lazy.UrlbarPrefs.get("trimHttps")) {
- sanitizedURL = sanitizedURL.replace("https://", "");
+ sanitizedURL = sanitizedURL.replace(/^<?https:\/\/>?/, "");
} else {
- sanitizedURL = sanitizedURL.replace("http://", "");
+ sanitizedURL = sanitizedURL.replace(/^<?http:\/\/>?/, "");
}
- // Remove empty emphasis markers in case the protocol was trimmed.
- sanitizedURL = sanitizedURL.replace("<>", "");
-
return sanitizedURL;
},
diff --git a/browser/components/urlbar/tests/browser/browser.toml b/browser/components/urlbar/tests/browser/browser.toml
index 44b964e5ca..38046aa26b 100644
--- a/browser/components/urlbar/tests/browser/browser.toml
+++ b/browser/components/urlbar/tests/browser/browser.toml
@@ -68,6 +68,8 @@ skip-if = ["apple_catalina && debug"] # Bug 1773790
["browser_UrlbarInput_trimURLs.js"]
https_first_disabled = true
+["browser_UrlbarInput_untrimOnUserInteraction.js"]
+
["browser_aboutHomeLoading.js"]
skip-if = [
"tsan", # Intermittently times out, see 1622698 (frequent on TSan).
@@ -365,6 +367,8 @@ support-files = [
["browser_quickactions.js"]
+["browser_quickactions_commands.js"]
+
["browser_quickactions_devtools.js"]
["browser_quickactions_screenshot.js"]
@@ -498,6 +502,8 @@ support-files = ["search-engines", "../../../search/test/browser/trendingSuggest
["browser_search_history_from_history_panel.js"]
+["browser_secondaryActions.js"]
+
["browser_selectStaleResults.js"]
support-files = [
"searchSuggestionEngineSlow.xml",
@@ -645,9 +651,6 @@ tags = "search-telemetry"
https_first_disabled = true
tags = "search-telemetry"
-["browser_urlbar_telemetry_quickactions.js"]
-tags = "search-telemetry"
-
["browser_urlbar_telemetry_remotetab.js"]
tags = "search-telemetry"
diff --git a/browser/components/urlbar/tests/browser/browser_UrlbarInput_untrimOnUserInteraction.js b/browser/components/urlbar/tests/browser/browser_UrlbarInput_untrimOnUserInteraction.js
new file mode 100644
index 0000000000..a6714df360
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_untrimOnUserInteraction.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let tests = [
+ {
+ description: "Test single click doesn't untrim",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
+ Assert.equal(gURLBar.selectionStart, 0, "Selection start is 0.");
+ Assert.equal(
+ gURLBar.selectionEnd,
+ gURLBar.value.length,
+ "Selection end is at and of text."
+ );
+ },
+ shouldUntrim: false,
+ },
+ {
+ description: "Test CTRL+L doesn't untrim",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ Assert.equal(gURLBar.selectionStart, 0, "Selection start is 0.");
+ Assert.equal(
+ gURLBar.selectionEnd,
+ gURLBar.value.length,
+ "Selection end is at and of text."
+ );
+ },
+ shouldUntrim: false,
+ },
+ {
+ description: "Test drag selection untrims",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ selectWithMouseDrag(100, 200);
+ Assert.greater(gURLBar.selectionStart, 0, "Selection start is positive.");
+ Assert.greater(
+ gURLBar.selectionEnd,
+ gURLBar.selectionStart,
+ "Selection is not empty."
+ );
+ },
+ shouldUntrim: true,
+ },
+ {
+ description: "Test double click selection untrims",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ selectWithDoubleClick(200);
+ Assert.greater(gURLBar.selectionStart, 0, "Selection start is positive.");
+ Assert.greater(
+ gURLBar.selectionEnd,
+ gURLBar.selectionStart,
+ "Selection is not empty."
+ );
+ },
+ shouldUntrim: true,
+ },
+ {
+ description: "Test click, LEFT untrims",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
+ EventUtils.synthesizeKey("KEY_ArrowLeft");
+ },
+ shouldUntrim: true,
+ },
+ {
+ description: "Test CTRL+L, HOME untrims",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ execute() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ if (AppConstants.platform == "macosx") {
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { metaKey: true });
+ } else {
+ EventUtils.synthesizeKey("KEY_Home");
+ }
+ },
+ shouldUntrim: true,
+ },
+ {
+ description: "Test SHIFT+LEFT untrims",
+ untrimmedValue: BrowserUIUtils.trimURLProtocol + "www.example.com/",
+ async execute() {
+ EventUtils.synthesizeKey("l", { accelKey: true });
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ Assert.equal(gURLBar.selectionStart, 0, "Selection start is 0.");
+ Assert.less(
+ gURLBar.selectionEnd,
+ gURLBar.value.length,
+ "Selection skips last characters."
+ );
+ },
+ shouldUntrim: true,
+ },
+];
+
+add_task(async function test_untrim() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.untrimOnUserInteraction.featureGate", true]],
+ });
+
+ for (let test of tests) {
+ info(test.description);
+ let trimmedValue = UrlbarTestUtils.trimURL(test.untrimmedValue);
+ gURLBar._setValue(test.untrimmedValue, {
+ allowTrim: true,
+ valueIsTyped: false,
+ });
+ gURLBar.blur();
+ Assert.equal(gURLBar.value, trimmedValue, "Value has been trimmed");
+ await test.execute();
+ Assert.equal(
+ gURLBar.value,
+ test.shouldUntrim ? test.untrimmedValue : trimmedValue,
+ "Value has been untrimmed"
+ );
+ gURLBar.handleRevert();
+ }
+});
diff --git a/browser/components/urlbar/tests/browser/browser_autocomplete_edit_completed.js b/browser/components/urlbar/tests/browser/browser_autocomplete_edit_completed.js
index 4fa60f6bf3..220634eb2c 100644
--- a/browser/components/urlbar/tests/browser/browser_autocomplete_edit_completed.js
+++ b/browser/components/urlbar/tests/browser/browser_autocomplete_edit_completed.js
@@ -51,6 +51,7 @@ add_task(async function () {
info("Press backspace");
EventUtils.synthesizeKey("KEY_Backspace");
+ info("Backspaced value is " + gURLBar.value);
await UrlbarTestUtils.promiseSearchComplete(window);
let editedValue = gURLBar.value;
diff --git a/browser/components/urlbar/tests/browser/browser_contextualsearch.js b/browser/components/urlbar/tests/browser/browser_contextualsearch.js
index 60e489a542..449d1864c2 100644
--- a/browser/components/urlbar/tests/browser/browser_contextualsearch.js
+++ b/browser/components/urlbar/tests/browser/browser_contextualsearch.js
@@ -3,22 +3,54 @@
"use strict";
-const { UrlbarProviderContextualSearch } = ChromeUtils.importESModule(
- "resource:///modules/UrlbarProviderContextualSearch.sys.mjs"
+const { ActionsProviderContextualSearch } = ChromeUtils.importESModule(
+ "resource:///modules/ActionsProviderContextualSearch.sys.mjs"
+);
+
+const { AddonTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/AddonTestUtils.sys.mjs"
);
add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
- set: [["browser.urlbar.contextualSearch.enabled", true]],
+ set: [
+ ["browser.urlbar.contextualSearch.enabled", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
+ ],
});
-});
-add_task(async function test_selectContextualSearchResult_already_installed() {
- await SearchTestUtils.installSearchExtension({
+ let ext = await SearchTestUtils.installSearchExtension({
name: "Contextual",
search_url: "https://example.com/browser",
});
+ await AddonTestUtils.waitForSearchProviderStartup(ext);
+});
+
+add_task(async function test_no_engine() {
+ const ENGINE_TEST_URL = "https://example.org/";
+ let onLoaded = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ ENGINE_TEST_URL
+ );
+ BrowserTestUtils.startLoadingURIString(
+ gBrowser.selectedBrowser,
+ ENGINE_TEST_URL
+ );
+ await onLoaded;
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ Assert.ok(
+ UrlbarTestUtils.getResultCount(window) > 0,
+ "At least one result is shown"
+ );
+});
+
+add_task(async function test_selectContextualSearchResult_already_installed() {
const ENGINE_TEST_URL = "https://example.com/";
let onLoaded = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
@@ -44,25 +76,15 @@ add_task(async function test_selectContextualSearchResult_already_installed() {
window,
value: query,
});
- const resultIndex = UrlbarTestUtils.getResultCount(window) - 1;
- const result = await UrlbarTestUtils.getDetailsOfResultAt(
- window,
- resultIndex
- );
-
- is(
- result.dynamicType,
- "contextualSearch",
- "Second last result is a contextual search result"
- );
info("Focus and select the contextual search result");
- UrlbarTestUtils.setSelectedRowIndex(window, resultIndex);
let onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
expectedUrl
);
+
+ EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Enter");
await onLoad;
@@ -95,25 +117,14 @@ add_task(async function test_selectContextualSearchResult_not_installed() {
window,
value: query,
});
- const resultIndex = UrlbarTestUtils.getResultCount(window) - 1;
- const result = await UrlbarTestUtils.getDetailsOfResultAt(
- window,
- resultIndex
- );
-
- Assert.equal(
- result.dynamicType,
- "contextualSearch",
- "Second last result is a contextual search result"
- );
info("Focus and select the contextual search result");
- UrlbarTestUtils.setSelectedRowIndex(window, resultIndex);
let onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
EXPECTED_URL
);
+ EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Enter");
await onLoad;
@@ -122,4 +133,6 @@ add_task(async function test_selectContextualSearchResult_not_installed() {
EXPECTED_URL,
"Selecting the contextual search result opens the search URL"
);
+
+ ActionsProviderContextualSearch.resetForTesting();
});
diff --git a/browser/components/urlbar/tests/browser/browser_decode.js b/browser/components/urlbar/tests/browser/browser_decode.js
index 577d39b587..ee7831eea6 100644
--- a/browser/components/urlbar/tests/browser/browser_decode.js
+++ b/browser/components/urlbar/tests/browser/browser_decode.js
@@ -72,7 +72,7 @@ add_task(async function actionURILosslessDecode() {
Assert.equal(
gURLBar.value,
- UrlbarTestUtils.trimURL(urlNoScheme),
+ urlNoScheme,
"The string displayed in the textbox should not be escaped"
);
diff --git a/browser/components/urlbar/tests/browser/browser_keepStateAcrossTabSwitches.js b/browser/components/urlbar/tests/browser/browser_keepStateAcrossTabSwitches.js
index 6ad6ce43e6..abe12846ab 100644
--- a/browser/components/urlbar/tests/browser/browser_keepStateAcrossTabSwitches.js
+++ b/browser/components/urlbar/tests/browser/browser_keepStateAcrossTabSwitches.js
@@ -3,6 +3,10 @@
"use strict";
+add_setup(async function setup() {
+ registerCleanupFunction(PlacesUtils.history.clear);
+});
+
/**
* Verify user typed text remains in the URL bar when tab switching, even when
* loads fail.
@@ -78,94 +82,94 @@ add_task(async function invalidURL() {
* Test the urlbar status of text selection and focusing by tab switching.
*/
add_task(async function selectAndFocus() {
- // Create a tab with normal web page. Use a test-url that uses a protocol that
- // is not trimmed.
- const webpageTabURL =
- UrlbarTestUtils.getTrimmedProtocolWithSlashes() == "https://"
- ? "http://example.com"
- : "https://example.com";
- const webpageTab = await BrowserTestUtils.openNewForegroundTab({
- gBrowser,
- url: webpageTabURL,
- });
+ // Test both protocols to ensure we're testing any trimming case.
+ for (let protocol of ["http://", "https://"]) {
+ const webpageTabURL = protocol + "example.com";
+ const webpageTab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: webpageTabURL,
+ });
- // Create a tab with userTypedValue.
- const userTypedTabText = "test";
- const userTypedTab = await BrowserTestUtils.openNewForegroundTab({
- gBrowser,
- });
- await UrlbarTestUtils.inputIntoURLBar(window, userTypedTabText);
+ // Create a tab with userTypedValue.
+ const userTypedTabText = "test";
+ const userTypedTab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ });
+ await UrlbarTestUtils.inputIntoURLBar(window, userTypedTabText);
- // Create an empty tab.
- const emptyTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser });
+ // Create an empty tab.
+ const emptyTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser });
- registerCleanupFunction(async () => {
- await PlacesUtils.history.clear();
- BrowserTestUtils.removeTab(webpageTab);
- BrowserTestUtils.removeTab(userTypedTab);
- BrowserTestUtils.removeTab(emptyTab);
- });
+ async function cleanup() {
+ await PlacesUtils.history.clear();
+ BrowserTestUtils.removeTab(webpageTab);
+ BrowserTestUtils.removeTab(userTypedTab);
+ BrowserTestUtils.removeTab(emptyTab);
+ }
- await doSelectAndFocusTest({
- targetTab: webpageTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: userTypedTab,
- });
- await doSelectAndFocusTest({
- targetTab: webpageTab,
- targetSelectionStart: 2,
- targetSelectionEnd: 5,
- anotherTab: userTypedTab,
- });
- await doSelectAndFocusTest({
- targetTab: webpageTab,
- targetSelectionStart: webpageTabURL.length,
- targetSelectionEnd: webpageTabURL.length,
- anotherTab: userTypedTab,
- });
- await doSelectAndFocusTest({
- targetTab: webpageTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: emptyTab,
- });
- await doSelectAndFocusTest({
- targetTab: userTypedTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: webpageTab,
- });
- await doSelectAndFocusTest({
- targetTab: userTypedTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: emptyTab,
- });
- await doSelectAndFocusTest({
- targetTab: userTypedTab,
- targetSelectionStart: 1,
- targetSelectionEnd: 2,
- anotherTab: emptyTab,
- });
- await doSelectAndFocusTest({
- targetTab: userTypedTab,
- targetSelectionStart: userTypedTabText.length,
- targetSelectionEnd: userTypedTabText.length,
- anotherTab: emptyTab,
- });
- await doSelectAndFocusTest({
- targetTab: emptyTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: webpageTab,
- });
- await doSelectAndFocusTest({
- targetTab: emptyTab,
- targetSelectionStart: 0,
- targetSelectionEnd: 0,
- anotherTab: userTypedTab,
- });
+ await doSelectAndFocusTest({
+ targetTab: webpageTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: userTypedTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: webpageTab,
+ targetSelectionStart: 2,
+ targetSelectionEnd: 5,
+ anotherTab: userTypedTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: webpageTab,
+ targetSelectionStart: webpageTabURL.length,
+ targetSelectionEnd: webpageTabURL.length,
+ anotherTab: userTypedTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: webpageTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: emptyTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: userTypedTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: webpageTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: userTypedTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: emptyTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: userTypedTab,
+ targetSelectionStart: 1,
+ targetSelectionEnd: 2,
+ anotherTab: emptyTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: userTypedTab,
+ targetSelectionStart: userTypedTabText.length,
+ targetSelectionEnd: userTypedTabText.length,
+ anotherTab: emptyTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: emptyTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: webpageTab,
+ });
+ await doSelectAndFocusTest({
+ targetTab: emptyTab,
+ targetSelectionStart: 0,
+ targetSelectionEnd: 0,
+ anotherTab: userTypedTab,
+ });
+
+ await cleanup();
+ }
});
async function doSelectAndFocusTest({
@@ -197,6 +201,13 @@ async function doSelectAndFocusTest({
targetSelectionStart,
targetSelectionEnd
);
+ const targetSelectedText = getSelectedText();
+ if (gURLBar.selectionStart != gURLBar.selectionEnd) {
+ Assert.ok(
+ targetSelectedText,
+ `Some text is selected: "${targetSelectedText}"`
+ );
+ }
const targetValue = gURLBar.value;
// Switch to another tab.
@@ -210,8 +221,9 @@ async function doSelectAndFocusTest({
Assert.equal(gURLBar.value, targetValue);
Assert.equal(gURLBar.focused, targetFocus);
if (gURLBar.focused) {
- Assert.equal(gURLBar.selectionStart, targetSelectionStart);
- Assert.equal(gURLBar.selectionEnd, targetSelectionEnd);
+ // Check the selected text rather than the selection indices, to keep
+ // untrimming into account.
+ Assert.equal(targetSelectedText, getSelectedText());
} else {
Assert.equal(gURLBar.selectionStart, gURLBar.value.length);
Assert.equal(gURLBar.selectionEnd, gURLBar.value.length);
@@ -221,12 +233,21 @@ async function doSelectAndFocusTest({
function setURLBarFocus(focus) {
if (focus) {
- gURLBar.focus();
+ // Simulate a user interaction, to eventually cause untrimming.
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
} else {
gURLBar.blur();
}
}
+function getSelectedText() {
+ return gURLBar.inputField.editor.selection.toStringWithFormat(
+ "text/plain",
+ Ci.nsIDocumentEncoder.OutputPreformatted | Ci.nsIDocumentEncoder.OutputRaw,
+ 0
+ );
+}
+
async function switchTab(tab) {
if (gBrowser.selectedTab !== tab) {
EventUtils.synthesizeMouseAtCenter(tab, {});
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
index 2ad6ee0e07..44a7ea64b2 100644
--- a/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js
+++ b/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js
@@ -238,51 +238,3 @@ function getTextWidth(inputText) {
.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_oneOffs.js b/browser/components/urlbar/tests/browser/browser_oneOffs.js
index 0c04f1e321..e517ea0a9a 100644
--- a/browser/components/urlbar/tests/browser/browser_oneOffs.js
+++ b/browser/components/urlbar/tests/browser/browser_oneOffs.js
@@ -30,7 +30,6 @@ add_setup(async function () {
set: [
["browser.search.separatePrivateDefault.ui.enabled", false],
["browser.urlbar.suggest.quickactions", false],
- ["browser.urlbar.shortcuts.quickactions", true],
],
});
@@ -943,7 +942,7 @@ async function doLocalShortcutsShownTest() {
await rebuildPromise;
let buttons = oneOffSearchButtons.localButtons;
- Assert.equal(buttons.length, 4, "Expected number of local shortcuts");
+ Assert.equal(buttons.length, 3, "Expected number of local shortcuts");
let expectedSource;
let seenIDs = new Set();
@@ -963,9 +962,6 @@ async function doLocalShortcutsShownTest() {
case "urlbar-engine-one-off-item-history":
expectedSource = UrlbarUtils.RESULT_SOURCE.HISTORY;
break;
- case "urlbar-engine-one-off-item-actions":
- expectedSource = UrlbarUtils.RESULT_SOURCE.ACTIONS;
- break;
default:
Assert.ok(false, `Unexpected local shortcut ID: ${button.id}`);
break;
diff --git a/browser/components/urlbar/tests/browser/browser_quickactions.js b/browser/components/urlbar/tests/browser/browser_quickactions.js
index ccf045d9e8..1fc4ef7cd6 100644
--- a/browser/components/urlbar/tests/browser/browser_quickactions.js
+++ b/browser/components/urlbar/tests/browser/browser_quickactions.js
@@ -10,32 +10,41 @@
ChromeUtils.defineESModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
UpdateService: "resource://gre/modules/UpdateService.sys.mjs",
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
+ ActionsProviderQuickActions:
+ "resource:///modules/ActionsProviderQuickActions.sys.mjs",
});
const DUMMY_PAGE =
- "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
+ "https://example.com/browser/browser/base/content/test/general/dummy_page.html";
let testActionCalled = 0;
+const assertAction = async name => {
+ await BrowserTestUtils.waitForCondition(() =>
+ window.document.querySelector(`.urlbarView-action-btn[data-action=${name}]`)
+ );
+ Assert.ok(true, `We found action "${name}`);
+};
+
+const hasQuickActions = win =>
+ !!win.document.querySelector(".urlbarView-action-btn");
+
add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.quickactions.enabled", true],
- ["browser.urlbar.suggest.quickactions", true],
- ["browser.urlbar.shortcuts.quickactions", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
],
});
- UrlbarProviderQuickActions.addAction("testaction", {
+ ActionsProviderQuickActions.addAction("testaction", {
commands: ["testaction"],
label: "quickactions-downloads2",
onPick: () => testActionCalled++,
});
registerCleanupFunction(() => {
- UrlbarProviderQuickActions.removeAction("testaction");
+ ActionsProviderQuickActions.removeAction("testaction");
});
});
@@ -57,183 +66,16 @@ add_task(async function basic() {
value: "testact",
});
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 2,
- "We matched the action"
- );
+ await assertAction("testaction");
info("The callback of the action is fired when selected");
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
- Assert.equal(testActionCalled, 1, "Test actionwas called");
-});
-
-add_task(async function test_label_command() {
- info("A prefix of the label matches");
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "View Dow",
- });
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 2,
- "We matched the action"
- );
-
- let { result } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
- Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.DYNAMIC);
- Assert.equal(result.providerName, "quickactions");
- await UrlbarTestUtils.promisePopupClose(window, () => {
- EventUtils.synthesizeKey("KEY_Escape");
- });
-});
-
-add_task(async function enter_search_mode_button() {
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "test",
- });
-
- await clickQuickActionOneoffButton();
-
- await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
- Assert.ok(true, "Actions are shown when we enter actions search mode.");
-
- await UrlbarTestUtils.exitSearchMode(window);
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
-});
-
-add_task(async function enter_search_mode_oneoff_by_key() {
- // Select actions oneoff button by keyboard.
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "",
- });
- await UrlbarTestUtils.enterSearchMode(window);
- const oneOffButtons = UrlbarTestUtils.getOneOffSearchButtons(window);
- for (;;) {
- EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true });
- if (
- oneOffButtons.selectedButton.source === UrlbarUtils.RESULT_SOURCE.ACTIONS
- ) {
- break;
- }
- }
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: " ",
- });
- await UrlbarTestUtils.assertSearchMode(window, {
- source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
- entry: "oneoff",
- });
-
- await UrlbarTestUtils.exitSearchMode(window);
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
-});
-
-add_task(async function enter_search_mode_key() {
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "> ",
- });
- await UrlbarTestUtils.assertSearchMode(window, {
- source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
- entry: "typed",
- });
- Assert.equal(
- await hasQuickActions(window),
- true,
- "Actions are shown in search mode"
- );
- await UrlbarTestUtils.exitSearchMode(window);
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
-});
-
-add_task(async function test_disabled() {
- UrlbarProviderQuickActions.addAction("disabledaction", {
- commands: ["disabledaction"],
- isActive: () => false,
- label: "quickactions-restart",
- });
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "disabled",
- });
-
- Assert.equal(
- await hasQuickActions(window),
- false,
- "Result for quick actions is hidden"
- );
-
- await UrlbarTestUtils.promisePopupClose(window);
- UrlbarProviderQuickActions.removeAction("disabledaction");
-});
-
-/**
- * The first part of this test confirms that when the screenshots component is enabled
- * the screenshot quick action button will be enabled on about: pages.
- * The second part confirms that when the screenshots extension is enabled the
- * screenshot quick action button will be disbaled on about: pages.
- */
-add_task(async function test_screenshot_enabled_or_disabled() {
- let onLoaded = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "about:blank"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "about:blank"
- );
- await onLoaded;
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "screenshot",
- });
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 2,
- "The action is displayed"
- );
- let screenshotButton = window.document.querySelector(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- );
- Assert.ok(
- !screenshotButton.hasAttribute("disabled"),
- "Screenshot button is enabled on about pages"
- );
-
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
-
- await SpecialPowers.pushPrefEnv({
- set: [["screenshots.browser.component.enabled", false]],
- });
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "screenshot",
- });
- Assert.equal(
- await hasQuickActions(window),
- false,
- "Result for quick actions is hidden"
- );
-
- await UrlbarTestUtils.promisePopupClose(window);
+ Assert.equal(testActionCalled, 1, "Test action was called");
});
add_task(async function match_in_phrase() {
- UrlbarProviderQuickActions.addAction("newtestaction", {
+ ActionsProviderQuickActions.addAction("newtestaction", {
commands: ["matchingstring"],
label: "quickactions-downloads2",
});
@@ -243,304 +85,31 @@ add_task(async function match_in_phrase() {
window,
value: "Test we match at end of matchingstring",
});
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 2,
- "We matched the action"
- );
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
- UrlbarProviderQuickActions.removeAction("newtestaction");
-});
-
-add_task(async function test_other_search_mode() {
- let defaultEngine = await SearchTestUtils.promiseNewSearchEngine({
- url: getRootDirectory(gTestPath) + "searchSuggestionEngine.xml",
- });
- defaultEngine.alias = "testalias";
- let oldDefaultEngine = await Services.search.getDefault();
- Services.search.setDefault(
- defaultEngine,
- Ci.nsISearchService.CHANGE_REASON_UNKNOWN
- );
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: defaultEngine.alias + " ",
- });
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 0,
- "The results should be empty as no actions are displayed in other search modes"
- );
- await UrlbarTestUtils.assertSearchMode(window, {
- engineName: defaultEngine.name,
- entry: "typed",
- });
- await UrlbarTestUtils.promisePopupClose(window, () => {
- EventUtils.synthesizeKey("KEY_Escape");
- });
- Services.search.setDefault(
- oldDefaultEngine,
- Ci.nsISearchService.CHANGE_REASON_UNKNOWN
- );
-});
-
-add_task(async function test_no_quickactions_suggestions() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["browser.urlbar.suggest.quickactions", false],
- ["screenshots.browser.component.enabled", true],
- ],
- });
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "screenshot",
- });
- Assert.ok(
- !window.document.querySelector(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- ),
- "Screenshot button is not suggested"
- );
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "> screenshot",
- });
- Assert.ok(
- window.document.querySelector(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- ),
- "Screenshot button is suggested"
- );
-
- await UrlbarTestUtils.promisePopupClose(window);
- EventUtils.synthesizeKey("KEY_Escape");
-
- await SpecialPowers.popPrefEnv();
-});
-
-add_task(async function test_quickactions_disabled() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["browser.urlbar.quickactions.enabled", false],
- ["browser.urlbar.suggest.quickactions", true],
- ],
- });
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "screenshot",
- });
-
- Assert.ok(
- !window.document.querySelector(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- ),
- "Screenshot button is not suggested"
- );
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "> screenshot",
- });
- Assert.ok(
- !window.document.querySelector(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- ),
- "Screenshot button is not suggested"
- );
-
+ await assertAction("newtestaction");
await UrlbarTestUtils.promisePopupClose(window);
EventUtils.synthesizeKey("KEY_Escape");
-
- await SpecialPowers.popPrefEnv();
-});
-
-let COMMANDS_TESTS = [
- {
- cmd: "add-ons",
- uri: "about:addons",
- testFun: async () => isSelected("button[name=discover]"),
- },
- {
- cmd: "plugins",
- uri: "about:addons",
- testFun: async () => isSelected("button[name=plugin]"),
- },
- {
- cmd: "extensions",
- uri: "about:addons",
- testFun: async () => isSelected("button[name=extension]"),
- },
- {
- cmd: "themes",
- uri: "about:addons",
- testFun: async () => isSelected("button[name=theme]"),
- },
- {
- cmd: "add-ons",
- setup: async () => {
- const onLoad = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "http://example.com/"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "http://example.com/"
- );
- await onLoad;
- },
- uri: "about:addons",
- isNewTab: true,
- testFun: async () => isSelected("button[name=discover]"),
- },
- {
- cmd: "plugins",
- setup: async () => {
- const onLoad = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "http://example.com/"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "http://example.com/"
- );
- await onLoad;
- },
- uri: "about:addons",
- isNewTab: true,
- testFun: async () => isSelected("button[name=plugin]"),
- },
- {
- cmd: "extensions",
- setup: async () => {
- const onLoad = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "http://example.com/"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "http://example.com/"
- );
- await onLoad;
- },
- uri: "about:addons",
- isNewTab: true,
- testFun: async () => isSelected("button[name=extension]"),
- },
- {
- cmd: "themes",
- setup: async () => {
- const onLoad = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "http://example.com/"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "http://example.com/"
- );
- await onLoad;
- },
- uri: "about:addons",
- isNewTab: true,
- testFun: async () => isSelected("button[name=theme]"),
- },
-];
-
-let isSelected = async selector =>
- SpecialPowers.spawn(gBrowser.selectedBrowser, [selector], arg => {
- return ContentTaskUtils.waitForCondition(() =>
- content.document.querySelector(arg)?.hasAttribute("selected")
- );
- });
-
-add_task(async function test_pages() {
- for (const { cmd, uri, setup, isNewTab, testFun } of COMMANDS_TESTS) {
- info(`Testing ${cmd} command is triggered`);
- let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
-
- if (setup) {
- info("Setup");
- await setup();
- }
-
- let onLoad = isNewTab
- ? BrowserTestUtils.waitForNewTab(gBrowser, uri, true)
- : BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: cmd,
- });
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
- EventUtils.synthesizeKey("KEY_Enter", {}, window);
-
- const newTab = await onLoad;
-
- Assert.ok(
- await testFun(),
- `The command "${cmd}" passed completed its test`
- );
-
- if (isNewTab) {
- await BrowserTestUtils.removeTab(newTab);
- }
- await BrowserTestUtils.removeTab(tab);
- }
+ ActionsProviderQuickActions.removeAction("newtestaction");
});
-const assertActionButtonStatus = async (name, expectedEnabled, description) => {
- await BrowserTestUtils.waitForCondition(() =>
- window.document.querySelector(`[data-key=${name}]`)
- );
- const target = window.document.querySelector(`[data-key=${name}]`);
- Assert.equal(!target.hasAttribute("disabled"), expectedEnabled, description);
-};
-
add_task(async function test_viewsource() {
info("Check the button status of when the page is not web content");
const tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
- opening: "about:home",
+ opening: "https://example.com",
waitForLoad: true,
});
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "viewsource",
- });
- await assertActionButtonStatus(
- "viewsource",
- true,
- "Should be enabled even if the page is not web content"
- );
- info("Check the button status of when the page is web content");
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "http://example.com"
- );
- await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "viewsource",
});
- await assertActionButtonStatus(
- "viewsource",
- true,
- "Should be enabled on web content as well"
- );
info("Do view source action");
const onLoad = BrowserTestUtils.waitForNewTab(
gBrowser,
- "view-source:http://example.com/"
+ "view-source:https://example.com/"
);
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
const viewSourceTab = await onLoad;
@@ -551,7 +120,7 @@ add_task(async function test_viewsource() {
});
Assert.equal(
- await hasQuickActions(window),
+ hasQuickActions(window),
false,
"Result for quick actions is hidden"
);
@@ -575,7 +144,7 @@ async function doAlertDialogTest({ input, dialogContentURI }) {
},
});
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await onDialog;
@@ -601,16 +170,16 @@ add_task(async function test_clear() {
});
});
-async function doUpdateActionTest(isActiveExpected, description) {
+async function doUpdateActionTest(isActiveExpected) {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "update",
});
if (isActiveExpected) {
- await assertActionButtonStatus("update", isActiveExpected, description);
+ await assertAction("update");
} else {
- Assert.equal(await hasQuickActions(window), false, description);
+ Assert.equal(hasQuickActions(window), false, "No QuickActions were shown");
}
}
@@ -644,43 +213,6 @@ add_task(async function test_update() {
}
});
-async function hasQuickActions(win) {
- for (let i = 0, count = UrlbarTestUtils.getResultCount(win); i < count; i++) {
- const { result } = await UrlbarTestUtils.getDetailsOfResultAt(win, i);
- if (result.providerName === "quickactions") {
- return true;
- }
- }
- return false;
-}
-
-add_task(async function test_show_in_zero_prefix() {
- for (const minimumSearchString of [0, 3]) {
- info(
- `Test when quickactions.minimumSearchString pref is ${minimumSearchString}`
- );
- await SpecialPowers.pushPrefEnv({
- set: [
- [
- "browser.urlbar.quickactions.minimumSearchString",
- minimumSearchString,
- ],
- ],
- });
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "",
- });
-
- Assert.equal(
- await hasQuickActions(window),
- !minimumSearchString,
- "Result for quick actions is as expected"
- );
- await SpecialPowers.popPrefEnv();
- }
-});
-
add_task(async function test_whitespace() {
info("Test with quickactions.showInZeroPrefix pref is false");
await SpecialPowers.pushPrefEnv({
@@ -691,7 +223,7 @@ add_task(async function test_whitespace() {
value: " ",
});
Assert.equal(
- await hasQuickActions(window),
+ hasQuickActions(window),
false,
"Result for quick actions is not shown"
);
diff --git a/browser/components/urlbar/tests/browser/browser_quickactions_commands.js b/browser/components/urlbar/tests/browser/browser_quickactions_commands.js
new file mode 100644
index 0000000000..19b8d31ada
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_quickactions_commands.js
@@ -0,0 +1,154 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test QuickActions.
+ */
+
+"use strict";
+
+add_setup(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.urlbar.quickactions.enabled", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
+ ],
+ });
+});
+
+let COMMANDS_TESTS = [
+ {
+ cmd: "add-ons",
+ uri: "about:addons",
+ testFun: async () => isSelected("button[name=discover]"),
+ },
+ {
+ cmd: "plugins",
+ uri: "about:addons",
+ testFun: async () => isSelected("button[name=plugin]"),
+ },
+ {
+ cmd: "extensions",
+ uri: "about:addons",
+ testFun: async () => isSelected("button[name=extension]"),
+ },
+ {
+ cmd: "themes",
+ uri: "about:addons",
+ testFun: async () => isSelected("button[name=theme]"),
+ },
+ {
+ cmd: "add-ons",
+ setup: async () => {
+ const onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ "https://example.com/"
+ );
+ BrowserTestUtils.startLoadingURIString(
+ gBrowser.selectedBrowser,
+ "https://example.com/"
+ );
+ await onLoad;
+ },
+ uri: "about:addons",
+ isNewTab: true,
+ testFun: async () => isSelected("button[name=discover]"),
+ },
+ {
+ cmd: "plugins",
+ setup: async () => {
+ const onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ "https://example.com/"
+ );
+ BrowserTestUtils.startLoadingURIString(
+ gBrowser.selectedBrowser,
+ "https://example.com/"
+ );
+ await onLoad;
+ },
+ uri: "about:addons",
+ isNewTab: true,
+ testFun: async () => isSelected("button[name=plugin]"),
+ },
+ {
+ cmd: "extensions",
+ setup: async () => {
+ const onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ "https://example.com/"
+ );
+ BrowserTestUtils.startLoadingURIString(
+ gBrowser.selectedBrowser,
+ "https://example.com/"
+ );
+ await onLoad;
+ },
+ uri: "about:addons",
+ isNewTab: true,
+ testFun: async () => isSelected("button[name=extension]"),
+ },
+ {
+ cmd: "themes",
+ setup: async () => {
+ const onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ "https://example.com/"
+ );
+ BrowserTestUtils.startLoadingURIString(
+ gBrowser.selectedBrowser,
+ "https://example.com/"
+ );
+ await onLoad;
+ },
+ uri: "about:addons",
+ isNewTab: true,
+ testFun: async () => isSelected("button[name=theme]"),
+ },
+];
+
+let isSelected = async selector =>
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [selector], arg => {
+ return ContentTaskUtils.waitForCondition(() =>
+ content.document.querySelector(arg)?.hasAttribute("selected")
+ );
+ });
+
+add_task(async function test_pages() {
+ for (const { cmd, uri, setup, isNewTab, testFun } of COMMANDS_TESTS) {
+ info(`Testing ${cmd} command is triggered`);
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ if (setup) {
+ info("Setup");
+ await setup();
+ }
+
+ let onLoad = isNewTab
+ ? BrowserTestUtils.waitForNewTab(gBrowser, uri, true)
+ : BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: cmd,
+ });
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
+ EventUtils.synthesizeKey("KEY_Enter", {}, window);
+
+ const newTab = await onLoad;
+
+ Assert.ok(
+ await testFun(),
+ `The command "${cmd}" passed completed its test`
+ );
+
+ if (isNewTab) {
+ await BrowserTestUtils.removeTab(newTab);
+ }
+ await BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/urlbar/tests/browser/browser_quickactions_devtools.js b/browser/components/urlbar/tests/browser/browser_quickactions_devtools.js
index 1e1e92fb31..bee98d42af 100644
--- a/browser/components/urlbar/tests/browser/browser_quickactions_devtools.js
+++ b/browser/components/urlbar/tests/browser/browser_quickactions_devtools.js
@@ -17,7 +17,7 @@ add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.quickactions.enabled", true],
- ["browser.urlbar.suggest.quickactions", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
["browser.urlbar.shortcuts.quickactions", true],
],
});
@@ -25,21 +25,14 @@ add_setup(async function setup() {
const assertActionButtonStatus = async (name, expectedEnabled, description) => {
await BrowserTestUtils.waitForCondition(() =>
- window.document.querySelector(`[data-key=${name}]`)
+ window.document.querySelector(`[data-action=${name}]`)
);
- const target = window.document.querySelector(`[data-key=${name}]`);
+ const target = window.document.querySelector(`[data-action=${name}]`);
Assert.equal(!target.hasAttribute("disabled"), expectedEnabled, description);
};
-async function hasQuickActions(win) {
- for (let i = 0, count = UrlbarTestUtils.getResultCount(win); i < count; i++) {
- const { result } = await UrlbarTestUtils.getDetailsOfResultAt(win, i);
- if (result.providerName === "quickactions") {
- return true;
- }
- }
- return false;
-}
+const hasQuickActions = win =>
+ !!win.document.querySelector(".urlbarView-action-btn");
add_task(async function test_inspector() {
const testData = [
@@ -129,7 +122,7 @@ add_task(async function test_inspector() {
);
} else {
Assert.equal(
- await hasQuickActions(window),
+ hasQuickActions(window),
false,
"Result for quick actions is not shown since the inspector tool is disabled"
);
@@ -142,7 +135,7 @@ add_task(async function test_inspector() {
}
info("Do inspect action");
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await BrowserTestUtils.waitForCondition(
() => DevToolsShim.hasToolboxForTab(gBrowser.selectedTab),
@@ -160,7 +153,7 @@ add_task(async function test_inspector() {
value: "inspector",
});
Assert.equal(
- await hasQuickActions(window),
+ hasQuickActions(window),
false,
"Result for quick actions is not shown since the inspector is already opening"
);
diff --git a/browser/components/urlbar/tests/browser/browser_quickactions_screenshot.js b/browser/components/urlbar/tests/browser/browser_quickactions_screenshot.js
index c81442f0f5..c83fdff441 100644
--- a/browser/components/urlbar/tests/browser/browser_quickactions_screenshot.js
+++ b/browser/components/urlbar/tests/browser/browser_quickactions_screenshot.js
@@ -34,11 +34,18 @@ async function clickQuickActionOneoffButton() {
});
}
+const assertAction = async name => {
+ await BrowserTestUtils.waitForCondition(() =>
+ window.document.querySelector(`.urlbarView-action-btn[data-action=${name}]`)
+ );
+ Assert.ok(true, `We found action "${name}`);
+};
+
add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.quickactions.enabled", true],
- ["browser.urlbar.suggest.quickactions", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
["browser.urlbar.shortcuts.quickactions", true],
],
});
@@ -61,17 +68,10 @@ add_task(async function test_screenshot() {
window,
value: "screenshot",
});
- Assert.equal(
- UrlbarTestUtils.getResultCount(window),
- 2,
- "We matched the action"
- );
- let { result } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
- Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.DYNAMIC);
- Assert.equal(result.providerName, "quickactions");
+ await assertAction("screenshot");
info("Trigger the screenshot mode");
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await TestUtils.waitForCondition(
isScreenshotInitialized,
@@ -104,38 +104,6 @@ add_task(async function search_mode_on_webpage() {
});
await UrlbarTestUtils.promiseSearchComplete(window);
- info("Enter quick action search mode");
- await clickQuickActionOneoffButton();
- await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
- Assert.ok(true, "Actions are shown when we enter actions search mode.");
-
- info("Trigger the screenshot mode");
- const initialActionButtons = window.document.querySelectorAll(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- );
- let screenshotButton;
- for (let i = 0; i < initialActionButtons.length; i++) {
- const item = initialActionButtons.item(i);
- if (item.dataset.key === "screenshot") {
- screenshotButton = item;
- break;
- }
- }
- EventUtils.synthesizeMouseAtCenter(screenshotButton, {}, window);
- await TestUtils.waitForCondition(
- isScreenshotInitialized,
- "Screenshot component is active",
- 200,
- 100
- );
-
- info("Press Escape to exit screenshot mode");
- EventUtils.synthesizeKey("KEY_Escape", {}, window);
- await TestUtils.waitForCondition(
- async () => !(await isScreenshotInitialized()),
- "Screenshot component has been dismissed"
- );
-
info("Check the urlbar state");
Assert.equal(gURLBar.value, UrlbarTestUtils.trimURL("https://example.com"));
Assert.equal(gURLBar.getAttribute("pageproxystate"), "valid");
@@ -146,23 +114,7 @@ add_task(async function search_mode_on_webpage() {
});
await UrlbarTestUtils.promiseSearchComplete(window);
- info("Enter quick action search mode again");
- await clickQuickActionOneoffButton();
- await UrlbarTestUtils.waitForAutocompleteResultAt(window, 0);
- const finalActionButtons = window.document.querySelectorAll(
- ".urlbarView-row[dynamicType=quickactions] .urlbarView-quickaction-button"
- );
-
- info("Check the action buttons and the urlbar");
- Assert.equal(
- finalActionButtons.length,
- initialActionButtons.length,
- "The same buttons as initially displayed will display"
- );
- Assert.equal(gURLBar.value, "");
-
info("Clean up");
- await UrlbarTestUtils.exitSearchMode(window);
await UrlbarTestUtils.promisePopupClose(window);
EventUtils.synthesizeKey("KEY_Escape");
BrowserTestUtils.removeTab(tab);
diff --git a/browser/components/urlbar/tests/browser/browser_quickactions_tab_refocus.js b/browser/components/urlbar/tests/browser/browser_quickactions_tab_refocus.js
index abac861931..ee1f3cab62 100644
--- a/browser/components/urlbar/tests/browser/browser_quickactions_tab_refocus.js
+++ b/browser/components/urlbar/tests/browser/browser_quickactions_tab_refocus.js
@@ -13,7 +13,7 @@ add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.quickactions.enabled", true],
- ["browser.urlbar.suggest.quickactions", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
["browser.urlbar.shortcuts.quickactions", true],
],
});
@@ -90,7 +90,7 @@ add_task(async function test_about_pages() {
window,
value: firstInput,
});
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
}
await onLoad;
@@ -106,7 +106,7 @@ add_task(async function test_about_pages() {
window,
value: secondInput || firstInput,
});
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
Assert.equal(
gBrowser.selectedTab,
@@ -158,7 +158,7 @@ add_task(async function test_about_addons_pages() {
window,
value: cmd,
});
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
Assert.ok(await testFun(), "The page content is correct");
@@ -175,7 +175,7 @@ add_task(async function test_about_addons_pages() {
window,
value: cmd,
});
- EventUtils.synthesizeKey("KEY_ArrowDown", {}, window);
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await BrowserTestUtils.waitForCondition(() => testFun());
Assert.ok(true, "The tab correspondent action is selected");
diff --git a/browser/components/urlbar/tests/browser/browser_secondaryActions.js b/browser/components/urlbar/tests/browser/browser_secondaryActions.js
new file mode 100644
index 0000000000..7e03ae036c
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_secondaryActions.js
@@ -0,0 +1,141 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Basic tests for secondary Actions.
+ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ ActionsProviderQuickActions:
+ "resource:///modules/ActionsProviderQuickActions.sys.mjs",
+});
+
+let testActionCalled = 0;
+
+let loadURI = async (browser, uri) => {
+ let onLoaded = BrowserTestUtils.browserLoaded(browser, false, uri);
+ BrowserTestUtils.startLoadingURIString(browser, uri);
+ return onLoaded;
+};
+
+add_setup(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.urlbar.quickactions.enabled", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
+ ["browser.urlbar.contextualSearch.enabled", true],
+ ],
+ });
+
+ ActionsProviderQuickActions.addAction("testaction", {
+ commands: ["testaction"],
+ actionKey: "testaction",
+ label: "quickactions-downloads2",
+ onPick: () => testActionCalled++,
+ });
+
+ registerCleanupFunction(() => {
+ ActionsProviderQuickActions.removeAction("testaction");
+ });
+});
+
+add_task(async function test_quickaction() {
+ info("Match an installed quickaction and trigger it via tab");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "testact",
+ });
+
+ Assert.equal(
+ UrlbarTestUtils.getResultCount(window),
+ 1,
+ "We matched the action"
+ );
+
+ info("The callback of the action is fired when selected");
+ EventUtils.synthesizeKey("KEY_Tab", {}, window);
+ EventUtils.synthesizeKey("KEY_Enter", {}, window);
+ Assert.equal(testActionCalled, 1, "Test action was called");
+});
+
+add_task(async function test_switchtab() {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ await loadURI(win.gBrowser, "https://example.com/");
+
+ info("Open a new tab, type in the urlbar and switch to previous tab");
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser);
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window: win,
+ value: "example",
+ });
+
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ EventUtils.synthesizeKey("KEY_Tab", {}, win);
+ EventUtils.synthesizeKey("KEY_Enter", {}, win);
+
+ is(win.gBrowser.tabs.length, 1, "We switched to previous tab");
+ is(
+ win.gBrowser.currentURI.spec,
+ "https://example.com/",
+ "We switched to previous tab"
+ );
+
+ info(
+ "Open a new tab, type in the urlbar, select result and open url in current tab"
+ );
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser);
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window: win,
+ value: "example",
+ });
+
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ EventUtils.synthesizeKey("KEY_Enter", {}, win);
+ await BrowserTestUtils.browserLoaded(win.gBrowser);
+ is(win.gBrowser.tabs.length, 2, "We switched to previous tab");
+ is(
+ win.gBrowser.currentURI.spec,
+ "https://example.com/",
+ "We opened in current tab"
+ );
+
+ BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_sitesearch() {
+ await SearchTestUtils.installSearchExtension({
+ name: "Contextual",
+ search_url: "https://example.com/browser",
+ });
+
+ let ENGINE_TEST_URL = "https://example.com/";
+ await loadURI(gBrowser.selectedBrowser, ENGINE_TEST_URL);
+
+ const query = "search";
+ let engine = Services.search.getEngineByName("Contextual");
+ const [expectedUrl] = UrlbarUtils.getSearchQueryUrl(engine, query);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "sea",
+ });
+
+ let onLoad = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ expectedUrl
+ );
+ gURLBar.value = query;
+ UrlbarTestUtils.fireInputEvent(window);
+ EventUtils.synthesizeKey("KEY_Tab");
+ EventUtils.synthesizeKey("KEY_Enter");
+ await onLoad;
+
+ Assert.equal(
+ gBrowser.selectedBrowser.currentURI.spec,
+ expectedUrl,
+ "Selecting the contextual search result opens the search URL"
+ );
+});
diff --git a/browser/components/urlbar/tests/browser/browser_strip_on_share.js b/browser/components/urlbar/tests/browser/browser_strip_on_share.js
index 9e045cee9c..ec3f96425a 100644
--- a/browser/components/urlbar/tests/browser/browser_strip_on_share.js
+++ b/browser/components/urlbar/tests/browser/browser_strip_on_share.js
@@ -101,6 +101,32 @@ add_task(async function testQueryParamIsNotStrippedForWrongSiteSpecific() {
});
});
+// Ensuring site specific parameters are stripped regardless
+// of capitalization in the URI
+add_task(async function testQueryParamIsStrippedWhenParamIsCapitalized() {
+ let originalUrl = "https://www.example.com/?TEST_1=1234";
+ let shortenedUrl = "https://www.example.com/";
+ await testMenuItemEnabled({
+ selectWholeUrl: true,
+ validUrl: originalUrl,
+ strippedUrl: shortenedUrl,
+ useTestList: true,
+ });
+});
+
+// Ensuring site specific parameters are stripped regardless
+// of capitalization in the URI
+add_task(async function testQueryParamIsStrippedWhenParamIsCapitalized() {
+ let originalUrl = "https://www.example.com/?test_5=1234";
+ let shortenedUrl = "https://www.example.com/";
+ await testMenuItemEnabled({
+ selectWholeUrl: true,
+ validUrl: originalUrl,
+ strippedUrl: shortenedUrl,
+ useTestList: true,
+ });
+});
+
/**
* Opens a new tab, opens the ulr bar context menu and checks that the strip-on-share menu item is not visible
*
@@ -163,7 +189,7 @@ async function testMenuItemEnabled({
topLevelSites: ["*"],
},
example: {
- queryParams: ["test_2", "test_1"],
+ queryParams: ["test_2", "test_1", "TEST_5"],
topLevelSites: ["www.example.com"],
},
exampleNet: {
diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_quickactions.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_quickactions.js
deleted file mode 100644
index b29807900b..0000000000
--- a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_quickactions.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-/**
- * This file tests urlbar telemetry for quickactions.
- */
-
-"use strict";
-
-ChromeUtils.defineESModuleGetters(this, {
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
-});
-
-let testActionCalled = 0;
-
-add_setup(async function setup() {
- await SpecialPowers.pushPrefEnv({
- set: [
- ["browser.urlbar.suggest.quickactions", true],
- ["browser.urlbar.quickactions.enabled", true],
- ],
- });
-
- UrlbarProviderQuickActions.addAction("testaction", {
- commands: ["testaction"],
- label: "quickactions-downloads2",
- onPick: () => testActionCalled++,
- });
-
- registerCleanupFunction(() => {
- UrlbarProviderQuickActions.removeAction("testaction");
- });
-});
-
-add_task(async function test() {
- const histograms = snapshotHistograms();
-
- // Do a search to show the quickaction.
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "testaction",
- waitForFocus,
- fireInputEvent: true,
- });
-
- await UrlbarTestUtils.promisePopupClose(window, () => {
- EventUtils.synthesizeKey("KEY_ArrowDown");
- EventUtils.synthesizeKey("KEY_Enter");
- });
-
- Assert.equal(testActionCalled, 1, "Test action was called");
-
- TelemetryTestUtils.assertHistogram(
- histograms.resultMethodHist,
- UrlbarTestUtils.SELECTED_RESULT_METHODS.arrowEnterSelection,
- 1
- );
-
- let scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
- TelemetryTestUtils.assertKeyedScalar(
- scalars,
- `urlbar.picked.quickaction`,
- 1,
- 1
- );
-
- TelemetryTestUtils.assertKeyedScalar(
- scalars,
- "quickaction.picked",
- "testaction-10",
- 1
- );
-
- TelemetryTestUtils.assertKeyedScalar(
- scalars,
- "quickaction.impression",
- "testaction-10",
- 1
- );
-
- // Clean up for subsequent tests.
- gURLBar.handleRevert();
-});
-
-add_task(async function test_impressions() {
- UrlbarProviderQuickActions.addAction("testaction2", {
- commands: ["testaction2"],
- label: "quickactions-downloads2",
- onPick: () => {},
- });
-
- await UrlbarTestUtils.promiseAutocompleteResultPopup({
- window,
- value: "testaction",
- waitForFocus,
- fireInputEvent: true,
- });
-
- await UrlbarTestUtils.promisePopupClose(window, () => {
- EventUtils.synthesizeKey("KEY_ArrowDown");
- EventUtils.synthesizeKey("KEY_Enter");
- });
-
- let scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
-
- TelemetryTestUtils.assertKeyedScalar(
- scalars,
- "quickaction.impression",
- `testaction-10`,
- 1
- );
- TelemetryTestUtils.assertKeyedScalar(
- scalars,
- "quickaction.impression",
- `testaction2-10`,
- 1
- );
-
- UrlbarProviderQuickActions.removeAction("testaction2");
- gURLBar.handleRevert();
-});
-
-function snapshotHistograms() {
- Services.telemetry.clearScalars();
- Services.telemetry.clearEvents();
- return {
- resultMethodHist: TelemetryTestUtils.getAndClearHistogram(
- "FX_URLBAR_SELECTED_RESULT_METHOD"
- ),
- };
-}
diff --git a/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js b/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
index 10110a8928..0625a3397f 100644
--- a/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
+++ b/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
@@ -6,16 +6,16 @@
// Test selection on result view by mouse.
ChromeUtils.defineESModuleGetters(this, {
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
+ ActionsProviderQuickActions:
+ "resource:///modules/ActionsProviderQuickActions.sys.mjs",
});
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
+ ["browser.urlbar.secondaryActions.featureGate", true],
["browser.urlbar.quickactions.enabled", true],
["browser.urlbar.suggest.quickactions", true],
- ["browser.urlbar.shortcuts.quickactions", true],
],
});
@@ -23,7 +23,7 @@ add_setup(async function () {
await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
- UrlbarProviderQuickActions.addAction("test-addons", {
+ ActionsProviderQuickActions.addAction("test-addons", {
commands: ["test-addons"],
label: "quickactions-addons",
onPick: () =>
@@ -32,7 +32,7 @@ add_setup(async function () {
"about:about"
),
});
- UrlbarProviderQuickActions.addAction("test-downloads", {
+ ActionsProviderQuickActions.addAction("test-downloads", {
commands: ["test-downloads"],
label: "quickactions-downloads2",
onPick: () =>
@@ -43,40 +43,22 @@ add_setup(async function () {
});
registerCleanupFunction(function () {
- UrlbarProviderQuickActions.removeAction("test-addons");
- UrlbarProviderQuickActions.removeAction("test-downloads");
+ ActionsProviderQuickActions.removeAction("test-addons");
+ ActionsProviderQuickActions.removeAction("test-downloads");
});
});
add_task(async function basic() {
const testData = [
{
- description: "Normal result to quick action button",
- mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
- mouseup: ".urlbarView-quickaction-button[data-key=test-downloads]",
- expected: "about:downloads",
- },
- {
- description: "Normal result to out of result",
- mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
- mouseup: "body",
- expected: false,
- },
- {
description: "Quick action button to normal result",
- mousedown: ".urlbarView-quickaction-button[data-key=test-addons]",
+ mousedown: ".urlbarView-action-btn[data-action=test-addons]",
mouseup: ".urlbarView-row:nth-child(1)",
expected: "https://example.com/?q=test",
},
{
- description: "Quick action button to quick action button",
- mousedown: ".urlbarView-quickaction-button[data-key=test-addons]",
- mouseup: ".urlbarView-quickaction-button[data-key=test-downloads]",
- expected: "about:downloads",
- },
- {
description: "Quick action button to out of result",
- mousedown: ".urlbarView-quickaction-button[data-key=test-downloads]",
+ mousedown: ".urlbarView-action-btn[data-action=test-addons]",
mouseup: "body",
expected: false,
},
@@ -148,7 +130,7 @@ add_task(async function outOfBrowser() {
},
{
description: "Quick action button to out of browser",
- mousedown: ".urlbarView-quickaction-button[data-key=test-addons]",
+ mousedown: ".urlbarView-action-btn[data-action=test-addons]",
},
];
@@ -204,22 +186,22 @@ add_task(async function withSelectionByKeyboard() {
mouseup: "body",
expected: {
selectedElementByKey:
- "#urlbar-results .urlbarView-quickaction-button[selected]",
+ "#urlbar-results .urlbarView-action-btn[selected]",
selectedElementAfterMouseDown:
- "#urlbar-results .urlbarView-quickaction-button[selected]",
+ "#urlbar-results .urlbarView-action-btn[selected]",
actionedPage: false,
},
},
{
- description: "Select normal result, then click on about:downloads",
- mousedown: ".urlbarView-quickaction-button[data-key=test-downloads]",
- mouseup: ".urlbarView-quickaction-button[data-key=test-downloads]",
+ description: "Select normal result, then click on about:addons",
+ mousedown: ".urlbarView-action-btn[data-action=test-addons]",
+ mouseup: ".urlbarView-action-btn[data-action=test-addons]",
expected: {
selectedElementByKey:
"#urlbar-results .urlbarView-row > .urlbarView-row-inner[selected]",
selectedElementAfterMouseDown:
- ".urlbarView-quickaction-button[data-key=test-downloads]",
- actionedPage: "about:downloads",
+ ".urlbarView-action-btn[data-action=test-addons]",
+ actionedPage: "about:about",
},
},
];
@@ -244,11 +226,7 @@ add_task(async function withSelectionByKeyboard() {
]);
if (arrowDown) {
- EventUtils.synthesizeKey(
- "KEY_ArrowDown",
- { repeat: arrowDown },
- window
- );
+ EventUtils.synthesizeKey("KEY_Tab", { repeat: arrowDown }, window);
}
let [selectedElementByKey] = await waitForElements([
diff --git a/browser/components/urlbar/tests/browser/head.js b/browser/components/urlbar/tests/browser/head.js
index f78624e68e..73f9fda3a9 100644
--- a/browser/components/urlbar/tests/browser/head.js
+++ b/browser/components/urlbar/tests/browser/head.js
@@ -161,7 +161,7 @@ async function search({
// Set the input value and move the caret to the end to simulate the user
// typing. It's important the caret is at the end because otherwise autofill
// won't happen.
- gURLBar.value = searchString;
+ gURLBar._setValue(searchString, { allowTrim: false });
gURLBar.inputField.setSelectionRange(
searchString.length,
searchString.length
@@ -173,28 +173,21 @@ async function search({
// autofill before the search completes.
UrlbarTestUtils.fireInputEvent(window);
- // Subtract the protocol length, when the searchString contains the https://
- // protocol and trimHttps is enabled.
- let trimmedProtocolWSlashes = UrlbarTestUtils.getTrimmedProtocolWithSlashes();
- let selectionOffset = searchString.includes(trimmedProtocolWSlashes)
- ? trimmedProtocolWSlashes.length
- : 0;
-
// Check the input value and selection immediately, before waiting on the
// search to complete.
Assert.equal(
gURLBar.value,
- UrlbarTestUtils.trimURL(valueBefore),
+ valueBefore,
"gURLBar.value before the search completes"
);
Assert.equal(
gURLBar.selectionStart,
- searchString.length - selectionOffset,
+ searchString.length,
"gURLBar.selectionStart before the search completes"
);
Assert.equal(
gURLBar.selectionEnd,
- valueBefore.length - selectionOffset,
+ valueBefore.length,
"gURLBar.selectionEnd before the search completes"
);
@@ -205,17 +198,17 @@ async function search({
// Check the final value after the results arrived.
Assert.equal(
gURLBar.value,
- UrlbarTestUtils.trimURL(valueAfter),
+ valueAfter,
"gURLBar.value after the search completes"
);
Assert.equal(
gURLBar.selectionStart,
- searchString.length - selectionOffset,
+ searchString.length,
"gURLBar.selectionStart after the search completes"
);
Assert.equal(
gURLBar.selectionEnd,
- valueAfter.length - selectionOffset,
+ valueAfter.length,
"gURLBar.selectionEnd after the search completes"
);
@@ -227,7 +220,7 @@ async function search({
);
Assert.strictEqual(
gURLBar._autofillPlaceholder.value,
- UrlbarTestUtils.trimURL(placeholderAfter),
+ placeholderAfter,
"gURLBar._autofillPlaceholder.value after the search completes"
);
} else {
@@ -246,3 +239,51 @@ async function search({
"First result is an autofill result iff a placeholder is expected"
);
}
+
+function selectWithMouseDrag(fromX, toX, win = window) {
+ let target = win.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, win = window) {
+ let target = win.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/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js
index ce69d30517..f930b28f59 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js
@@ -59,9 +59,9 @@ add_task(async function recent_search() {
assert: () =>
assertAbandonmentTelemetry([
{
- groups: "recent_search,suggested_index",
- results: "recent_search,action",
- n_results: 2,
+ groups: "recent_search",
+ results: "recent_search",
+ n_results: 1,
},
]),
});
@@ -114,9 +114,9 @@ add_task(async function top_site() {
assert: () =>
assertAbandonmentTelemetry([
{
- groups: "top_site,suggested_index",
- results: "top_site,action",
- n_results: 2,
+ groups: "top_site",
+ results: "top_site",
+ n_results: 1,
},
]),
});
@@ -128,9 +128,9 @@ add_task(async function clipboard() {
assert: () =>
assertAbandonmentTelemetry([
{
- groups: "general,suggested_index",
- results: "clipboard,action",
- n_results: 2,
+ groups: "general",
+ results: "clipboard",
+ n_results: 1,
},
]),
});
@@ -170,9 +170,9 @@ add_task(async function general() {
assert: () =>
assertAbandonmentTelemetry([
{
- groups: "heuristic,suggested_index,general",
- results: "search_engine,action,bookmark",
- n_results: 3,
+ groups: "heuristic,general",
+ results: "search_engine,bookmark",
+ n_results: 2,
},
]),
});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js
index 7edcc47a30..45f4b79e7c 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_search_mode.js
@@ -45,10 +45,3 @@ add_task(async function tabs() {
assert: () => assertAbandonmentTelemetry([{ search_mode: "tabs" }]),
});
});
-
-add_task(async function actions() {
- await doActionsTest({
- trigger: () => doBlur(),
- assert: () => assertAbandonmentTelemetry([{ search_mode: "actions" }]),
- });
-});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js
index 04ef7e9757..1668470714 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_edge_cases.js
@@ -196,7 +196,6 @@ add_task(async function enter_to_reload_current_url() {
await BrowserTestUtils.waitForCondition(
() => window.document.activeElement === gURLBar.inputField
);
- await UrlbarTestUtils.promiseSearchComplete(window);
// Press Enter key to reload the page without selecting any suggestions.
await doEnter();
@@ -213,8 +212,8 @@ add_task(async function enter_to_reload_current_url() {
selected_result: "input_field",
selected_result_subtype: "",
provider: undefined,
- results: "action",
- groups: "suggested_index",
+ results: "",
+ groups: "",
},
]);
});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js
index d46c874403..a0ef61dd19 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js
@@ -59,9 +59,9 @@ add_task(async function recent_search() {
assert: () =>
assertEngagementTelemetry([
{
- groups: "recent_search,suggested_index",
- results: "recent_search,action",
- n_results: 2,
+ groups: "recent_search",
+ results: "recent_search",
+ n_results: 1,
},
]),
});
@@ -114,9 +114,9 @@ add_task(async function top_site() {
assert: () =>
assertEngagementTelemetry([
{
- groups: "top_site,suggested_index",
- results: "top_site,action",
- n_results: 2,
+ groups: "top_site",
+ results: "top_site",
+ n_results: 1,
},
]),
});
@@ -128,9 +128,9 @@ add_task(async function clipboard() {
assert: () =>
assertEngagementTelemetry([
{
- groups: "general,suggested_index",
- results: "clipboard,action",
- n_results: 2,
+ groups: "general",
+ results: "clipboard",
+ n_results: 1,
},
]),
});
@@ -170,9 +170,9 @@ add_task(async function general() {
assert: () =>
assertEngagementTelemetry([
{
- groups: "heuristic,suggested_index,general",
- results: "search_engine,action,bookmark",
- n_results: 3,
+ groups: "heuristic,general",
+ results: "search_engine,bookmark",
+ n_results: 2,
},
]),
});
@@ -255,6 +255,7 @@ add_task(async function always_empty_if_drop_go() {
await doTest(async () => {
// Open the results view once.
+ await addTopSites("https://example.com/");
await showResultByArrowDown();
await UrlbarTestUtils.promisePopupClose(window);
@@ -282,6 +283,7 @@ add_task(async function always_empty_if_paste_go() {
await doTest(async () => {
// Open the results view once.
+ await addTopSites("https://example.com/");
await showResultByArrowDown();
await UrlbarTestUtils.promisePopupClose(window);
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js
index 2866186c30..d2deacf597 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_interaction.js
@@ -42,6 +42,7 @@ add_task(async function dropped() {
});
await doTest(async () => {
+ await addTopSites("https://example.com/");
await showResultByArrowDown();
await doDropAndGo("example.com");
@@ -67,6 +68,7 @@ add_task(async function pasted() {
});
await doTest(async () => {
+ await addTopSites("https://example.com/");
await showResultByArrowDown();
await doPasteAndGo("www.example.com");
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js
index 013bef1904..9227b81fd0 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_search_mode.js
@@ -42,6 +42,7 @@ add_task(async function tabs() {
await doTabTest({
trigger: async () => {
const currentTab = gBrowser.selectedTab;
+ EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Enter");
await BrowserTestUtils.waitForCondition(
() => gBrowser.selectedTab !== currentTab
@@ -50,14 +51,3 @@ add_task(async function tabs() {
assert: () => assertEngagementTelemetry([{ search_mode: "tabs" }]),
});
});
-
-add_task(async function actions() {
- await doActionsTest({
- trigger: async () => {
- const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
- doClickSubButton(".urlbarView-quickaction-button[data-key=addons]");
- await onLoad;
- },
- assert: () => assertEngagementTelemetry([{ search_mode: "actions" }]),
- });
-});
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js
index bea266dbf4..34083e4369 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js
@@ -119,9 +119,9 @@ add_task(async function selected_result_bookmark() {
{
selected_result: "bookmark",
selected_result_subtype: "",
- selected_position: 3,
+ selected_position: 2,
provider: "Places",
- results: "search_engine,action,bookmark",
+ results: "search_engine,bookmark",
},
]);
});
@@ -267,27 +267,11 @@ add_task(async function selected_result_url() {
});
});
-add_task(async function selected_result_action() {
- await doTest(async () => {
- await showResultByArrowDown();
- await selectRowByProvider("quickactions");
- const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
- doClickSubButton(".urlbarView-quickaction-button[data-key=addons]");
- await onLoad;
-
- assertEngagementTelemetry([
- {
- selected_result: "action",
- selected_result_subtype: "addons",
- selected_position: 1,
- provider: "quickactions",
- results: "action",
- },
- ]);
+add_task(async function selected_result_tab() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.secondaryActions.featureGate", false]],
});
-});
-add_task(async function selected_result_tab() {
const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/");
await doTest(async () => {
@@ -307,6 +291,7 @@ add_task(async function selected_result_tab() {
]);
});
+ await SpecialPowers.popPrefEnv();
BrowserTestUtils.removeTab(tab);
});
@@ -402,7 +387,7 @@ add_task(async function selected_result_top_site() {
selected_result_subtype: "",
selected_position: 1,
provider: "UrlbarProviderTopSites",
- results: "top_site,action",
+ results: "top_site",
},
]);
});
@@ -456,7 +441,7 @@ add_task(async function selected_result_clipboard() {
selected_result_subtype: "",
selected_position: 1,
provider: "UrlbarProviderClipboard",
- results: "clipboard,action",
+ results: "clipboard",
},
]);
});
@@ -492,50 +477,6 @@ add_task(async function selected_result_unit() {
await SpecialPowers.popPrefEnv();
});
-add_task(async function selected_result_site_specific_contextual_search() {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.urlbar.contextualSearch.enabled", true]],
- });
-
- await doTest(async () => {
- const extension = await SearchTestUtils.installSearchExtension(
- {
- name: "Contextual",
- search_url: "https://example.com/browser",
- },
- { skipUnload: true }
- );
- const onLoaded = BrowserTestUtils.browserLoaded(
- gBrowser.selectedBrowser,
- false,
- "https://example.com/"
- );
- BrowserTestUtils.startLoadingURIString(
- gBrowser.selectedBrowser,
- "https://example.com/"
- );
- await onLoaded;
-
- await openPopup("search");
- await selectRowByProvider("UrlbarProviderContextualSearch");
- await doEnter();
-
- assertEngagementTelemetry([
- {
- selected_result: "site_specific_contextual_search",
- selected_result_subtype: "",
- selected_position: 2,
- provider: "UrlbarProviderContextualSearch",
- results: "search_engine,site_specific_contextual_search",
- },
- ]);
-
- await extension.unload();
- });
-
- await SpecialPowers.popPrefEnv();
-});
-
add_task(async function selected_result_rs_adm_sponsored() {
const cleanupQuickSuggest = await ensureQuickSuggestInit({
prefs: [["quicksuggest.rustEnabled", false]],
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js
index 58c55b416f..dffdebad97 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js
@@ -130,6 +130,7 @@ async function doTypedTest({ trigger, assert }) {
async function doTypedWithResultsPopupTest({ trigger, assert }) {
await doTest(async () => {
+ await addTopSites("https://example.org/");
await showResultByArrowDown();
EventUtils.synthesizeKey("x");
await UrlbarTestUtils.promiseSearchComplete(window);
@@ -150,6 +151,7 @@ async function doPastedTest({ trigger, assert }) {
async function doPastedWithResultsPopupTest({ trigger, assert }) {
await doTest(async () => {
+ await addTopSites("https://example.org/");
await showResultByArrowDown();
await doPaste("x");
@@ -257,6 +259,7 @@ async function doPersistedSearchTermsRestartedRefinedTest({
for (const { firstInput, secondInput, expected } of testData) {
await doTest(async () => {
+ await addTopSites("https://example.com/");
await openPopup(firstInput);
await doEnter();
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js
index 86151e1ba3..25a6504109 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js
@@ -78,16 +78,3 @@ async function doTabTest({ trigger, assert }) {
BrowserTestUtils.removeTab(tab);
}
-
-async function doActionsTest({ trigger, assert }) {
- await doTest(async () => {
- await openPopup("add");
- await UrlbarTestUtils.enterSearchMode(window, {
- source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
- });
- await selectRowByProvider("quickactions");
-
- await trigger();
- await assert();
- });
-}
diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
index 1373cc7e27..a3b2b020c0 100644
--- a/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
+++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head.js
@@ -406,9 +406,7 @@ async function setup() {
set: [
["browser.urlbar.searchEngagementTelemetry.enabled", true],
["browser.urlbar.quickactions.enabled", true],
- ["browser.urlbar.quickactions.minimumSearchString", 0],
- ["browser.urlbar.suggest.quickactions", true],
- ["browser.urlbar.shortcuts.quickactions", true],
+ ["browser.urlbar.secondaryActions.featureGate", true],
],
});
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
index 98f6ba6117..87f94a641b 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest.js
@@ -34,6 +34,27 @@ const REMOTE_SETTINGS_RESULTS = [
},
];
+const MERINO_NAVIGATIONAL_SUGGESTION = {
+ url: "https://example.com/navigational-suggestion",
+ title: "Navigational suggestion",
+ provider: "top_picks",
+ is_sponsored: false,
+ score: 0.25,
+ block_id: 0,
+ is_top_pick: true,
+};
+
+const MERINO_DYNAMIC_WIKIPEDIA_SUGGESTION = {
+ url: "https://example.com/dynamic-wikipedia",
+ title: "Dynamic Wikipedia suggestion",
+ click_url: "https://example.com/click",
+ impression_url: "https://example.com/impression",
+ advertiser: "dynamic-wikipedia",
+ provider: "wikipedia",
+ iab_category: "5 - Education",
+ block_id: 1,
+};
+
add_setup(async function () {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
@@ -46,7 +67,11 @@ add_setup(async function () {
attachment: REMOTE_SETTINGS_RESULTS,
},
],
+ merinoSuggestions: [],
});
+
+ // Disable Merino so we trigger only remote settings suggestions.
+ UrlbarPrefs.set("quicksuggest.dataCollection.enabled", false);
});
// Tests a sponsored result and keyword highlighting.
@@ -167,30 +192,9 @@ add_tasks_with_rust(
// 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);
+ await doManageTest({
+ input: "fra",
+ index: 1,
});
});
@@ -201,3 +205,35 @@ add_tasks_with_rust(async function resultMenu_manage_nonSponsored() {
index: 1,
});
});
+
+// Tests the "Manage" result menu for Navigational suggestion.
+add_tasks_with_rust(async function resultMenu_manage_navigational() {
+ // Enable Merino.
+ UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
+ MerinoTestUtils.server.response.body.suggestions = [
+ MERINO_NAVIGATIONAL_SUGGESTION,
+ ];
+
+ await doManageTest({
+ input: "test",
+ index: 1,
+ });
+
+ UrlbarPrefs.clear("quicksuggest.dataCollection.enabled");
+});
+
+// Tests the "Manage" result menu for Dynamic Wikipedia suggestion.
+add_tasks_with_rust(async function resultMenu_manage_dynamicWikipedia() {
+ // Enable Merino.
+ UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
+ MerinoTestUtils.server.response.body.suggestions = [
+ MERINO_DYNAMIC_WIKIPEDIA_SUGGESTION,
+ ];
+
+ await doManageTest({
+ input: "test",
+ index: 1,
+ });
+
+ UrlbarPrefs.clear("quicksuggest.dataCollection.enabled");
+});
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 71c289e0ef..c659eee268 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_dynamicWikipedia.js
@@ -88,24 +88,6 @@ add_task(async function () {
},
},
},
- // help
- {
- command: "help",
- scalars: {
- [TELEMETRY_SCALARS.IMPRESSION_DYNAMIC_WIKIPEDIA]: position,
- [TELEMETRY_SCALARS.HELP_DYNAMIC_WIKIPEDIA]: position,
- },
- event: {
- category: QuickSuggest.TELEMETRY_EVENT_CATEGORY,
- method: "engagement",
- object: "help",
- extra: {
- suggestion_type,
- match_type,
- position: position.toString(),
- },
- },
- },
],
});
});
diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_weather.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_weather.js
index e87c64740f..90044a95bd 100644
--- a/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_weather.js
+++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_telemetry_weather.js
@@ -121,24 +121,6 @@ add_task(async function () {
},
},
},
- // help
- {
- command: "help",
- scalars: {
- [WEATHER_SCALARS.IMPRESSION]: position,
- [WEATHER_SCALARS.HELP]: position,
- },
- event: {
- category: QuickSuggest.TELEMETRY_EVENT_CATEGORY,
- method: "engagement",
- object: "help",
- extra: {
- suggestion_type,
- match_type,
- position: position.toString(),
- },
- },
- },
],
});
});
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_dynamicWikipedia.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_dynamicWikipedia.js
index a9f339c324..7fc687d3df 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_dynamicWikipedia.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_dynamicWikipedia.js
@@ -90,14 +90,11 @@ function makeExpectedResult() {
qsSuggestion: "full_keyword",
source: "merino",
provider: "wikipedia",
- 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,
},
};
}
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merino.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merino.js
index 64f4991236..b88e14e0e0 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merino.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_merino.js
@@ -549,14 +549,11 @@ add_task(async function bestMatch() {
url: "url",
icon: null,
qsSuggestion: "full_keyword",
- 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,
displayUrl: "url",
source: "merino",
provider: "some_top_pick_provider",
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js
index 224dd6cb22..89fefd3163 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js
@@ -657,14 +657,11 @@ function makeExpectedDefaultResult({ suggestion }) {
? { id: "urlbar-result-action-sponsored" }
: undefined,
shouldShowUrl: true,
- 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,
},
};
}
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_topPicks.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_topPicks.js
index 1b8da54920..468cedbe0b 100644
--- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_topPicks.js
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_topPicks.js
@@ -175,14 +175,11 @@ function makeExpectedResult({
shouldShowUrl: true,
source: "merino",
provider: telemetryType,
- 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,
},
};
if (typeof dupedHeuristic == "boolean") {
diff --git a/browser/components/urlbar/tests/unit/test_autofill_origins.js b/browser/components/urlbar/tests/unit/test_autofill_origins.js
index 33e462a8af..454881b933 100644
--- a/browser/components/urlbar/tests/unit/test_autofill_origins.js
+++ b/browser/components/urlbar/tests/unit/test_autofill_origins.js
@@ -1039,3 +1039,68 @@ async function doTitleTest({ visits, input, expected }) {
await cleanup();
}
+
+/* Tests sorting order when only unvisited bookmarks are available (e.g. in
+ permanent private browsing mode), then the only information we have is the
+ number of bookmarks per origin, and we're going to use that. */
+add_task(async function just_multiple_unvisited_bookmarks() {
+ // These are sorted to avoid confusion with natural sorting, so the one with
+ // the highest score is added in the middle.
+ let filledUrl = "https://www.tld2.com/";
+ let urls = [
+ {
+ url: "https://tld1.com/",
+ count: 1,
+ },
+ {
+ url: "https://tld2.com/",
+ count: 2,
+ },
+ {
+ url: filledUrl,
+ count: 2,
+ },
+ {
+ url: "https://tld3.com/",
+ count: 3,
+ },
+ ];
+
+ await PlacesUtils.history.clear();
+ for (let { url, count } of urls) {
+ while (count--) {
+ await PlacesTestUtils.addBookmarkWithDetails({
+ uri: url,
+ });
+ }
+ }
+ await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
+
+ let context = createContext("tld", { isPrivate: false });
+ await check_results({
+ context,
+ autofilled: "tld2.com/",
+ completed: filledUrl,
+ matches: [
+ makeVisitResult(context, {
+ uri: filledUrl,
+ title: "A bookmark",
+ heuristic: true,
+ }),
+ makeBookmarkResult(context, {
+ uri: "https://tld3.com/",
+ title: "A bookmark",
+ }),
+ makeBookmarkResult(context, {
+ uri: "https://tld2.com/",
+ title: "A bookmark",
+ }),
+ makeBookmarkResult(context, {
+ uri: "https://tld1.com/",
+ title: "A bookmark",
+ }),
+ ],
+ });
+
+ await cleanup();
+});
diff --git a/browser/components/urlbar/tests/unit/test_quickactions.js b/browser/components/urlbar/tests/unit/test_quickactions.js
index 00206c77b2..30e3fbdd95 100644
--- a/browser/components/urlbar/tests/unit/test_quickactions.js
+++ b/browser/components/urlbar/tests/unit/test_quickactions.js
@@ -5,108 +5,58 @@
"use strict";
ChromeUtils.defineESModuleGetters(this, {
- UrlbarProviderQuickActions:
- "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
+ ActionsProviderQuickActions:
+ "resource:///modules/ActionsProviderQuickActions.sys.mjs",
});
-let expectedMatch = (key, inputLength) => ({
- type: UrlbarUtils.RESULT_TYPE.DYNAMIC,
- source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
- heuristic: false,
- payload: {
- results: [{ key }],
- dynamicType: "quickactions",
- inQuickActionsSearchMode: false,
- helpUrl: UrlbarProviderQuickActions.helpUrl,
- inputLength,
- },
-});
-
-testEngine_setup();
-
add_setup(async () => {
UrlbarPrefs.set("quickactions.enabled", true);
- UrlbarPrefs.set("suggest.quickactions", true);
- UrlbarProviderQuickActions.addAction("newaction", {
+ ActionsProviderQuickActions.addAction("newaction", {
commands: ["newaction"],
});
registerCleanupFunction(async () => {
UrlbarPrefs.clear("quickactions.enabled");
- UrlbarPrefs.clear("suggest.quickactions");
- UrlbarProviderQuickActions.removeAction("newaction");
+ ActionsProviderQuickActions.removeAction("newaction");
});
});
add_task(async function nomatch() {
- let context = createContext("this doesnt match", {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
- await check_results({
- context,
- matches: [],
- });
-});
-
-add_task(async function quickactions_disabled() {
- UrlbarPrefs.set("suggest.quickactions", false);
- let context = createContext("new", {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
- await check_results({
- context,
- matches: [],
- });
+ let context = createContext("this doesnt match", {});
+ let result = await ActionsProviderQuickActions.queryAction(context);
+ Assert.ok(result === null, "there were no matches");
});
add_task(async function quickactions_match() {
- UrlbarPrefs.set("suggest.quickactions", true);
- let context = createContext("new", {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
- await check_results({
- context,
- matches: [expectedMatch("newaction", 3)],
- });
+ let context = createContext("new", {});
+ let result = await ActionsProviderQuickActions.queryAction(context);
+ Assert.ok(result.key == "newaction", "Matched the new action");
});
add_task(async function duplicate_matches() {
- UrlbarProviderQuickActions.addAction("testaction", {
+ ActionsProviderQuickActions.addAction("testaction", {
commands: ["testaction", "test"],
});
- let context = createContext("testaction", {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
+ let context = createContext("test", {});
+ let result = await ActionsProviderQuickActions.queryAction(context);
- await check_results({
- context,
- matches: [expectedMatch("testaction", 10)],
- });
+ Assert.ok(result.key == "testaction", "Matched the test action");
- UrlbarProviderQuickActions.removeAction("testaction");
+ ActionsProviderQuickActions.removeAction("testaction");
});
add_task(async function remove_action() {
- UrlbarProviderQuickActions.addAction("testaction", {
+ ActionsProviderQuickActions.addAction("testaction", {
commands: ["testaction"],
});
- UrlbarProviderQuickActions.removeAction("testaction");
+ ActionsProviderQuickActions.removeAction("testaction");
- let context = createContext("test", {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
+ let context = createContext("test", {});
+ let result = await ActionsProviderQuickActions.queryAction(context);
- await check_results({
- context,
- matches: [],
- });
+ Assert.ok(result === null, "there were no matches");
});
add_task(async function minimum_search_string() {
@@ -114,13 +64,18 @@ add_task(async function minimum_search_string() {
for (let minimumSearchString of [0, 3]) {
UrlbarPrefs.set("quickactions.minimumSearchString", minimumSearchString);
for (let i = 1; i < 4; i++) {
- let context = createContext(searchString.substring(0, i), {
- providers: [UrlbarProviderQuickActions.name],
- isPrivate: false,
- });
- let matches =
- i >= minimumSearchString ? [expectedMatch("newaction", i)] : [];
- await check_results({ context, matches });
+ let context = createContext(searchString.substring(0, i), {});
+ let result = await ActionsProviderQuickActions.queryAction(context);
+
+ if (i >= minimumSearchString) {
+ Assert.ok(result.key == "newaction", "Matched the new action");
+ } else {
+ Assert.equal(
+ ActionsProviderQuickActions.isActive(context),
+ false,
+ "QuickActions Provider is not active"
+ );
+ }
}
}
UrlbarPrefs.clear("quickactions.minimumSearchString");
diff --git a/browser/components/urlbar/tests/unit/test_tokenizer.js b/browser/components/urlbar/tests/unit/test_tokenizer.js
index 835d1a5909..76b2c31ac2 100644
--- a/browser/components/urlbar/tests/unit/test_tokenizer.js
+++ b/browser/components/urlbar/tests/unit/test_tokenizer.js
@@ -32,6 +32,12 @@ add_task(async function test_tokenizer() {
],
},
{
+ desc: "do not separate restriction char at beginning in search mode",
+ searchMode: { engineName: "testEngine" },
+ searchString: `${UrlbarTokenizer.RESTRICT.SEARCH}test`,
+ expectedTokens: [{ value: "?test", type: UrlbarTokenizer.TYPE.TEXT }],
+ },
+ {
desc: "separate restriction char at end",
searchString: `test ${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
expectedTokens: [