diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /toolkit/components/search/OpenSearchEngine.sys.mjs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/search/OpenSearchEngine.sys.mjs')
-rw-r--r-- | toolkit/components/search/OpenSearchEngine.sys.mjs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/toolkit/components/search/OpenSearchEngine.sys.mjs b/toolkit/components/search/OpenSearchEngine.sys.mjs new file mode 100644 index 0000000000..e1b9b976fb --- /dev/null +++ b/toolkit/components/search/OpenSearchEngine.sys.mjs @@ -0,0 +1,257 @@ +/* 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 { + EngineURL, + SearchEngine, +} from "resource://gre/modules/SearchEngine.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + loadAndParseOpenSearchEngine: + "resource://gre/modules/OpenSearchLoader.sys.mjs", + SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", +}); + +ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { + return console.createInstance({ + prefix: "OpenSearchEngine", + maxLogLevel: lazy.SearchUtils.loggingEnabled ? "Debug" : "Warn", + }); +}); + +// The default engine update interval, in days. This is only used if an engine +// specifies an updateURL, but not an updateInterval. +const OPENSEARCH_DEFAULT_UPDATE_INTERVAL = 7; + +/** + * OpenSearchEngine represents an OpenSearch base search engine. + */ +export class OpenSearchEngine extends SearchEngine { + // The data describing the engine, in the form of an XML document element. + _data = null; + // The number of days between update checks for new versions + _updateInterval = null; + // The url to check at for a new update + _updateURL = null; + // The url to check for a new icon + _iconUpdateURL = null; + + /** + * Creates a OpenSearchEngine. + * + * @param {object} [options] + * The options object + * @param {object} [options.json] + * An object that represents the saved JSON settings for the engine. + * @param {OpenSearchProperties} [options.engineData] + * The engine data for this search engine that will have been loaded via + * `OpenSearchLoader`. + */ + constructor(options = {}) { + super({ + loadPath: + options.json?._loadPath ?? + OpenSearchEngine.getAnonymizedLoadPath( + lazy.SearchUtils.sanitizeName(options.engineData.name), + options.engineData.installURL + ), + }); + + if (options.engineData) { + this.#setEngineData(options.engineData); + + // As this is a new engine, we must set the verification hash for the load + // path set in the constructor. + this.setAttr( + "loadPathHash", + lazy.SearchUtils.getVerificationHash(this._loadPath) + ); + + if (this.hasUpdates) { + this.#setNextUpdateTime(); + } + } else { + this._initWithJSON(options.json); + this._updateInterval = options.json._updateInterval ?? null; + this._updateURL = options.json._updateURL ?? null; + this._iconUpdateURL = options.json._iconUpdateURL ?? null; + } + } + + /** + * Creates a JavaScript object that represents this engine. + * + * @returns {object} + * An object suitable for serialization as JSON. + */ + toJSON() { + let json = super.toJSON(); + json._updateInterval = this._updateInterval; + json._updateURL = this._updateURL; + json._iconUpdateURL = this._iconUpdateURL; + return json; + } + + /** + * Determines if this search engine has updates url. + * + * @returns {boolean} + * Returns true if this search engine may update itself. + */ + get hasUpdates() { + // Whether or not the engine has an update URL + let selfURL = this._getURLOfType( + lazy.SearchUtils.URL_TYPE.OPENSEARCH, + "self" + ); + return !!(this._updateURL || this._iconUpdateURL || selfURL); + } + + /** + * Returns the engine's updateURI if it exists and returns null otherwise + * + * @returns {?string} + */ + get updateURI() { + let updateURL = this._getURLOfType(lazy.SearchUtils.URL_TYPE.OPENSEARCH); + let updateURI = + updateURL && updateURL._hasRelation("self") + ? updateURL.getSubmission("", this).uri + : lazy.SearchUtils.makeURI(this._updateURL); + return updateURI; + } + + /** + * Considers if this engine needs to be updated, and updates it if necessary. + */ + async maybeUpdate() { + if (!this.hasUpdates) { + return; + } + + let currentTime = Date.now(); + + let expireTime = this.getAttr("updateexpir"); + + if (!expireTime || !(expireTime <= currentTime)) { + lazy.logConsole.debug(this.name, "Skipping update, not expired yet."); + return; + } + + await this.#update(); + + this.#setNextUpdateTime(); + } + + /** + * Updates the OpenSearch engine details from the server. + */ + async #update() { + let updateURI = this.updateURI; + if (updateURI) { + let data = await lazy.loadAndParseOpenSearchEngine( + updateURI, + this.getAttr("updatelastmodified") + ); + + this.#setEngineData(data); + + lazy.SearchUtils.notifyAction( + this, + lazy.SearchUtils.MODIFIED_TYPE.CHANGED + ); + + // Keep track of the last modified date, so that we can make conditional + // server requests for future updates. + this.setAttr("updatelastmodified", new Date().toUTCString()); + } + + if (this._iconUpdateURL) { + // Force update of the icon from the icon URL. + this._setIcon(this._iconUpdateURL, true); + } + } + + /** + * Sets the data for this engine based on the OpenSearch properties. + * + * @param {OpenSearchProperties} data + * The OpenSearch data. + */ + #setEngineData(data) { + let name = data.name.trim(); + if (!this._engineToUpdate) { + if (Services.search.getEngineByName(name)) { + throw Components.Exception( + "Found a duplicate engine", + Ci.nsISearchService.ERROR_DUPLICATE_ENGINE + ); + } + } + + this._name = name; + this._description = data.description ?? ""; + this._searchForm = data.searchForm ?? ""; + this._queryCharset = data.queryCharset ?? "UTF-8"; + + for (let url of data.urls) { + let engineURL; + try { + engineURL = new EngineURL(url.type, url.method, url.template); + } catch (ex) { + throw Components.Exception( + `Failed to add ${url.template} as an Engine URL`, + Cr.NS_ERROR_FAILURE + ); + } + + if (url.rels.length) { + engineURL.rels = url.rels; + } + + for (let param of url.params) { + try { + engineURL.addParam(param.name, param.value); + } catch (ex) { + // Ignore failure + lazy.logConsole.error("OpenSearch url has an invalid param", param); + } + } + + this._urls.push(engineURL); + } + + for (let image of data.images) { + this._setIcon(image.url, image.isPrefered, image.width, image.height); + } + } + + /** + * Sets the next update time for this engine. + */ + #setNextUpdateTime() { + var interval = this._updateInterval || OPENSEARCH_DEFAULT_UPDATE_INTERVAL; + var milliseconds = interval * 86400000; // |interval| is in days + this.setAttr("updateexpir", Date.now() + milliseconds); + } + + /** + * This indicates where we found the .xml file to load the engine, + * and attempts to hide user-identifiable data (such as username). + * + * @param {string} sanitizedName + * The sanitized name of the engine. + * @param {nsIURI} uri + * The uri the engine was loaded from. + * @returns {string} + * A load path with reduced data. + */ + static getAnonymizedLoadPath(sanitizedName, uri) { + return `[${uri.scheme}]${uri.host}/${sanitizedName}.xml`; + } +} |