forked from mirrors/gecko-dev
The current process creates the OpenSearchEngine and gets it to load its data. Once the data is loaded, it notifies the SearchService that is ready to be added. This changes the structure so that the SearchService loads the data, creates the engine and then adds it straight away. This also means that the contextual search provider can be simplified and doesn't need to tell the engine not to add itself to the SearchService. Differential Revision: https://phabricator.services.mozilla.com/D198814
257 lines
7.1 KiB
JavaScript
257 lines
7.1 KiB
JavaScript
/* 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`;
|
|
}
|
|
}
|