summaryrefslogtreecommitdiffstats
path: root/toolkit/components/search/SearchSuggestions.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/search/SearchSuggestions.sys.mjs
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/search/SearchSuggestions.sys.mjs')
-rw-r--r--toolkit/components/search/SearchSuggestions.sys.mjs221
1 files changed, 221 insertions, 0 deletions
diff --git a/toolkit/components/search/SearchSuggestions.sys.mjs b/toolkit/components/search/SearchSuggestions.sys.mjs
new file mode 100644
index 0000000000..16ba9668f0
--- /dev/null
+++ b/toolkit/components/search/SearchSuggestions.sys.mjs
@@ -0,0 +1,221 @@
+/* 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/. */
+
+import { FormAutoCompleteResult } from "resource://gre/modules/nsFormAutoCompleteResult.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ FormAutoCompleteResult: "resource://gre/modules/FormAutoComplete.sys.mjs",
+ FormHistoryClient: "resource://gre/modules/FormAutoComplete.sys.mjs",
+
+ SearchSuggestionController:
+ "resource://gre/modules/SearchSuggestionController.sys.mjs",
+});
+
+/**
+ * SuggestAutoComplete is a base class that implements nsIAutoCompleteSearch
+ * and can collect results for a given search by using this.#suggestionController.
+ * We do it this way since the AutoCompleteController in Mozilla requires a
+ * unique XPCOM Service for every search provider, even if the logic for two
+ * providers is identical.
+ *
+ * @class
+ */
+class SuggestAutoComplete {
+ constructor() {
+ this.#suggestionController = new lazy.SearchSuggestionController();
+ this.#suggestionController.maxLocalResults = this.#historyLimit;
+ }
+
+ /**
+ * Notifies the front end of new results.
+ *
+ * @param {string} searchString
+ * The user's query string.
+ * @param {Array} results
+ * An array of results to the search.
+ * @param {object} formHistoryResult
+ * Any previous form history result.
+ * @private
+ */
+ onResultsReady(searchString, results, formHistoryResult) {
+ if (this.#listener) {
+ let result = new FormAutoCompleteResult(
+ searchString,
+ Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
+ 0,
+ "",
+ results.map(result => ({
+ value: result,
+ label: result,
+ // We supply the comments field so that autocomplete does not kick
+ // in the unescaping of the results for display which it uses for
+ // urls.
+ comment: result,
+ removable: true,
+ })),
+ formHistoryResult
+ );
+
+ this.#listener.onSearchResult(this, result);
+
+ // Null out listener to make sure we don't notify it twice
+ this.#listener = null;
+ }
+ }
+
+ /**
+ * Initiates the search result gathering process. Part of
+ * nsIAutoCompleteSearch implementation.
+ *
+ * @param {string} searchString
+ * The user's query string.
+ * @param {string} searchParam
+ * unused, "an extra parameter"; even though this parameter and the
+ * next are unused, pass them through in case the form history
+ * service wants them
+ * @param {object} previousResult
+ * unused, a client-cached store of the previous generated resultset
+ * for faster searching.
+ * @param {object} listener
+ * object implementing nsIAutoCompleteObserver which we notify when
+ * results are ready.
+ */
+ startSearch(searchString, searchParam, previousResult, listener) {
+ var formHistorySearchParam = searchParam.split("|")[0];
+
+ // Receive the information about the privacy mode of the window to which
+ // this search box belongs. The front-end's search.xml bindings passes this
+ // information in the searchParam parameter. The alternative would have
+ // been to modify nsIAutoCompleteSearch to add an argument to startSearch
+ // and patch all of autocomplete to be aware of this, but the searchParam
+ // argument is already an opaque argument, so this solution is hopefully
+ // less hackish (although still gross.)
+ var privacyMode = searchParam.split("|")[1] == "private";
+
+ // Start search immediately if possible, otherwise once the search
+ // service is initialized
+ if (Services.search.isInitialized) {
+ this.#triggerSearch(
+ searchString,
+ formHistorySearchParam,
+ listener,
+ privacyMode
+ ).catch(console.error);
+ return;
+ }
+
+ Services.search
+ .init()
+ .then(() => {
+ this.#triggerSearch(
+ searchString,
+ formHistorySearchParam,
+ listener,
+ privacyMode
+ ).catch(console.error);
+ })
+ .catch(result =>
+ console.error(
+ "Could not initialize search service, bailing out: " + result
+ )
+ );
+ }
+
+ /**
+ * Ends the search result gathering process. Part of nsIAutoCompleteSearch
+ * implementation.
+ */
+ stopSearch() {
+ this.#suggestionController.stop();
+ }
+
+ #suggestionController;
+
+ /**
+ * Maximum number of history items displayed. This is capped at 7
+ * because the primary consumer (Firefox search bar) displays 10 rows
+ * by default, and so we want to leave some space for suggestions
+ * to be visible.
+ *
+ * @type {number}
+ */
+ #historyLimit = 7;
+
+ /**
+ * The object implementing nsIAutoCompleteObserver that we notify when
+ * we have found results.
+ *
+ * @type {object|null}
+ */
+ #listener = null;
+
+ /**
+ * Actual implementation of search.
+ *
+ * @param {string} searchString
+ * The user's query string.
+ * @param {string} searchParam
+ * unused
+ * @param {object} listener
+ * object implementing nsIAutoCompleteObserver which we notify when
+ * results are ready.
+ * @param {boolean} privacyMode
+ * True if the search was made from a private browsing mode context.
+ */
+ async #triggerSearch(searchString, searchParam, listener, privacyMode) {
+ this.#listener = listener;
+ let results = await this.#suggestionController.fetch(
+ searchString,
+ privacyMode,
+ Services.search.defaultEngine
+ );
+
+ // If form history has results, add them to the list.
+ let finalResults = results.local.map(r => r.value);
+
+ // If there are remote matches, add them.
+ if (results.remote.length) {
+ // now put the history results above the suggestions
+ // We shouldn't show tail suggestions in their full-text form.
+ let nonTailEntries = results.remote.filter(
+ e => !e.matchPrefix && !e.tail
+ );
+ finalResults = finalResults.concat(nonTailEntries.map(e => e.value));
+ }
+
+ // Bug 1822297: This re-uses the wrappers from Satchel, to avoid re-writing
+ // our own nsIAutoCompleteSimpleResult implementation for now. However,
+ // we should do that at some stage to remove the dependency on satchel.
+ let client = new lazy.FormHistoryClient({
+ formField: null,
+ inputName: this.#suggestionController.formHistoryParam,
+ });
+ let formHistoryResult = new lazy.FormAutoCompleteResult(
+ client,
+ results.formHistoryResults,
+ this.#suggestionController.formHistoryParam,
+ searchString
+ );
+
+ // Notify the FE of our new results
+ this.onResultsReady(results.term, finalResults, formHistoryResult);
+ }
+
+ QueryInterface = ChromeUtils.generateQI([
+ "nsIAutoCompleteSearch",
+ "nsIAutoCompleteObserver",
+ ]);
+}
+
+/**
+ * SearchSuggestAutoComplete is a service implementation that handles suggest
+ * results specific to web searches.
+ *
+ * @class
+ */
+export class SearchSuggestAutoComplete extends SuggestAutoComplete {
+ classID = Components.ID("{aa892eb4-ffbf-477d-9f9a-06c995ae9f27}");
+ serviceURL = "";
+}