2020-06-11 20:12:52 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This module exports a provider class that is used for providers created by
|
|
|
|
* extensions using the `omnibox` API.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var EXPORTED_SYMBOLS = ["UrlbarProviderOmnibox"];
|
|
|
|
|
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
|
|
);
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
|
|
ExtensionSearchHandler: "resource://gre/modules/ExtensionSearchHandler.jsm",
|
|
|
|
SkippableTimer: "resource:///modules/UrlbarUtils.jsm",
|
|
|
|
UrlbarProvider: "resource:///modules/UrlbarUtils.jsm",
|
|
|
|
UrlbarResult: "resource:///modules/UrlbarResult.jsm",
|
|
|
|
UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
|
|
|
|
});
|
|
|
|
|
|
|
|
// After this time, we'll give up waiting for the extension to return matches.
|
|
|
|
const MAXIMUM_ALLOWED_EXTENSION_TIME_MS = 3000;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This provider handles results returned by extensions using the WebExtensions
|
|
|
|
* Omnibox API. If the user types a registered keyword, we send subsequent
|
|
|
|
* keystrokes to the extension.
|
|
|
|
*/
|
|
|
|
class ProviderOmnibox extends UrlbarProvider {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the name of this provider.
|
|
|
|
* @returns {string} the name of this provider.
|
|
|
|
*/
|
|
|
|
get name() {
|
|
|
|
return "Omnibox";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the type of this provider.
|
|
|
|
* @returns {integer} one of the types from UrlbarUtils.PROVIDER_TYPE.*
|
|
|
|
*/
|
|
|
|
get type() {
|
2020-07-12 22:54:09 +03:00
|
|
|
return UrlbarUtils.PROVIDER_TYPE.HEURISTIC;
|
2020-06-11 20:12:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the 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.
|
|
|
|
* @returns {boolean}
|
|
|
|
* Whether this provider should be invoked for the search.
|
|
|
|
*/
|
|
|
|
isActive(queryContext) {
|
|
|
|
if (
|
|
|
|
queryContext.tokens[0] &&
|
|
|
|
queryContext.tokens[0].value.length &&
|
|
|
|
ExtensionSearchHandler.isKeywordRegistered(
|
|
|
|
queryContext.tokens[0].value
|
|
|
|
) &&
|
|
|
|
UrlbarUtils.substringAfter(
|
|
|
|
queryContext.searchString,
|
|
|
|
queryContext.tokens[0].value
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to handle cancellation here since isActive is called once per
|
|
|
|
// query but cancelQuery can be called multiple times per query.
|
|
|
|
// The frequent cancels can cause the extension's state to drift from the
|
|
|
|
// provider's state.
|
|
|
|
if (ExtensionSearchHandler.hasActiveInputSession()) {
|
|
|
|
ExtensionSearchHandler.handleInputCancelled();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the provider's priority.
|
|
|
|
*
|
|
|
|
* @param {UrlbarQueryContext} queryContext
|
|
|
|
* The query context object.
|
|
|
|
* @returns {number}
|
|
|
|
* The provider's priority for the given query.
|
|
|
|
*/
|
|
|
|
getPriority(queryContext) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method is called by the providers manager when a query starts to fetch
|
|
|
|
* each extension provider's results. It fires the resultsRequested event.
|
|
|
|
*
|
|
|
|
* @param {UrlbarQueryContext} queryContext
|
|
|
|
* The query context object.
|
|
|
|
* @param {function} addCallback
|
|
|
|
* The callback invoked by this method to add each result.
|
|
|
|
*/
|
|
|
|
async startQuery(queryContext, addCallback) {
|
2020-07-15 13:16:50 +03:00
|
|
|
let instance = this.queryInstance;
|
2020-06-11 20:12:52 +03:00
|
|
|
|
2020-07-12 22:54:09 +03:00
|
|
|
// Fetch heuristic result.
|
|
|
|
let keyword = queryContext.tokens[0].value;
|
|
|
|
let description = ExtensionSearchHandler.getDescription(keyword);
|
|
|
|
let heuristicResult = new UrlbarResult(
|
|
|
|
UrlbarUtils.RESULT_TYPE.OMNIBOX,
|
|
|
|
UrlbarUtils.RESULT_SOURCE.OTHER_NETWORK,
|
|
|
|
...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
|
|
|
|
title: [description, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
|
|
content: [queryContext.searchString, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
|
|
keyword: [queryContext.tokens[0].value, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
|
|
icon: UrlbarUtils.ICON.EXTENSION,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
heuristicResult.heuristic = true;
|
|
|
|
addCallback(this, heuristicResult);
|
|
|
|
|
|
|
|
// Fetch non-heuristic results.
|
2020-06-11 20:12:52 +03:00
|
|
|
let data = {
|
2020-07-12 22:54:09 +03:00
|
|
|
keyword,
|
2020-06-11 20:12:52 +03:00
|
|
|
text: queryContext.searchString,
|
|
|
|
inPrivateWindow: queryContext.isPrivate,
|
|
|
|
};
|
|
|
|
this._resultsPromise = ExtensionSearchHandler.handleSearch(
|
|
|
|
data,
|
|
|
|
suggestions => {
|
2020-07-15 13:16:50 +03:00
|
|
|
if (instance != this.queryInstance) {
|
|
|
|
return;
|
|
|
|
}
|
2020-06-11 20:12:52 +03:00
|
|
|
for (let suggestion of suggestions) {
|
|
|
|
let content = `${queryContext.tokens[0].value} ${suggestion.content}`;
|
2020-07-12 22:54:09 +03:00
|
|
|
if (content == heuristicResult.payload.content) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-11 20:12:52 +03:00
|
|
|
let result = new UrlbarResult(
|
|
|
|
UrlbarUtils.RESULT_TYPE.OMNIBOX,
|
|
|
|
UrlbarUtils.RESULT_SOURCE.OTHER_NETWORK,
|
|
|
|
...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
|
|
|
|
title: [suggestion.description, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
|
|
content: [content, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
|
|
keyword: [
|
|
|
|
queryContext.tokens[0].value,
|
|
|
|
UrlbarUtils.HIGHLIGHT.TYPED,
|
|
|
|
],
|
|
|
|
icon: UrlbarUtils.ICON.EXTENSION,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
addCallback(this, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Since the extension has no way to signal when it's done pushing results,
|
|
|
|
// we add a timer racing with the addition.
|
|
|
|
let timeoutPromise = new SkippableTimer({
|
|
|
|
name: "ProviderOmnibox",
|
|
|
|
time: MAXIMUM_ALLOWED_EXTENSION_TIME_MS,
|
2020-07-12 22:54:09 +03:00
|
|
|
logger: this.logger,
|
2020-06-11 20:12:52 +03:00
|
|
|
}).promise;
|
2021-10-18 17:01:10 +03:00
|
|
|
await Promise.race([timeoutPromise, this._resultsPromise]).catch(ex =>
|
|
|
|
this.logger.error(ex)
|
2020-06-11 20:12:52 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var UrlbarProviderOmnibox = new ProviderOmnibox();
|