diff options
Diffstat (limited to 'toolkit/components/search/AddonSearchEngine.sys.mjs')
-rw-r--r-- | toolkit/components/search/AddonSearchEngine.sys.mjs | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/toolkit/components/search/AddonSearchEngine.sys.mjs b/toolkit/components/search/AddonSearchEngine.sys.mjs new file mode 100644 index 0000000000..dedf9bcc88 --- /dev/null +++ b/toolkit/components/search/AddonSearchEngine.sys.mjs @@ -0,0 +1,236 @@ +/* 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/. */ + +/* eslint no-shadow: error, mozilla/no-aArgs: error */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +import { SearchEngine } from "resource://gre/modules/SearchEngine.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(lazy, { + ExtensionParent: "resource://gre/modules/ExtensionParent.jsm", +}); + +/** + * AddonSearchEngine represents a search engine defined by an add-on. + */ +export class AddonSearchEngine extends SearchEngine { + // Whether the engine is provided by the application. + #isAppProvided = false; + + /** + * Creates a AddonSearchEngine. + * + * @param {object} options + * The options object + * @param {boolean} options.isAppProvided + * Indicates whether the engine is provided by Firefox, either + * shipped in omni.ja or via Normandy. If it is, it will + * be treated as read-only. + * @param {object} [options.details] + * An object that simulates the manifest object from a WebExtension. + * @param {object} [options.json] + * An object that represents the saved JSON settings for the engine. + */ + constructor({ isAppProvided, details, json } = {}) { + let extensionId = + details?.extensionID ?? json.extensionID ?? json._extensionID; + let id = extensionId + (details?.locale ?? json._locale); + + super({ + loadPath: "[addon]" + extensionId, + isAppProvided, + id, + }); + + this.#isAppProvided = isAppProvided; + + if (details) { + if (!details.extensionID) { + throw Components.Exception( + "Empty extensionID passed to _createAndAddEngine!", + Cr.NS_ERROR_INVALID_ARG + ); + } + + this.#initFromManifest( + details.extensionID, + details.extensionBaseURI, + details.manifest, + details.locale, + details.config + ); + } else { + this._initWithJSON(json); + } + } + + /** + * Update this engine based on new manifest, used during + * webextension upgrades. + * + * @param {string} extensionID + * The WebExtension ID. + * @param {string} extensionBaseURI + * The Base URI of the WebExtension. + * @param {object} manifest + * An object representing the WebExtensions' manifest. + * @param {string} locale + * The locale that is being used for the WebExtension. + * @param {object} [configuration] + * The search engine configuration for application provided engines, that + * may be overriding some of the WebExtension's settings. + */ + updateFromManifest( + extensionID, + extensionBaseURI, + manifest, + locale, + configuration = {} + ) { + this._urls = []; + this._iconMapObj = null; + this.#initFromManifest( + extensionID, + extensionBaseURI, + manifest, + locale, + configuration + ); + lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED); + } + + /** + * Whether or not this engine is provided by the application, e.g. it is + * in the list of configured search engines. Overrides the definition in + * `SearchEngine`. + * + * @returns {boolean} + */ + get isAppProvided() { + return this.#isAppProvided; + } + + /** + * Whether or not this engine is an in-memory only search engine. + * These engines are typically application provided or policy engines, + * where they are loaded every time on SearchService initialization + * using the policy JSON or the extension manifest. Minimal details of the + * in-memory engines are saved to disk, but they are never loaded + * from the user's saved settings file. + * + * @returns {boolean} + * Only returns true for application provided engines. + */ + get inMemory() { + return this.#isAppProvided; + } + + /** + * Creates a JavaScript object that represents this engine. + * + * @returns {object} + * An object suitable for serialization as JSON. + */ + toJSON() { + // For built-in engines we don't want to store all their data in the settings + // file so just store the relevant metadata. + if (this.#isAppProvided) { + return { + id: this.id, + _name: this.name, + _isAppProvided: true, + _metaData: this._metaData, + }; + } + return super.toJSON(); + } + + /** + * Initializes the engine based on the manifest and other values. + * + * @param {string} extensionID + * The WebExtension ID. + * @param {string} extensionBaseURI + * The Base URI of the WebExtension. + * @param {object} manifest + * An object representing the WebExtensions' manifest. + * @param {string} locale + * The locale that is being used for the WebExtension. + * @param {object} [configuration] + * The search engine configuration for application provided engines, that + * may be overriding some of the WebExtension's settings. + */ + #initFromManifest( + extensionID, + extensionBaseURI, + manifest, + locale, + configuration = {} + ) { + let searchProvider = manifest.chrome_settings_overrides.search_provider; + + this._extensionID = extensionID; + this._locale = locale; + + // We only set _telemetryId for app-provided engines. See also telemetryId + // getter. + if (this.#isAppProvided) { + if (configuration.telemetryId) { + this._telemetryId = configuration.telemetryId; + } else { + let telemetryId = extensionID.split("@")[0]; + if (locale != lazy.SearchUtils.DEFAULT_TAG) { + telemetryId += "-" + locale; + } + this._telemetryId = telemetryId; + } + } + + // Set the main icon URL for the engine. + let iconURL = searchProvider.favicon_url; + + if (!iconURL) { + iconURL = + manifest.icons && + extensionBaseURI.resolve( + lazy.ExtensionParent.IconDetails.getPreferredIcon(manifest.icons).icon + ); + } + + // Record other icons that the WebExtension has. + if (manifest.icons) { + let iconList = Object.entries(manifest.icons).map(icon => { + return { + width: icon[0], + height: icon[0], + url: extensionBaseURI.resolve(icon[1]), + }; + }); + for (let icon of iconList) { + this._addIconToMap(icon.size, icon.size, icon.url); + } + } + + // Filter out any untranslated parameters, the extension has to list all + // possible mozParams for each engine where a 'locale' may only provide + // actual values for some (or none). + if (searchProvider.params) { + searchProvider.params = searchProvider.params.filter(param => { + return !(param.value && param.value.startsWith("__MSG_")); + }); + } + + this._initWithDetails( + { ...searchProvider, iconURL, description: manifest.description }, + configuration + ); + } +} |