From 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:33 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- .../search/AppProvidedSearchEngine.sys.mjs | 185 +++++++++++++++------ 1 file changed, 133 insertions(+), 52 deletions(-) (limited to 'toolkit/components/search/AppProvidedSearchEngine.sys.mjs') diff --git a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs index a8db801ac4..7401ba115c 100644 --- a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs +++ b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs @@ -12,9 +12,94 @@ import { const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", }); +/** + * Handles loading application provided search engine icons from remote settings. + */ +class IconHandler { + #iconList = null; + #iconCollection = null; + + /** + * Returns the icon for the record that matches the engine identifier + * and the preferred width. + * + * @param {string} engineIdentifier + * The identifier of the engine to match against. + * @param {number} preferredWidth + * The preferred with of the icon. + * @returns {string} + * An object URL that can be used to reference the contents of the specified + * source object. + */ + async getIcon(engineIdentifier, preferredWidth) { + if (!this.#iconList) { + await this.#getIconList(); + } + + let iconRecords = this.#iconList.filter(r => { + return r.engineIdentifiers.some(i => { + if (i.endsWith("*")) { + return engineIdentifier.startsWith(i.slice(0, -1)); + } + return engineIdentifier == i; + }); + }); + + if (!iconRecords.length) { + console.warn("No icon found for", engineIdentifier); + return null; + } + + // Default to the first record, in the event we don't have any records + // that match the width. + let iconRecord = iconRecords[0]; + for (let record of iconRecords) { + // TODO: Bug 1655070. We should be using the closest size, but for now use + // an exact match. + if (record.imageSize == preferredWidth) { + iconRecord = record; + break; + } + } + + let iconURL; + try { + iconURL = await this.#iconCollection.attachments.get(iconRecord); + } catch (ex) { + console.error(ex); + return null; + } + if (!iconURL) { + console.warn("Unable to find the icon for", engineIdentifier); + return null; + } + return URL.createObjectURL( + new Blob([iconURL.buffer]), + iconRecord.attachment.mimetype + ); + } + + /** + * Obtains the icon list from the remote settings collection. + */ + async #getIconList() { + this.#iconCollection = lazy.RemoteSettings("search-config-icons"); + try { + this.#iconList = await this.#iconCollection.get(); + } catch (ex) { + console.error(ex); + this.#iconList = []; + } + if (!this.#iconList.length) { + console.error("Failed to obtain search engine icon list records"); + } + } +} + /** * AppProvidedSearchEngine represents a search engine defined by the * search configuration. @@ -25,6 +110,20 @@ export class AppProvidedSearchEngine extends SearchEngine { ["suggestions", lazy.SearchUtils.URL_TYPE.SUGGEST_JSON], ["trending", lazy.SearchUtils.URL_TYPE.TRENDING_JSON], ]); + static iconHandler = new IconHandler(); + + /** + * @typedef {?Promise} + * A promise for the blob URL of the icon. We save the promise to avoid + * reentrancy issues. + */ + #blobURLPromise = null; + + /** + * @typedef {?string} + * The identifier from the configuration. + */ + #configurationId = null; /** * @param {object} options @@ -51,11 +150,23 @@ export class AppProvidedSearchEngine extends SearchEngine { this._extensionID = extensionId; this._locale = config.webExtension.locale; + this.#configurationId = config.identifier; this.#init(config); this._loadSettings(settings); } + /** + * Used to clean up the engine when it is removed. This will revoke the blob + * URL for the icon. + */ + async cleanup() { + if (this.#blobURLPromise) { + URL.revokeObjectURL(await this.#blobURLPromise); + this.#blobURLPromise = null; + } + } + /** * Update this engine based on new config, used during * config upgrades. @@ -63,14 +174,11 @@ export class AppProvidedSearchEngine extends SearchEngine { * @param {object} options * The options object. * - * @param {object} options.locale - * The locale that is being used for the engine. * @param {object} options.configuration * The search engine configuration for application provided engines. */ - update({ locale, configuration } = {}) { + update({ configuration } = {}) { this._urls = []; - this._iconMapObj = null; this.#init(configuration); lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED); } @@ -89,24 +197,10 @@ export class AppProvidedSearchEngine extends SearchEngine { * Returns true if the engine was updated, false otherwise. */ async updateIfNoNameChange({ configuration, locale }) { - let newName; - if (locale != "default") { - newName = configuration.webExtension.searchProvider[locale].name; - } else if ( - locale == "default" && - configuration.webExtension.default_locale - ) { - newName = - configuration.webExtension.searchProvider[ - configuration.webExtension.default_locale - ].name; - } else { - newName = configuration.webExtension.name; - } - - if (this.name != newName.trim()) { + if (this.name != configuration.name.trim()) { return false; } + this.update({ locale, configuration }); return true; } @@ -144,6 +238,25 @@ export class AppProvidedSearchEngine extends SearchEngine { ); } + /** + * Returns the icon URL for the search engine closest to the preferred width. + * + * @param {number} preferredWidth + * The preferred width of the image. + * @returns {Promise} + * A promise that resolves to the URL of the icon. + */ + async getIconURL(preferredWidth) { + if (this.#blobURLPromise) { + return this.#blobURLPromise; + } + this.#blobURLPromise = AppProvidedSearchEngine.iconHandler.getIcon( + this.#configurationId, + preferredWidth + ); + return this.#blobURLPromise; + } + /** * Creates a JavaScript object that represents this engine. * @@ -175,38 +288,6 @@ export class AppProvidedSearchEngine extends SearchEngine { this._telemetryId += `-${engineConfig.telemetrySuffix}`; } - // 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); - // } - // } - - // this._initWithDetails(config); - - // this._sendAttributionRequest = config.sendAttributionRequest ?? false; // TODO check if we need to this? - // if (details.iconURL) { - // this._setIcon(details.iconURL, true); - // } - this._name = engineConfig.name.trim(); this._definedAliases = engineConfig.aliases?.map(alias => `@${alias}`) ?? []; -- cgit v1.2.3