summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/ActionsProviderQuickActions.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/ActionsProviderQuickActions.sys.mjs')
-rw-r--r--browser/components/urlbar/ActionsProviderQuickActions.sys.mjs152
1 files changed, 152 insertions, 0 deletions
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();