Files
tubestation/toolkit/components/search/UserSearchEngine.sys.mjs

173 lines
4.9 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 {
SearchEngine,
EngineURL,
} from "resource://gre/modules/SearchEngine.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
});
/**
* @typedef FormInfo
*
* Information about a search engine. This is similar to the WebExtension
* style object used by `SearchEngine._initWithDetails` but it contains a
* FormData object so it can easily be generated from an HTML form.
*
* Either `url` or `formData` must contain {searchTerms}.
*
* @property {string} url
* The url template for searches.
* @property {string} name
* The name of the engine.
* @property {FormData} [formData]
* The search parameters. May only contain string values.
* @property {string} [charset]
* The encoding for the requests. Defaults to `SearchUtils.DEFAULT_QUERY_CHARSET`.
* @property {string} [method]
* The HTTP method. Defaults to GET.
* @property {string} [icon]
* A URL to the engine's icon.
* @property {string} [alias]
* An engine keyword.
* @property {string} [suggestUrl]
* The url template for suggestions.
*/
/**
* UserSearchEngine represents a search engine defined by a user or generated
* from a web page.
*/
export class UserSearchEngine extends SearchEngine {
/**
* Creates a UserSearchEngine from either a FormInfo object or JSON settings.
*
* @param {object} options
* The options for this search engine.
* @param {FormInfo} [options.formInfo]
* General information about the search engine.
* @param {object} [options.json]
* An object that represents the saved JSON settings for the engine.
*/
constructor(options = {}) {
super({
loadPath: "[user]",
});
if (options.formInfo) {
this.#initWithFormInfo(options.formInfo);
} else {
this._initWithJSON(options.json);
}
}
/**
* Generates a search engine from a FormInfo object.
* The alias is treated as a user-defined alias.
*
* @param {FormInfo} formInfo
* General information about the search engine.
*/
#initWithFormInfo(formInfo) {
this._name = formInfo.name.trim();
let charset = formInfo.charset ?? lazy.SearchUtils.DEFAULT_QUERY_CHARSET;
let url = new EngineURL(
lazy.SearchUtils.URL_TYPE.SEARCH,
formInfo.method ?? "GET",
formInfo.url
);
for (let [key, value] of formInfo.formData ?? []) {
if (typeof value != "string") {
throw new Error("Non-string values are not supported.");
}
url.addParam(
Services.textToSubURI.ConvertAndEscape(charset, key),
Services.textToSubURI
.ConvertAndEscape(charset, value)
.replaceAll("%7BsearchTerms%7D", "{searchTerms}")
);
}
this._urls.push(url);
if (formInfo.suggestUrl) {
let suggestUrl = new EngineURL(
lazy.SearchUtils.URL_TYPE.SUGGEST_JSON,
"GET",
formInfo.suggestUrl
);
this._urls.push(suggestUrl);
}
if (formInfo.icon) {
this._setIcon(formInfo.icon);
}
if (formInfo.charset) {
this._queryCharset = formInfo.charset;
}
this.alias = formInfo.alias;
}
/**
* Returns the appropriate identifier to use for telemetry.
*
* @returns {string}
*/
get telemetryId() {
return `other-${this.name}`;
}
/**
* Changes the name of the engine if the new name is available.
*
* @param {string} newName
* The new name.
* @returns {bool}
* Whether the name was changed successfully.
*/
rename(newName) {
if (newName == this.name) {
return true;
} else if (Services.search.getEngineByName(newName)) {
return false;
}
this._name = newName;
lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED);
return true;
}
/**
* Changes the url of the specified type.
* The HTTP method is determined by whether postData is null.
*
* @param {string} type
* The type of url to change. Must be a `SearchUtils.URL_TYPE`.
* @param {string} template
* The URL to which search queries should be sent. Should contain
* "{searchTerms}" as the placeholder for the search terms for GET
* requests.
* @param {?string} postData
* x-www-form-urlencoded body containing "{searchTerms}" for POST or
* null for GET.
*/
changeUrl(type, template, postData) {
this._urls = this._urls.filter(url => url.type != type);
let method = postData ? "POST" : "GET";
let url = new EngineURL(type, method, template);
for (let [key, value] of new URLSearchParams(postData ?? "").entries()) {
url.addParam(key, value);
}
this._urls.push(url);
lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED);
}
}