diff options
Diffstat (limited to 'toolkit/modules/KeywordUtils.sys.mjs')
-rw-r--r-- | toolkit/modules/KeywordUtils.sys.mjs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/toolkit/modules/KeywordUtils.sys.mjs b/toolkit/modules/KeywordUtils.sys.mjs new file mode 100644 index 0000000000..2765dd3ce9 --- /dev/null +++ b/toolkit/modules/KeywordUtils.sys.mjs @@ -0,0 +1,119 @@ +/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", +}); + +export var KeywordUtils = { + /** + * Replaces %s or %S in the provided url or postData with the given parameter, + * acccording to the best charset for the given url. + * + * @return [url, postData] + * @throws if nor url nor postData accept a param, but a param was provided. + */ + async parseUrlAndPostData(url, postData, param) { + let hasGETParam = /%s/i.test(url); + let decodedPostData = postData ? unescape(postData) : ""; + let hasPOSTParam = /%s/i.test(decodedPostData); + + if (!hasGETParam && !hasPOSTParam) { + if (param) { + // If nor the url, nor postData contain parameters, but a parameter was + // provided, return the original input. + throw new Error( + "A param was provided but there's nothing to bind it to" + ); + } + return [url, postData]; + } + + let charset = ""; + const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/; + let matches = url.match(re); + if (matches) { + [, url, charset] = matches; + } else { + // Try to fetch a charset from History. + try { + // Will return an empty string if character-set is not found. + let pageInfo = await lazy.PlacesUtils.history.fetch(url, { + includeAnnotations: true, + }); + if ( + pageInfo && + pageInfo.annotations.has(lazy.PlacesUtils.CHARSET_ANNO) + ) { + charset = pageInfo.annotations.get(lazy.PlacesUtils.CHARSET_ANNO); + } + } catch (ex) { + // makeURI() throws if url is invalid. + console.error(ex); + } + } + + // encodeURIComponent produces UTF-8, and cannot be used for other charsets. + // escape() works in those cases, but it doesn't uri-encode +, @, and /. + // Therefore we need to manually replace these ASCII characters by their + // encodeURIComponent result, to match the behavior of nsEscape() with + // url_XPAlphas. + let encodedParam = ""; + if (charset && charset != "UTF-8") { + try { + let converter = Cc[ + "@mozilla.org/intl/scriptableunicodeconverter" + ].createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = charset; + encodedParam = converter.ConvertFromUnicode(param) + converter.Finish(); + } catch (ex) { + encodedParam = param; + } + encodedParam = escape(encodedParam).replace( + /[+@\/]+/g, + encodeURIComponent + ); + } else { + // Default charset is UTF-8 + encodedParam = encodeURIComponent(param); + } + + url = url.replace(/%s/g, encodedParam).replace(/%S/g, param); + if (hasPOSTParam) { + postData = decodedPostData + .replace(/%s/g, encodedParam) + .replace(/%S/g, param); + } + return [url, postData]; + }, + + /** + * Returns a set of parameters if a keyword is registered and the search + * string can be bound to it. + * + * @param {string} keyword The typed keyword. + * @param {string} searchString The full search string, including the keyword. + * @returns { entry, url, postData } + */ + async getBindableKeyword(keyword, searchString) { + let entry = await lazy.PlacesUtils.keywords.fetch(keyword); + if (!entry) { + return {}; + } + + try { + let [url, postData] = await this.parseUrlAndPostData( + entry.url.href, + entry.postData, + searchString + ); + return { entry, url, postData }; + } catch (ex) { + return {}; + } + }, +}; |