/* 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/. */ /** * This module exports a provider returning the user's recent searches. */ import { UrlbarProvider, UrlbarUtils, } from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { DEFAULT_FORM_HISTORY_PARAM: "moz-src:///toolkit/components/search/SearchSuggestionController.sys.mjs", FormHistory: "resource://gre/modules/FormHistory.sys.mjs", SearchUtils: "moz-src:///toolkit/components/search/SearchUtils.sys.mjs", UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs", UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs", UrlbarSearchUtils: "moz-src:///browser/components/urlbar/UrlbarSearchUtils.sys.mjs", }); // These prefs are relative to the `browser.urlbar` branch. const ENABLED_PREF = "recentsearches.featureGate"; const SUGGEST_PREF = "suggest.recentsearches"; const EXPIRATION_PREF = "recentsearches.expirationMs"; const LASTDEFAULTCHANGED_PREF = "recentsearches.lastDefaultChanged"; /** * A provider that returns the Recent Searches performed by the user. */ export class UrlbarProviderRecentSearches extends UrlbarProvider { constructor() { super(); Services.obs.addObserver(this, lazy.SearchUtils.TOPIC_ENGINE_MODIFIED); } /** * @returns {Values} */ get type() { return UrlbarUtils.PROVIDER_TYPE.PROFILE; } async isActive(queryContext) { if (queryContext.sapName == "searchbar") { // On the searchbar, we show recent searches of all engines, // regardless of searchmode or prefs. return !queryContext.searchString; } return ( lazy.UrlbarPrefs.get(ENABLED_PREF) && lazy.UrlbarPrefs.get(SUGGEST_PREF) && !queryContext.searchString && !queryContext.searchMode && !queryContext.restrictSource ); } /** * We use the same priority as `UrlbarProviderTopSites` as these are both * shown on an empty urlbar query. * * @returns {number} The provider's priority for the given query. */ getPriority() { return 1; } onEngagement(queryContext, controller, details) { let { result } = details; if (details.selType == "dismiss") { // Unlike in startQuery, do not pass the engine as `source`, // otherwise it will only remove the source relation. lazy.FormHistory.update({ op: "remove", fieldname: lazy.DEFAULT_FORM_HISTORY_PARAM, value: result.payload.suggestion, }).catch(error => console.error(`Removing form history failed: ${error}`) ); controller.removeResult(result); } } /** * Starts querying. * * @param {UrlbarQueryContext} queryContext * @param {(provider: UrlbarProvider, result: UrlbarResult) => void} addCallback * Callback invoked by the provider to add a new result. */ async startQuery(queryContext, addCallback) { let engine; if (queryContext.searchMode?.engineName) { engine = lazy.UrlbarSearchUtils.getEngineByName( queryContext.searchMode.engineName ); } else { engine = lazy.UrlbarSearchUtils.getDefaultEngine(queryContext.isPrivate); } if (!engine) { return; } let results = await lazy.FormHistory.search(["value", "lastUsed"], { fieldname: lazy.DEFAULT_FORM_HISTORY_PARAM, // Use undefined to show recent searches of all engines. source: queryContext.sapName == "searchbar" ? undefined : engine.name, }); let now = Date.now(); let expiration; if (queryContext.sapName != "searchbar") { expiration = parseInt(lazy.UrlbarPrefs.get(EXPIRATION_PREF), 10); let lastDefaultChanged = parseInt( lazy.UrlbarPrefs.get(LASTDEFAULTCHANGED_PREF), 10 ); // We only want to show searches since the last engine change, if we // havent changed the engine we expire the display of the searches // after a period of time. if (lastDefaultChanged != -1) { expiration = Math.min(expiration, now - lastDefaultChanged); } } else { expiration = Infinity; } results = results.filter( result => now - Math.floor(result.lastUsed / 1000) < expiration ); results.sort((a, b) => b.lastUsed - a.lastUsed); if ( queryContext.sapName != "searchbar" && results.length > lazy.UrlbarPrefs.get("recentsearches.maxResults") ) { results.length = lazy.UrlbarPrefs.get("recentsearches.maxResults"); } for (let result of results) { let res = new lazy.UrlbarResult({ type: UrlbarUtils.RESULT_TYPE.SEARCH, source: UrlbarUtils.RESULT_SOURCE.HISTORY, payload: { engine: engine.name, suggestion: result.value, title: result.value, isBlockable: true, blockL10n: { id: "urlbar-result-menu-remove-from-history" }, helpUrl: Services.urlFormatter.formatURLPref("app.support.baseURL") + "awesome-bar-result-menu", }, }); addCallback(this, res); } } observe(subject, topic, data) { switch (data) { case lazy.SearchUtils.MODIFIED_TYPE.DEFAULT: lazy.UrlbarPrefs.set(LASTDEFAULTCHANGED_PREF, Date.now().toString()); break; } } }