655 lines
21 KiB
JavaScript
655 lines
21 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/. */
|
|
|
|
/**
|
|
* This module exports a provider that offers search engine suggestions.
|
|
*/
|
|
|
|
import {
|
|
SkippableTimer,
|
|
UrlbarProvider,
|
|
UrlbarUtils,
|
|
} from "resource:///modules/UrlbarUtils.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
FormHistory: "resource://gre/modules/FormHistory.sys.mjs",
|
|
SearchSuggestionController:
|
|
"moz-src:///toolkit/components/search/SearchSuggestionController.sys.mjs",
|
|
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
|
UrlbarProviderTopSites: "resource:///modules/UrlbarProviderTopSites.sys.mjs",
|
|
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
|
UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
|
|
UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.sys.mjs",
|
|
});
|
|
|
|
const RESULT_MENU_COMMANDS = {
|
|
TRENDING_BLOCK: "trendingblock",
|
|
TRENDING_HELP: "help",
|
|
};
|
|
|
|
const TRENDING_HELP_URL =
|
|
Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
|
"google-trending-searches-on-awesomebar";
|
|
|
|
/**
|
|
* Returns whether the passed in string looks like a url.
|
|
*
|
|
* @param {string} str
|
|
* The string to check.
|
|
* @param {boolean} [ignoreAlphanumericHosts]
|
|
* If true, don't consider a string with an alphanumeric host to be a URL.
|
|
* @returns {boolean}
|
|
* True if the query looks like a URL.
|
|
*/
|
|
function looksLikeUrl(str, ignoreAlphanumericHosts = false) {
|
|
// Single word including special chars.
|
|
return (
|
|
!lazy.UrlbarTokenizer.REGEXP_SPACES.test(str) &&
|
|
(["/", "@", ":", "["].some(c => str.includes(c)) ||
|
|
(ignoreAlphanumericHosts
|
|
? /^([\[\]A-Z0-9-]+\.){3,}[^.]+$/i.test(str)
|
|
: str.includes(".")))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Class used to create the provider.
|
|
*/
|
|
class ProviderSearchSuggestions extends UrlbarProvider {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Returns the name of this provider.
|
|
*
|
|
* @returns {string} the name of this provider.
|
|
*/
|
|
get name() {
|
|
return "SearchSuggestions";
|
|
}
|
|
|
|
/**
|
|
* @returns {Values<typeof UrlbarUtils.PROVIDER_TYPE>}
|
|
*/
|
|
get type() {
|
|
return UrlbarUtils.PROVIDER_TYPE.NETWORK;
|
|
}
|
|
|
|
/**
|
|
* Whether this provider should be invoked for the given context.
|
|
* If this method returns false, the providers manager won't start a query
|
|
* with this provider, to save on resources.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext The query context object
|
|
*/
|
|
async isActive(queryContext) {
|
|
// If the sources don't include search or the user used a restriction
|
|
// character other than search, don't allow any suggestions.
|
|
if (
|
|
!queryContext.sources.includes(UrlbarUtils.RESULT_SOURCE.SEARCH) ||
|
|
(queryContext.restrictSource &&
|
|
queryContext.restrictSource != UrlbarUtils.RESULT_SOURCE.SEARCH)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// No suggestions for empty search strings, unless we are restricting to
|
|
// search or showing trending suggestions.
|
|
if (
|
|
!queryContext.trimmedSearchString &&
|
|
!this._isTokenOrRestrictionPresent(queryContext) &&
|
|
!this.#shouldFetchTrending(queryContext)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (!this._allowSuggestions(queryContext)) {
|
|
return false;
|
|
}
|
|
|
|
let wantsLocalSuggestions =
|
|
lazy.UrlbarPrefs.get("maxHistoricalSearchSuggestions") &&
|
|
queryContext.trimmedSearchString;
|
|
|
|
return (
|
|
!!wantsLocalSuggestions || this._allowRemoteSuggestions(queryContext)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the user typed a token alias or restriction token, or is in
|
|
* search mode. We use this value to override the pref to disable search
|
|
* suggestions in the Urlbar.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext The query context object.
|
|
* @returns {boolean} True if the user typed a token alias or search
|
|
* restriction token.
|
|
*/
|
|
_isTokenOrRestrictionPresent(queryContext) {
|
|
return (
|
|
queryContext.searchString.startsWith("@") ||
|
|
(queryContext.restrictSource &&
|
|
queryContext.restrictSource == UrlbarUtils.RESULT_SOURCE.SEARCH) ||
|
|
queryContext.tokens.some(
|
|
t => t.type == lazy.UrlbarTokenizer.TYPE.RESTRICT_SEARCH
|
|
) ||
|
|
(queryContext.searchMode &&
|
|
queryContext.sources.includes(UrlbarUtils.RESULT_SOURCE.SEARCH))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns whether suggestions in general are allowed for a given query
|
|
* context. If this returns false, then we shouldn't fetch either form
|
|
* history or remote suggestions.
|
|
*
|
|
* @param {object} queryContext The query context object
|
|
* @returns {boolean} True if suggestions in general are allowed and false if
|
|
* not.
|
|
*/
|
|
_allowSuggestions(queryContext) {
|
|
if (
|
|
// If the user typed a restriction token or token alias, we ignore the
|
|
// pref to disable suggestions in the Urlbar.
|
|
(!lazy.UrlbarPrefs.get("suggest.searches") &&
|
|
!this._isTokenOrRestrictionPresent(queryContext)) ||
|
|
!lazy.UrlbarPrefs.get("browser.search.suggest.enabled") ||
|
|
(queryContext.isPrivate &&
|
|
!lazy.UrlbarPrefs.get("browser.search.suggest.enabled.private"))
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether remote suggestions are allowed for a given query context.
|
|
*
|
|
* @param {object} queryContext The query context object
|
|
* @param {string} [searchString] The effective search string without
|
|
* restriction tokens or aliases. Defaults to the context searchString.
|
|
* @returns {boolean} True if remote suggestions are allowed and false if not.
|
|
*/
|
|
_allowRemoteSuggestions(
|
|
queryContext,
|
|
searchString = queryContext.searchString
|
|
) {
|
|
// This is checked by `queryContext.allowRemoteResults` below, but we can
|
|
// short-circuit that call with the `_isTokenOrRestrictionPresent` block
|
|
// before that. Make sure we don't allow remote suggestions if this is set.
|
|
if (queryContext.prohibitRemoteResults) {
|
|
return false;
|
|
}
|
|
|
|
// Allow remote suggestions if trending suggestions are enabled.
|
|
if (this.#shouldFetchTrending(queryContext)) {
|
|
return true;
|
|
}
|
|
|
|
if (!searchString.trim()) {
|
|
return false;
|
|
}
|
|
|
|
// Skip all remaining checks and allow remote suggestions at this point if
|
|
// the user used a token alias or restriction token. We want "@engine query"
|
|
// to return suggestions from the engine. We'll return early from startQuery
|
|
// if the query doesn't match an alias.
|
|
if (this._isTokenOrRestrictionPresent(queryContext)) {
|
|
return true;
|
|
}
|
|
|
|
// If the user is just adding on to a query that previously didn't return
|
|
// many remote suggestions, we are unlikely to get any more results.
|
|
if (
|
|
!!this._lastLowResultsSearchSuggestion &&
|
|
searchString.length > this._lastLowResultsSearchSuggestion.length &&
|
|
searchString.startsWith(this._lastLowResultsSearchSuggestion)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return queryContext.allowRemoteResults(
|
|
searchString,
|
|
lazy.UrlbarPrefs.get("trending.featureGate")
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Starts querying.
|
|
*
|
|
* @param {object} queryContext The query context object
|
|
* @param {Function} addCallback Callback invoked by the provider to add a new
|
|
* result.
|
|
* @returns {Promise} resolved when the query stops.
|
|
*/
|
|
async startQuery(queryContext, addCallback) {
|
|
let instance = this.queryInstance;
|
|
|
|
let aliasEngine = await this._maybeGetAlias(queryContext);
|
|
if (!aliasEngine) {
|
|
// Autofill matches queries starting with "@" to token alias engines.
|
|
// If the string starts with "@", but an alias engine is not yet
|
|
// matched, then autofill might still be filtering token alias
|
|
// engine results. We don't want to mix search suggestions with those
|
|
// engine results, so we return early. See bug 1551049 comment 1 for
|
|
// discussion on how to improve this behavior.
|
|
if (queryContext.searchString.startsWith("@")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
let query = aliasEngine
|
|
? aliasEngine.query
|
|
: UrlbarUtils.substringAt(
|
|
queryContext.searchString,
|
|
queryContext.tokens[0]?.value || ""
|
|
).trim();
|
|
|
|
let leadingRestrictionToken = null;
|
|
if (
|
|
lazy.UrlbarTokenizer.isRestrictionToken(queryContext.tokens[0]) &&
|
|
(queryContext.tokens.length > 1 ||
|
|
queryContext.tokens[0].type ==
|
|
lazy.UrlbarTokenizer.TYPE.RESTRICT_SEARCH)
|
|
) {
|
|
leadingRestrictionToken = queryContext.tokens[0].value;
|
|
}
|
|
|
|
// Strip a leading search restriction char, because we prepend it to text
|
|
// when the search shortcut is used and it's not user typed. Don't strip
|
|
// other restriction chars, so that it's possible to search for things
|
|
// including one of those (e.g. "c#").
|
|
if (leadingRestrictionToken === lazy.UrlbarTokenizer.RESTRICT.SEARCH) {
|
|
query = UrlbarUtils.substringAfter(query, leadingRestrictionToken).trim();
|
|
}
|
|
|
|
// Find our search engine. It may have already been set with an alias.
|
|
let engine;
|
|
if (aliasEngine) {
|
|
engine = aliasEngine.engine;
|
|
} else if (queryContext.searchMode?.engineName) {
|
|
engine = lazy.UrlbarSearchUtils.getEngineByName(
|
|
queryContext.searchMode.engineName
|
|
);
|
|
} else {
|
|
engine = lazy.UrlbarSearchUtils.getDefaultEngine(queryContext.isPrivate);
|
|
}
|
|
|
|
if (!engine) {
|
|
return;
|
|
}
|
|
|
|
let alias = (aliasEngine && aliasEngine.alias) || "";
|
|
let results = await this._fetchSearchSuggestions(
|
|
queryContext,
|
|
engine,
|
|
query,
|
|
alias
|
|
);
|
|
|
|
if (!results || instance != this.queryInstance) {
|
|
return;
|
|
}
|
|
|
|
for (let result of results) {
|
|
addCallback(this, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the provider's priority.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext The query context object
|
|
* @returns {number} The provider's priority for the given query.
|
|
*/
|
|
getPriority(queryContext) {
|
|
if (this.#shouldFetchTrending(queryContext)) {
|
|
return lazy.UrlbarProviderTopSites.PRIORITY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Cancels a running query.
|
|
*/
|
|
cancelQuery() {
|
|
if (this._suggestionsController) {
|
|
this._suggestionsController.stop();
|
|
this._suggestionsController = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the menu commands to be shown for trending results.
|
|
*
|
|
* @param {UrlbarResult} result
|
|
* The result to get menu comands for.
|
|
*
|
|
* @returns {Array} The commands to be shown.
|
|
*/
|
|
getResultCommands(result) {
|
|
if (result.payload.trending) {
|
|
return [
|
|
{
|
|
name: RESULT_MENU_COMMANDS.TRENDING_BLOCK,
|
|
l10n: { id: "urlbar-result-menu-trending-dont-show" },
|
|
},
|
|
{
|
|
name: "separator",
|
|
},
|
|
{
|
|
name: RESULT_MENU_COMMANDS.TRENDING_HELP,
|
|
l10n: { id: "urlbar-result-menu-trending-why" },
|
|
},
|
|
];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
onEngagement(queryContext, controller, details) {
|
|
let { result } = details;
|
|
if (details.selType == "dismiss" && queryContext.formHistoryName) {
|
|
lazy.FormHistory.update({
|
|
op: "remove",
|
|
fieldname: queryContext.formHistoryName,
|
|
value: result.payload.suggestion,
|
|
}).catch(error =>
|
|
console.error(`Removing form history failed: ${error}`)
|
|
);
|
|
controller.removeResult(result);
|
|
return;
|
|
}
|
|
|
|
switch (details.selType) {
|
|
case RESULT_MENU_COMMANDS.TRENDING_HELP:
|
|
// Handled by UrlbarInput
|
|
break;
|
|
case RESULT_MENU_COMMANDS.TRENDING_BLOCK:
|
|
lazy.UrlbarPrefs.set("suggest.trending", false);
|
|
this.#recordTrendingBlockedTelemetry(details.selType);
|
|
this.#replaceTrendingResultWithAcknowledgement(controller);
|
|
break;
|
|
}
|
|
}
|
|
|
|
async _fetchSearchSuggestions(queryContext, engine, searchString, alias) {
|
|
if (!engine) {
|
|
return null;
|
|
}
|
|
|
|
this._suggestionsController = new lazy.SearchSuggestionController(
|
|
queryContext.formHistoryName
|
|
);
|
|
|
|
// If there's a form history entry that equals the search string, the search
|
|
// suggestions controller will include it, and we'll make a result for it.
|
|
// If the heuristic result ends up being a search result, the muxer will
|
|
// discard the form history result since it dupes the heuristic, and the
|
|
// final list of results would be left with `count` - 1 form history results
|
|
// instead of `count`. Therefore we request `count` + 1 entries. The muxer
|
|
// will dedupe and limit the final form history count as appropriate.
|
|
this._suggestionsController.maxLocalResults = queryContext.maxResults + 1;
|
|
|
|
// Request maxResults + 1 remote suggestions for the same reason we request
|
|
// maxResults + 1 form history entries.
|
|
let allowRemote = this._allowRemoteSuggestions(queryContext, searchString);
|
|
this._suggestionsController.maxRemoteResults = allowRemote
|
|
? queryContext.maxResults + 1
|
|
: 0;
|
|
|
|
if (allowRemote && this.#shouldFetchTrending(queryContext)) {
|
|
if (
|
|
queryContext.searchMode &&
|
|
lazy.UrlbarPrefs.get("trending.maxResultsSearchMode") != -1
|
|
) {
|
|
this._suggestionsController.maxRemoteResults = lazy.UrlbarPrefs.get(
|
|
"trending.maxResultsSearchMode"
|
|
);
|
|
} else if (
|
|
!queryContext.searchMode &&
|
|
lazy.UrlbarPrefs.get("trending.maxResultsNoSearchMode") != -1
|
|
) {
|
|
this._suggestionsController.maxRemoteResults = lazy.UrlbarPrefs.get(
|
|
"trending.maxResultsNoSearchMode"
|
|
);
|
|
}
|
|
}
|
|
|
|
this._suggestionsFetchCompletePromise = this._suggestionsController.fetch(
|
|
searchString,
|
|
queryContext.isPrivate,
|
|
engine,
|
|
queryContext.userContextId,
|
|
this._isTokenOrRestrictionPresent(queryContext),
|
|
false,
|
|
this.#shouldFetchTrending(queryContext)
|
|
);
|
|
|
|
// See `SearchSuggestionsController.fetch` documentation for a description
|
|
// of `fetchData`.
|
|
let fetchData = await this._suggestionsFetchCompletePromise;
|
|
// The fetch was canceled.
|
|
if (!fetchData) {
|
|
return null;
|
|
}
|
|
|
|
let results = [];
|
|
|
|
// maxHistoricalSearchSuggestions used to determine the initial number of
|
|
// form history results, with the special case where zero means to never
|
|
// show form history at all. With the introduction of flexed result
|
|
// groups, we now use it only as a boolean: Zero means don't show form
|
|
// history at all (as before), non-zero means show it.
|
|
if (lazy.UrlbarPrefs.get("maxHistoricalSearchSuggestions")) {
|
|
for (let entry of fetchData.local) {
|
|
results.push(makeFormHistoryResult(queryContext, engine, entry));
|
|
}
|
|
}
|
|
|
|
// If we don't return many results, then keep track of the query. If the
|
|
// user just adds on to the query, we won't fetch more suggestions if the
|
|
// query is very long since we are unlikely to get any.
|
|
if (
|
|
allowRemote &&
|
|
!fetchData.remote.length &&
|
|
searchString.length > lazy.UrlbarPrefs.get("maxCharsForSearchSuggestions")
|
|
) {
|
|
this._lastLowResultsSearchSuggestion = searchString;
|
|
}
|
|
|
|
// If we have only tail suggestions, we only show them if we have no other
|
|
// results. We need to wait for other results to arrive to avoid flickering.
|
|
// We will wait for this timer unless we have suggestions that don't have a
|
|
// tail.
|
|
let tailTimer = new SkippableTimer({
|
|
name: "ProviderSearchSuggestions",
|
|
time: 100,
|
|
logger: this.logger,
|
|
});
|
|
|
|
for (let entry of fetchData.remote) {
|
|
if (looksLikeUrl(entry.value)) {
|
|
continue;
|
|
}
|
|
|
|
let tail = entry.tail;
|
|
let tailPrefix = entry.matchPrefix;
|
|
|
|
// Skip tail suggestions if the pref is disabled.
|
|
if (tail && !lazy.UrlbarPrefs.get("richSuggestions.tail")) {
|
|
continue;
|
|
}
|
|
|
|
if (!tail) {
|
|
await tailTimer.fire().catch(ex => this.logger.error(ex));
|
|
}
|
|
|
|
try {
|
|
let payload = {
|
|
engine: [engine.name, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
suggestion: [entry.value, UrlbarUtils.HIGHLIGHT.SUGGESTED],
|
|
lowerCaseSuggestion: entry.value.toLocaleLowerCase(),
|
|
tailPrefix,
|
|
tail: [tail, UrlbarUtils.HIGHLIGHT.SUGGESTED],
|
|
tailOffsetIndex: tail ? entry.tailOffsetIndex : undefined,
|
|
keyword: [alias ? alias : undefined, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
trending: entry.trending,
|
|
description: entry.description || undefined,
|
|
query: [searchString.trim(), UrlbarUtils.HIGHLIGHT.NONE],
|
|
icon: !entry.value ? await engine.getIconURL() : entry.icon,
|
|
};
|
|
|
|
if (entry.trending) {
|
|
payload.helpUrl = TRENDING_HELP_URL;
|
|
}
|
|
|
|
results.push(
|
|
Object.assign(
|
|
new lazy.UrlbarResult(
|
|
UrlbarUtils.RESULT_TYPE.SEARCH,
|
|
UrlbarUtils.RESULT_SOURCE.SEARCH,
|
|
...lazy.UrlbarResult.payloadAndSimpleHighlights(
|
|
queryContext.tokens,
|
|
payload
|
|
)
|
|
),
|
|
{ isRichSuggestion: !!entry.icon }
|
|
)
|
|
);
|
|
} catch (err) {
|
|
this.logger.error(err);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
await tailTimer.promise;
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* @typedef {object} EngineAlias
|
|
*
|
|
* @property {nsISearchEngine} engine
|
|
* The search engine
|
|
* @property {string} alias
|
|
* The search engine's alias
|
|
* @property {string} query
|
|
* The remainder of the search engine string after the alias
|
|
*/
|
|
|
|
/**
|
|
* Searches for an engine alias given the queryContext.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext
|
|
* The query context object.
|
|
* @returns {Promise<EngineAlias?>} aliasEngine
|
|
* A representation of the aliased engine. Null if there's no match.
|
|
*/
|
|
async _maybeGetAlias(queryContext) {
|
|
if (queryContext.searchMode) {
|
|
// If we're in search mode, don't try to parse an alias at all.
|
|
return null;
|
|
}
|
|
|
|
let possibleAlias = queryContext.tokens[0]?.value;
|
|
// "@" on its own is handled by UrlbarProviderTokenAliasEngines and returns
|
|
// a list of every available token alias.
|
|
if (!possibleAlias || possibleAlias == "@") {
|
|
return null;
|
|
}
|
|
|
|
let query = UrlbarUtils.substringAfter(
|
|
queryContext.searchString,
|
|
possibleAlias
|
|
);
|
|
|
|
// Match an alias only when it has a space after it. If there's no trailing
|
|
// space, then continue to treat it as part of the search string.
|
|
if (!lazy.UrlbarTokenizer.REGEXP_SPACES_START.test(query)) {
|
|
return null;
|
|
}
|
|
|
|
// Check if the user entered an engine alias directly.
|
|
let engineMatch =
|
|
await lazy.UrlbarSearchUtils.engineForAlias(possibleAlias);
|
|
if (engineMatch) {
|
|
return {
|
|
engine: engineMatch,
|
|
alias: possibleAlias,
|
|
query: query.trim(),
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Whether we should show trending suggestions. These are shown when the
|
|
* user enters a specific engines searchMode when enabled, the
|
|
* seperate `requireSearchMode` pref controls whether they are visible
|
|
* when the urlbar is first opened without any search mode.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext
|
|
* The query context object.
|
|
* @returns {boolean}
|
|
* Whether we should fetch trending results.
|
|
*/
|
|
#shouldFetchTrending(queryContext) {
|
|
return !!(
|
|
queryContext.searchString == "" &&
|
|
lazy.UrlbarPrefs.get("trending.featureGate") &&
|
|
lazy.UrlbarPrefs.get("suggest.trending") &&
|
|
(queryContext.searchMode ||
|
|
!lazy.UrlbarPrefs.get("trending.requireSearchMode"))
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Send telemetry to indicating trending results have been hidden.
|
|
*/
|
|
#recordTrendingBlockedTelemetry() {
|
|
Glean.urlbarTrending.block.add(1);
|
|
}
|
|
|
|
/*
|
|
* Remove all the trending results and show an acknowledgement that the
|
|
* trending suggestions have been turned off.
|
|
*/
|
|
#replaceTrendingResultWithAcknowledgement(controller) {
|
|
let resultsToRemove = controller.view.visibleResults.filter(
|
|
result => result.payload.trending
|
|
);
|
|
if (resultsToRemove.length) {
|
|
// Show an acknowledgement tip for the first result.
|
|
resultsToRemove[0].acknowledgeDismissalL10n = {
|
|
id: "urlbar-trending-dismissal-acknowledgment",
|
|
};
|
|
}
|
|
// Remove results in reverse order so the acknowledgment tip isn't removed.
|
|
resultsToRemove.reverse();
|
|
resultsToRemove.forEach(result => controller.removeResult(result));
|
|
}
|
|
}
|
|
|
|
function makeFormHistoryResult(queryContext, engine, entry) {
|
|
return new lazy.UrlbarResult(
|
|
UrlbarUtils.RESULT_TYPE.SEARCH,
|
|
UrlbarUtils.RESULT_SOURCE.HISTORY,
|
|
...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
|
|
engine: engine.name,
|
|
suggestion: [entry.value, UrlbarUtils.HIGHLIGHT.SUGGESTED],
|
|
lowerCaseSuggestion: entry.value.toLocaleLowerCase(),
|
|
isBlockable: true,
|
|
blockL10n: { id: "urlbar-result-menu-remove-from-history" },
|
|
helpUrl:
|
|
Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
|
"awesome-bar-result-menu",
|
|
})
|
|
);
|
|
}
|
|
|
|
export var UrlbarProviderSearchSuggestions = new ProviderSearchSuggestions();
|