tor-browser

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

DynamicSuggestions.sys.mjs (5767B)


      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 import { SuggestProvider } from "moz-src:///browser/components/urlbar/private/SuggestFeature.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  QuickSuggest: "moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs",
     11  UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs",
     12  UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs",
     13  UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs",
     14 });
     15 
     16 /**
     17 * A feature that manages the dynamic Rust suggestion types defined in the
     18 * `quickSuggestDynamicSuggestionTypes` Nimbus variable or its related pref
     19 * `quicksuggest.dynamicSuggestionTypes`. Dynamic Rust suggestions are not
     20 * statically typed except for a few core properties, so they can be used to
     21 * serve many different types of suggestions without any Rust changes. They are
     22 * also used for hidden-exposure suggestions (potential exposures).
     23 *
     24 * This feature only manages the types defined in the variable or pref. Other
     25 * features can manage dynamic suggestion types that are *not* defined in the
     26 * variable or pref by extending `SuggestProvider` as usual and overriding the
     27 * `dynamicRustSuggestionTypes` getter. That makes it possible for a feature to
     28 * use dynamic suggestions as an implementation detail.
     29 */
     30 export class DynamicSuggestions extends SuggestProvider {
     31  get enablingPreferences() {
     32    return ["quicksuggest.dynamicSuggestionTypes"];
     33  }
     34 
     35  get shouldEnable() {
     36    return !!this.dynamicRustSuggestionTypes.length;
     37  }
     38 
     39  get rustSuggestionType() {
     40    return "Dynamic";
     41  }
     42 
     43  get dynamicRustSuggestionTypes() {
     44    // UrlbarPrefs converts this pref to a `Set` of type strings.
     45    return [...lazy.UrlbarPrefs.get("quicksuggest.dynamicSuggestionTypes")];
     46  }
     47 
     48  isSuggestionSponsored(suggestion) {
     49    return !!suggestion.data?.result?.payload?.isSponsored;
     50  }
     51 
     52  getSuggestionTelemetryType(suggestion) {
     53    if (suggestion.data?.result?.payload?.hasOwnProperty("telemetryType")) {
     54      return suggestion.data.result.payload.telemetryType;
     55    }
     56    if (suggestion.data?.result?.isHiddenExposure) {
     57      return "exposure";
     58    }
     59    return suggestion.suggestionType;
     60  }
     61 
     62  makeResult(queryContext, suggestion, _searchString) {
     63    let { data } = suggestion;
     64    if (!data || typeof data != "object") {
     65      this.logger.warn(
     66        "suggestion.data is falsey or not an object, ignoring suggestion"
     67      );
     68      return null;
     69    }
     70 
     71    let { result } = data;
     72    if (!result || typeof result != "object") {
     73      this.logger.warn(
     74        "suggestion.data.result is falsey or not an object, ignoring suggestion"
     75      );
     76      return null;
     77    }
     78 
     79    let payload = {};
     80    if (result.hasOwnProperty("payload")) {
     81      if (typeof result.payload != "object") {
     82        this.logger.warn(
     83          "suggestion.data.result.payload is not an object, ignoring suggestion"
     84        );
     85        return null;
     86      }
     87      payload = result.payload;
     88    }
     89 
     90    if (result.isHiddenExposure) {
     91      return this.#makeExposureResult(suggestion, payload);
     92    }
     93 
     94    // Dynamic results can set `result.bypassSuggestAll` to be shown even when
     95    // `suggest.quicksuggest.all` is false. Typically results should not do this
     96    // unless they aren't considered part of the Suggest brand.
     97    if (
     98      !result.bypassSuggestAll &&
     99      (!lazy.UrlbarPrefs.get("suggest.quicksuggest.all") ||
    100        (payload.isSponsored &&
    101          !lazy.UrlbarPrefs.get("suggest.quicksuggest.sponsored")))
    102    ) {
    103      return null;
    104    }
    105 
    106    payload.isManageable = true;
    107    payload.helpUrl = lazy.QuickSuggest.HELP_URL;
    108 
    109    if (!payload.title && payload.url) {
    110      try {
    111        // If there's no title, show the domain as the title. Not all valid URLs
    112        // have a domain.
    113        payload.title = new URL(payload.url).URI.displayHostPort;
    114      } catch (e) {}
    115    }
    116 
    117    let resultProperties = { ...result };
    118    delete resultProperties.payload;
    119    return new lazy.UrlbarResult({
    120      type: lazy.UrlbarUtils.RESULT_TYPE.URL,
    121      source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH,
    122      ...resultProperties,
    123      payload,
    124    });
    125  }
    126 
    127  onEngagement(_queryContext, controller, details, _searchString) {
    128    switch (details.selType) {
    129      case "manage":
    130        // "manage" is handled by UrlbarInput, no need to do anything here.
    131        break;
    132      case "dismiss": {
    133        let { result } = details;
    134        lazy.QuickSuggest.dismissResult(result);
    135        result.acknowledgeDismissalL10n = {
    136          id: "firefox-suggest-dismissal-acknowledgment-one",
    137        };
    138        controller.removeResult(result);
    139        break;
    140      }
    141    }
    142  }
    143 
    144  #makeExposureResult(suggestion, payload) {
    145    // It doesn't really matter what kind of result we return since it won't be
    146    // shown. Use a dynamic result since that kind of makes sense and there are
    147    // no requirements for its payload other than `dynamicType`.
    148    return new lazy.UrlbarResult({
    149      type: lazy.UrlbarUtils.RESULT_TYPE.DYNAMIC,
    150      source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH,
    151      // Exposure suggestions should always be hidden, and it's assumed that
    152      // exposure telemetry should be recorded for them, so as a convenience
    153      // set `exposureTelemetry` here. Otherwise experiments would need to set
    154      // the corresponding Nimbus variables properly. (They can still do that,
    155      // it's just not required.)
    156      exposureTelemetry: lazy.UrlbarUtils.EXPOSURE_TELEMETRY.HIDDEN,
    157      payload: {
    158        ...payload,
    159        dynamicType: "exposure",
    160      },
    161    });
    162  }
    163 }