summaryrefslogtreecommitdiffstats
path: root/toolkit/components/search/AddonSearchEngine.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/search/AddonSearchEngine.sys.mjs')
-rw-r--r--toolkit/components/search/AddonSearchEngine.sys.mjs236
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
+ );
+ }
+}