tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UrlbarProviderGlobalActions.sys.mjs (6718B)


      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/. */
      4 
      5 /**
      6 * This module exports a provider that returns all the available
      7 * global actions for a query.
      8 */
      9 
     10 import {
     11  UrlbarProvider,
     12  UrlbarUtils,
     13 } from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs";
     14 
     15 const lazy = {};
     16 
     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";
     20 
     21 // The suggestion index of the actions row within the urlbar results.
     22 const SUGGESTED_INDEX = 1;
     23 const SUGGESTED_INDEX_TABS_MODE = 0;
     24 
     25 const SCOTCH_BONNET_PREF = "scotchBonnet.enableOverride";
     26 const ACTIONS_PREF = "secondaryActions.featureGate";
     27 const QUICK_ACTIONS_PREF = "suggest.quickactions";
     28 const MAX_ACTIONS_PREF = "secondaryActions.maxActionsShown";
     29 
     30 // Prefs relating to the onboarding label shown to new users.
     31 const TIMES_TO_SHOW_PREF = "quickactions.timesToShowOnboardingLabel";
     32 const TIMES_SHOWN_PREF = "quickactions.timesShownOnboardingLabel";
     33 
     34 ChromeUtils.defineESModuleGetters(lazy, {
     35  UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs",
     36  UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs",
     37 });
     38 
     39 import { ActionsProviderQuickActions } from "moz-src:///browser/components/urlbar/ActionsProviderQuickActions.sys.mjs";
     40 import { ActionsProviderContextualSearch } from "moz-src:///browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs";
     41 import { ActionsProviderTabGroups } from "moz-src:///browser/components/urlbar/ActionsProviderTabGroups.sys.mjs";
     42 
     43 let globalActionsProviders = [
     44  ActionsProviderContextualSearch,
     45  ActionsProviderQuickActions,
     46  ActionsProviderTabGroups,
     47 ];
     48 
     49 /**
     50 * A provider that lets the user view all available global actions for a query.
     51 */
     52 export class UrlbarProviderGlobalActions extends UrlbarProvider {
     53  /**
     54   * @returns {Values<typeof UrlbarUtils.PROVIDER_TYPE>}
     55   */
     56  get type() {
     57    return UrlbarUtils.PROVIDER_TYPE.PROFILE;
     58  }
     59 
     60  async isActive(_queryContext) {
     61    return (
     62      (lazy.UrlbarPrefs.get(SCOTCH_BONNET_PREF) ||
     63        lazy.UrlbarPrefs.get(ACTIONS_PREF)) &&
     64      lazy.UrlbarPrefs.get(QUICK_ACTIONS_PREF)
     65    );
     66  }
     67 
     68  /**
     69   * Starts querying.
     70   *
     71   * @param {UrlbarQueryContext} queryContext
     72   * @param {(provider: UrlbarProvider, result: UrlbarResult) => void} addCallback
     73   *   Callback invoked by the provider to add a new result.
     74   */
     75  async startQuery(queryContext, addCallback) {
     76    let actionsResults = [];
     77 
     78    for (let provider of globalActionsProviders) {
     79      if (provider.isActive(queryContext)) {
     80        for (let action of (await provider.queryActions(queryContext)) || []) {
     81          action.providerName = provider.name;
     82          actionsResults.push(action);
     83        }
     84      }
     85    }
     86 
     87    if (!actionsResults.length) {
     88      return;
     89    }
     90 
     91    if (actionsResults.length > lazy.UrlbarPrefs.get(MAX_ACTIONS_PREF)) {
     92      actionsResults.length = lazy.UrlbarPrefs.get(MAX_ACTIONS_PREF);
     93    }
     94 
     95    let showOnboardingLabel =
     96      lazy.UrlbarPrefs.get(TIMES_TO_SHOW_PREF) >
     97      lazy.UrlbarPrefs.get(TIMES_SHOWN_PREF);
     98 
     99    let query = actionsResults.some(a => a.key == "matched-contextual-search")
    100      ? ""
    101      : queryContext.searchString;
    102 
    103    let payload = {
    104      actionsResults,
    105      dynamicType: DYNAMIC_TYPE_NAME,
    106      inputLength: queryContext.searchString.length,
    107      input: query,
    108      showOnboardingLabel,
    109      query,
    110    };
    111 
    112    let result = new lazy.UrlbarResult({
    113      type: UrlbarUtils.RESULT_TYPE.DYNAMIC,
    114      source: UrlbarUtils.RESULT_SOURCE.ACTIONS,
    115      suggestedIndex:
    116        queryContext.restrictSource == UrlbarUtils.RESULT_SOURCE.TABS
    117          ? SUGGESTED_INDEX_TABS_MODE
    118          : SUGGESTED_INDEX,
    119      payload,
    120    });
    121    addCallback(this, result);
    122  }
    123 
    124  onSelection(result, element) {
    125    let key = element.dataset.action;
    126    let action = result.payload.actionsResults.find(a => a.key == key);
    127    action.onSelection?.(result, element);
    128  }
    129 
    130  onEngagement(queryContext, controller, details) {
    131    let key = details.element.dataset.action;
    132    let action = details.result.payload.actionsResults.find(a => a.key == key);
    133    let options = action.onPick(queryContext, controller);
    134    if (options?.focusContent) {
    135      details.element.ownerGlobal.gBrowser.selectedBrowser.focus();
    136    }
    137    controller.view.close();
    138  }
    139 
    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  }
    154 
    155  getViewTemplate(result) {
    156    let children = result.payload.actionsResults.map((action, i) => {
    157      let btn = {
    158        name: `button-${i}`,
    159        tag: "span",
    160        classList: ["urlbarView-action-btn"],
    161        attributes: {
    162          inputLength: result.payload.inputLength,
    163          "data-action": action.key,
    164          role: "button",
    165        },
    166        children: [
    167          {
    168            tag: "img",
    169            attributes: {
    170              src: action.icon || DEFAULT_ICON,
    171            },
    172          },
    173          {
    174            name: `label-${i}`,
    175            tag: "span",
    176            classList: ["urlbarView-action-btn-label"],
    177          },
    178        ],
    179      };
    180 
    181      if (action.dataset?.style) {
    182        let style = "";
    183        for (let [prop, val] of Object.entries(action.dataset.style)) {
    184          style += `${prop}: ${val};`;
    185        }
    186        btn.attributes.style = style;
    187      }
    188 
    189      if (action.dataset?.providesSearchMode) {
    190        btn.attributes["data-provides-searchmode"] = "true";
    191        btn.attributes["data-engine"] = action.engine;
    192      }
    193 
    194      return btn;
    195    });
    196 
    197    if (result.payload.showOnboardingLabel) {
    198      children.unshift({
    199        name: "press-tab-label",
    200        tag: "span",
    201        classList: ["urlbarView-press-tab-label"],
    202      });
    203    }
    204 
    205    return { children };
    206  }
    207 
    208  getViewUpdate(result) {
    209    let viewUpdate = {};
    210    if (result.payload.showOnboardingLabel) {
    211      viewUpdate["press-tab-label"] = {
    212        l10n: { id: "press-tab-label" },
    213      };
    214    }
    215    result.payload.actionsResults.forEach((action, i) => {
    216      viewUpdate[`label-${i}`] = {
    217        l10n: { id: action.l10nId, args: action.l10nArgs },
    218      };
    219    });
    220    return viewUpdate;
    221  }
    222 }