/* 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 { SuggestProvider } from "moz-src:///browser/components/urlbar/private/SuggestFeature.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { QuickSuggest: "moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs", UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs", UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs", UrlbarUtils: "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs", }); /** * A feature that manages the dynamic Rust suggestion types defined in the * `quickSuggestDynamicSuggestionTypes` Nimbus variable or its related pref * `quicksuggest.dynamicSuggestionTypes`. Dynamic Rust suggestions are not * statically typed except for a few core properties, so they can be used to * serve many different types of suggestions without any Rust changes. They are * also used for hidden-exposure suggestions (potential exposures). * * This feature only manages the types defined in the variable or pref. Other * features can manage dynamic suggestion types that are *not* defined in the * variable or pref by extending `SuggestProvider` as usual and overriding the * `dynamicRustSuggestionTypes` getter. That makes it possible for a feature to * use dynamic suggestions as an implementation detail. */ export class DynamicSuggestions extends SuggestProvider { get enablingPreferences() { return ["quicksuggest.dynamicSuggestionTypes"]; } get shouldEnable() { return !!this.dynamicRustSuggestionTypes.length; } get rustSuggestionType() { return "Dynamic"; } get dynamicRustSuggestionTypes() { // UrlbarPrefs converts this pref to a `Set` of type strings. return [...lazy.UrlbarPrefs.get("quicksuggest.dynamicSuggestionTypes")]; } isSuggestionSponsored(suggestion) { return !!suggestion.data?.result?.payload?.isSponsored; } getSuggestionTelemetryType(suggestion) { if (suggestion.data?.result?.payload?.hasOwnProperty("telemetryType")) { return suggestion.data.result.payload.telemetryType; } if (suggestion.data?.result?.isHiddenExposure) { return "exposure"; } return suggestion.suggestionType; } makeResult(queryContext, suggestion, _searchString) { let { data } = suggestion; if (!data || typeof data != "object") { this.logger.warn( "suggestion.data is falsey or not an object, ignoring suggestion" ); return null; } let { result } = data; if (!result || typeof result != "object") { this.logger.warn( "suggestion.data.result is falsey or not an object, ignoring suggestion" ); return null; } let payload = {}; if (result.hasOwnProperty("payload")) { if (typeof result.payload != "object") { this.logger.warn( "suggestion.data.result.payload is not an object, ignoring suggestion" ); return null; } payload = result.payload; } if (result.isHiddenExposure) { return this.#makeExposureResult(suggestion, payload); } // Dynamic results can set `result.bypassSuggestAll` to be shown even when // `suggest.quicksuggest.all` is false. Typically results should not do this // unless they aren't considered part of the Suggest brand. if ( !result.bypassSuggestAll && (!lazy.UrlbarPrefs.get("suggest.quicksuggest.all") || (payload.isSponsored && !lazy.UrlbarPrefs.get("suggest.quicksuggest.sponsored"))) ) { return null; } payload.isManageable = true; payload.helpUrl = lazy.QuickSuggest.HELP_URL; let resultProperties = { ...result }; delete resultProperties.payload; return new lazy.UrlbarResult({ type: lazy.UrlbarUtils.RESULT_TYPE.URL, source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH, ...resultProperties, payload, }); } onEngagement(_queryContext, controller, details, _searchString) { switch (details.selType) { case "manage": // "manage" is handled by UrlbarInput, no need to do anything here. break; case "dismiss": { let { result } = details; lazy.QuickSuggest.dismissResult(result); result.acknowledgeDismissalL10n = { id: "firefox-suggest-dismissal-acknowledgment-one", }; controller.removeResult(result); break; } } } #makeExposureResult(suggestion, payload) { // It doesn't really matter what kind of result we return since it won't be // shown. Use a dynamic result since that kind of makes sense and there are // no requirements for its payload other than `dynamicType`. return new lazy.UrlbarResult({ type: lazy.UrlbarUtils.RESULT_TYPE.DYNAMIC, source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH, // Exposure suggestions should always be hidden, and it's assumed that // exposure telemetry should be recorded for them, so as a convenience // set `exposureTelemetry` here. Otherwise experiments would need to set // the corresponding Nimbus variables properly. (They can still do that, // it's just not required.) exposureTelemetry: lazy.UrlbarUtils.EXPOSURE_TELEMETRY.HIDDEN, payload: { ...payload, dynamicType: "exposure", }, }); } }