Bug 1944627 - update sidebar button checked state for non-revamped sidebar cases...
[gecko.git] / browser / components / urlbar / UrlbarProviderGlobalActions.sys.mjs
blob0f23f96e65a783ec3e4d83f799ef6b2b6d68daef
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6  * This module exports a provider that returns all the available
7  * global actions for a query.
8  */
10 import {
11   UrlbarProvider,
12   UrlbarUtils,
13 } from "resource:///modules/UrlbarUtils.sys.mjs";
15 const lazy = {};
17 // Default icon shown for actions if no custom one is provided.
18 const DEFAULT_ICON = "chrome://global/skin/icons/settings.svg";
19 const DYNAMIC_TYPE_NAME = "actions";
21 // The suggestion index of the actions row within the urlbar results.
22 const SUGGESTED_INDEX = 1;
24 const SCOTCH_BONNET_PREF = "scotchBonnet.enableOverride";
25 const ACTIONS_PREF = "secondaryActions.featureGate";
26 const QUICK_ACTIONS_PREF = "suggest.quickactions";
28 // Prefs relating to the onboarding label shown to new users.
29 const TIMES_TO_SHOW_PREF = "quickactions.timesToShowOnboardingLabel";
30 const TIMES_SHOWN_PREF = "quickactions.timesShownOnboardingLabel";
32 ChromeUtils.defineESModuleGetters(lazy, {
33   UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
34   UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
35 });
37 import { ActionsProviderQuickActions } from "resource:///modules/ActionsProviderQuickActions.sys.mjs";
38 import { ActionsProviderContextualSearch } from "resource:///modules/ActionsProviderContextualSearch.sys.mjs";
39 import { ActionsProviderTabGroups } from "resource:///modules/ActionsProviderTabGroups.sys.mjs";
41 let globalActionsProviders = [
42   ActionsProviderContextualSearch,
43   ActionsProviderQuickActions,
44   ActionsProviderTabGroups,
47 /**
48  * A provider that lets the user view all available global actions for a query.
49  */
50 class ProviderGlobalActions extends UrlbarProvider {
51   // A Map of the last queried actions.
52   #actions = new Map();
54   get name() {
55     return "UrlbarProviderGlobalActions";
56   }
58   get type() {
59     return UrlbarUtils.PROVIDER_TYPE.PROFILE;
60   }
62   isActive() {
63     return (
64       (lazy.UrlbarPrefs.get(SCOTCH_BONNET_PREF) ||
65         lazy.UrlbarPrefs.get(ACTIONS_PREF)) &&
66       lazy.UrlbarPrefs.get(QUICK_ACTIONS_PREF)
67     );
68   }
70   async startQuery(queryContext, addCallback) {
71     this.#actions.clear();
73     let searchModeEngine = "";
75     for (let provider of globalActionsProviders) {
76       if (provider.isActive(queryContext)) {
77         for (let action of (await provider.queryActions(queryContext)) || []) {
78           if (action.engine && !searchModeEngine) {
79             searchModeEngine = action.engine;
80           } else if (action.engine) {
81             // We only allow one action that provides an engine search mode.
82             continue;
83           }
84           this.#actions.set(action.key, action);
85         }
86       }
87     }
89     if (!this.#actions.size) {
90       return;
91     }
93     let showOnboardingLabel =
94       lazy.UrlbarPrefs.get(TIMES_TO_SHOW_PREF) >
95       lazy.UrlbarPrefs.get(TIMES_SHOWN_PREF);
97     let results = [...this.#actions.keys()];
99     let query = results.includes("matched-contextual-search")
100       ? ""
101       : queryContext.searchString;
103     let payload = {
104       results,
105       dynamicType: DYNAMIC_TYPE_NAME,
106       inputLength: queryContext.searchString.length,
107       input: query,
108       showOnboardingLabel,
109       query,
110     };
112     if (searchModeEngine) {
113       payload.providesSearchMode = true;
114       payload.engine = searchModeEngine;
115     }
117     let result = new lazy.UrlbarResult(
118       UrlbarUtils.RESULT_TYPE.DYNAMIC,
119       UrlbarUtils.RESULT_SOURCE.ACTIONS,
120       payload
121     );
122     result.suggestedIndex = SUGGESTED_INDEX;
123     addCallback(this, result);
124   }
126   onSelection(result, element) {
127     let key = element.dataset.action;
128     this.#actions.get(key).onSelection?.(result, element);
129   }
131   onEngagement(queryContext, controller, details) {
132     let key = details.element.dataset.action;
133     let options = this.#actions.get(key).onPick(queryContext, controller);
134     if (options?.focusContent) {
135       details.element.ownerGlobal.gBrowser.selectedBrowser.focus();
136     }
137     controller.view.close();
138   }
140   onSearchSessionEnd(queryContext, controller, details) {
141     let showOnboardingLabel = queryContext.results?.find(
142       r => r.providerName == this.name
143     )?.payload.showOnboardingLabel;
144     if (showOnboardingLabel) {
145       lazy.UrlbarPrefs.set(
146         TIMES_SHOWN_PREF,
147         lazy.UrlbarPrefs.get(TIMES_SHOWN_PREF) + 1
148       );
149     }
150     for (let provider of globalActionsProviders) {
151       provider.onSearchSessionEnd?.(queryContext, controller, details);
152     }
153   }
155   getViewTemplate(result) {
156     let children = result.payload.results.map((key, i) => {
157       let action = this.#actions.get(key);
158       let style;
159       if (action.dataset?.style) {
160         style = "";
161         for (let [prop, val] of Object.entries(action.dataset.style)) {
162           style += `${prop}: ${val};`;
163         }
164       }
165       return {
166         name: `button-${i}`,
167         tag: "span",
168         classList: ["urlbarView-action-btn"],
169         attributes: {
170           style,
171           inputLength: result.payload.inputLength,
172           "data-action": key,
173           role: "button",
174         },
175         children: [
176           {
177             tag: "img",
178             attributes: {
179               src: action.icon || DEFAULT_ICON,
180             },
181           },
182           {
183             name: `label-${i}`,
184             tag: "span",
185           },
186         ],
187       };
188     });
190     if (result.payload.showOnboardingLabel) {
191       children.unshift({
192         name: "press-tab-label",
193         tag: "span",
194         classList: ["urlbarView-press-tab-label"],
195       });
196     }
198     return { children };
199   }
201   getViewUpdate(result) {
202     let viewUpdate = {};
203     if (result.payload.showOnboardingLabel) {
204       viewUpdate["press-tab-label"] = {
205         l10n: { id: "press-tab-label", cacheable: true },
206       };
207     }
208     result.payload.results.forEach((key, i) => {
209       let action = this.#actions.get(key);
210       viewUpdate[`label-${i}`] = {
211         l10n: { id: action.l10nId, args: action.l10nArgs, cacheable: true },
212       };
213     });
214     return viewUpdate;
215   }
218 export var UrlbarProviderGlobalActions = new ProviderGlobalActions();