2014-04-25 06:09:20 +04: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";
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
var EXPORTED_SYMBOLS = ["ContentSearchParent", "ContentSearch"];
|
2014-04-25 06:09:20 +04:00
|
|
|
|
2019-01-17 21:18:31 +03:00
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
|
|
);
|
2014-08-01 23:00:44 +04:00
|
|
|
|
2018-05-26 03:02:29 +03:00
|
|
|
XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
|
2017-09-19 19:17:10 +03:00
|
|
|
|
2018-01-30 08:17:48 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"FormHistory",
|
2014-08-01 23:00:44 +04:00
|
|
|
"resource://gre/modules/FormHistory.jsm"
|
|
|
|
);
|
2018-01-30 08:17:48 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"PrivateBrowsingUtils",
|
2014-08-01 23:00:44 +04:00
|
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm"
|
|
|
|
);
|
2018-01-30 08:17:48 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"SearchSuggestionController",
|
2014-08-01 23:00:44 +04:00
|
|
|
"resource://gre/modules/SearchSuggestionController.jsm"
|
|
|
|
);
|
2014-04-25 06:09:20 +04:00
|
|
|
|
2015-02-10 14:47:43 +03:00
|
|
|
const MAX_LOCAL_SUGGESTIONS = 3;
|
|
|
|
const MAX_SUGGESTIONS = 6;
|
2014-04-25 06:09:20 +04:00
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
// Set of all ContentSearch actors, used to broadcast messages to all of them.
|
|
|
|
let gContentSearchActors = new Set();
|
|
|
|
|
2014-04-25 06:09:20 +04:00
|
|
|
/**
|
|
|
|
* Inbound messages have the following types:
|
|
|
|
*
|
2014-08-01 23:00:44 +04:00
|
|
|
* AddFormHistoryEntry
|
|
|
|
* Adds an entry to the search form history.
|
|
|
|
* data: the entry, a string
|
|
|
|
* GetSuggestions
|
|
|
|
* Retrieves an array of search suggestions given a search string.
|
2016-12-20 08:37:00 +03:00
|
|
|
* data: { engineName, searchString }
|
2014-04-25 06:09:20 +04:00
|
|
|
* GetState
|
2014-08-01 23:00:44 +04:00
|
|
|
* Retrieves the current search engine state.
|
|
|
|
* data: null
|
2015-06-29 23:52:20 +03:00
|
|
|
* GetStrings
|
|
|
|
* Retrieves localized search UI strings.
|
|
|
|
* data: null
|
2014-04-25 06:09:20 +04:00
|
|
|
* ManageEngines
|
2014-08-01 23:00:44 +04:00
|
|
|
* Opens the search engine management window.
|
|
|
|
* data: null
|
|
|
|
* RemoveFormHistoryEntry
|
|
|
|
* Removes an entry from the search form history.
|
|
|
|
* data: the entry, a string
|
2014-04-25 06:09:20 +04:00
|
|
|
* Search
|
2014-08-01 23:00:44 +04:00
|
|
|
* Performs a search.
|
2015-10-23 21:03:55 +03:00
|
|
|
* Any GetSuggestions messages in the queue from the same target will be
|
|
|
|
* cancelled.
|
2015-06-29 23:52:20 +03:00
|
|
|
* data: { engineName, searchString, healthReportKey, searchPurpose }
|
2014-04-25 06:09:20 +04:00
|
|
|
* SetCurrentEngine
|
2014-08-01 23:00:44 +04:00
|
|
|
* Sets the current engine.
|
|
|
|
* data: the name of the engine
|
|
|
|
* SpeculativeConnect
|
|
|
|
* Speculatively connects to an engine.
|
|
|
|
* data: the name of the engine
|
2014-04-25 06:09:20 +04:00
|
|
|
*
|
|
|
|
* Outbound messages have the following types:
|
|
|
|
*
|
|
|
|
* CurrentEngine
|
2014-08-01 23:00:44 +04:00
|
|
|
* Broadcast when the current engine changes.
|
2014-04-25 06:09:20 +04:00
|
|
|
* data: see _currentEngineObj
|
2014-06-17 23:35:34 +04:00
|
|
|
* CurrentState
|
2014-08-01 23:00:44 +04:00
|
|
|
* Broadcast when the current search state changes.
|
2016-05-10 17:32:31 +03:00
|
|
|
* data: see currentStateObj
|
2014-04-25 06:09:20 +04:00
|
|
|
* State
|
2014-06-17 23:35:34 +04:00
|
|
|
* Sent in reply to GetState.
|
2016-05-10 17:32:31 +03:00
|
|
|
* data: see currentStateObj
|
2015-06-29 23:52:20 +03:00
|
|
|
* Strings
|
|
|
|
* Sent in reply to GetStrings
|
|
|
|
* data: Object containing string names and values for the current locale.
|
2014-08-01 23:00:44 +04:00
|
|
|
* Suggestions
|
|
|
|
* Sent in reply to GetSuggestions.
|
|
|
|
* data: see _onMessageGetSuggestions
|
2015-10-23 21:03:55 +03:00
|
|
|
* SuggestionsCancelled
|
|
|
|
* Sent in reply to GetSuggestions when pending GetSuggestions events are
|
|
|
|
* cancelled.
|
|
|
|
* data: null
|
2014-04-25 06:09:20 +04:00
|
|
|
*/
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
let ContentSearch = {
|
|
|
|
initialized: false,
|
|
|
|
|
2014-06-17 23:35:34 +04:00
|
|
|
// Inbound events are queued and processed in FIFO order instead of handling
|
|
|
|
// them immediately, which would result in non-FIFO responses due to the
|
|
|
|
// asynchrononicity added by converting image data URIs to ArrayBuffers.
|
|
|
|
_eventQueue: [],
|
2014-08-21 13:18:54 +04:00
|
|
|
_currentEventPromise: null,
|
2014-06-17 23:35:34 +04:00
|
|
|
|
2014-08-01 23:00:44 +04:00
|
|
|
// This is used to handle search suggestions. It maps xul:browsers to objects
|
|
|
|
// { controller, previousFormHistoryResult }. See _onMessageGetSuggestions.
|
|
|
|
_suggestionMap: new WeakMap(),
|
|
|
|
|
2014-08-31 14:13:08 +04:00
|
|
|
// Resolved when we finish shutting down.
|
|
|
|
_destroyedPromise: null,
|
|
|
|
|
2015-10-23 21:03:55 +03:00
|
|
|
// The current controller and browser in _onMessageGetSuggestions. Allows
|
|
|
|
// fetch cancellation from _cancelSuggestions.
|
|
|
|
_currentSuggestion: null,
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
init() {
|
2020-04-12 02:10:45 +03:00
|
|
|
if (!this.initialized) {
|
|
|
|
Services.obs.addObserver(this, "browser-search-engine-modified");
|
|
|
|
Services.obs.addObserver(this, "browser-search-service");
|
|
|
|
Services.obs.addObserver(this, "shutdown-leaks-before-check");
|
|
|
|
Services.prefs.addObserver("browser.search.hiddenOneOffs", this);
|
|
|
|
this._stringBundle = Services.strings.createBundle(
|
|
|
|
"chrome://global/locale/autocomplete.properties"
|
|
|
|
);
|
|
|
|
|
|
|
|
this.initialized = true;
|
|
|
|
}
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2015-06-29 23:52:20 +03:00
|
|
|
get searchSuggestionUIStrings() {
|
|
|
|
if (this._searchSuggestionUIStrings) {
|
|
|
|
return this._searchSuggestionUIStrings;
|
|
|
|
}
|
|
|
|
this._searchSuggestionUIStrings = {};
|
|
|
|
let searchBundle = Services.strings.createBundle(
|
|
|
|
"chrome://browser/locale/search.properties"
|
|
|
|
);
|
2018-03-17 21:43:14 +03:00
|
|
|
let stringNames = [
|
|
|
|
"searchHeader",
|
|
|
|
"searchForSomethingWith2",
|
2015-07-22 12:30:10 +03:00
|
|
|
"searchWithHeader",
|
|
|
|
"searchSettings",
|
|
|
|
];
|
2016-05-10 17:32:31 +03:00
|
|
|
|
2015-06-29 23:52:20 +03:00
|
|
|
for (let name of stringNames) {
|
|
|
|
this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(
|
|
|
|
name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return this._searchSuggestionUIStrings;
|
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
destroy() {
|
2020-04-12 02:10:45 +03:00
|
|
|
if (!this.initialized) {
|
|
|
|
return new Promise();
|
|
|
|
}
|
|
|
|
|
2014-08-31 14:13:08 +04:00
|
|
|
if (this._destroyedPromise) {
|
|
|
|
return this._destroyedPromise;
|
|
|
|
}
|
|
|
|
|
2014-08-21 13:18:54 +04:00
|
|
|
Services.obs.removeObserver(this, "browser-search-engine-modified");
|
2018-06-05 00:45:50 +03:00
|
|
|
Services.obs.removeObserver(this, "browser-search-service");
|
2014-08-31 14:13:08 +04:00
|
|
|
Services.obs.removeObserver(this, "shutdown-leaks-before-check");
|
2014-08-21 13:18:54 +04:00
|
|
|
|
|
|
|
this._eventQueue.length = 0;
|
2016-05-10 17:32:31 +03:00
|
|
|
this._destroyedPromise = Promise.resolve(this._currentEventPromise);
|
|
|
|
return this._destroyedPromise;
|
2014-08-21 13:18:54 +04:00
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
observe(subj, topic, data) {
|
2014-06-17 23:35:34 +04:00
|
|
|
switch (topic) {
|
2018-06-05 00:45:50 +03:00
|
|
|
case "browser-search-service":
|
|
|
|
if (data != "init-complete") {
|
2019-07-05 10:55:19 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-08-08 18:18:49 +03:00
|
|
|
// fall through
|
2015-06-29 23:52:20 +03:00
|
|
|
case "nsPref:changed":
|
2014-06-17 23:35:34 +04:00
|
|
|
case "browser-search-engine-modified":
|
|
|
|
this._eventQueue.push({
|
|
|
|
type: "Observe",
|
2019-07-05 10:55:19 +03:00
|
|
|
data,
|
|
|
|
});
|
2014-06-17 23:35:34 +04:00
|
|
|
this._processEventQueue();
|
2019-07-05 10:55:19 +03:00
|
|
|
break;
|
2014-08-31 14:13:08 +04:00
|
|
|
case "shutdown-leaks-before-check":
|
|
|
|
subj.wrappedJSObject.client.addBlocker(
|
|
|
|
"ContentSearch: Wait until the service is destroyed",
|
|
|
|
() => this.destroy()
|
2019-07-05 10:55:19 +03:00
|
|
|
);
|
2018-06-05 00:45:50 +03:00
|
|
|
break;
|
2014-04-25 06:09:20 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
removeFormHistoryEntry(browser, entry) {
|
|
|
|
let browserData = this._suggestionDataForBrowser(browser);
|
2016-05-10 17:32:31 +03:00
|
|
|
if (browserData && browserData.previousFormHistoryResult) {
|
|
|
|
let { previousFormHistoryResult } = browserData;
|
|
|
|
for (let i = 0; i < previousFormHistoryResult.matchCount; i++) {
|
|
|
|
if (previousFormHistoryResult.getValueAt(i) === entry) {
|
2020-04-15 13:05:12 +03:00
|
|
|
previousFormHistoryResult.removeValueAt(i);
|
2016-05-10 17:32:31 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
performSearch(browser, data) {
|
2016-05-10 17:32:31 +03:00
|
|
|
this._ensureDataHasProperties(data, [
|
|
|
|
"engineName",
|
|
|
|
"searchString",
|
|
|
|
"healthReportKey",
|
|
|
|
"searchPurpose",
|
|
|
|
]);
|
|
|
|
let engine = Services.search.getEngineByName(data.engineName);
|
|
|
|
let submission = engine.getSubmission(
|
|
|
|
data.searchString,
|
|
|
|
"",
|
|
|
|
data.searchPurpose
|
|
|
|
);
|
2016-07-16 11:20:04 +03:00
|
|
|
let win = browser.ownerGlobal;
|
|
|
|
if (!win) {
|
2016-05-10 17:32:31 +03:00
|
|
|
// The browser may have been closed between the time its content sent the
|
2016-07-16 11:20:04 +03:00
|
|
|
// message and the time we handle it.
|
2016-05-10 17:32:31 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let where = win.whereToOpenLink(data.originalEvent);
|
|
|
|
|
|
|
|
// There is a chance that by the time we receive the search message, the user
|
|
|
|
// has switched away from the tab that triggered the search. If, based on the
|
|
|
|
// event, we need to load the search in the same tab that triggered it (i.e.
|
|
|
|
// where === "current"), openUILinkIn will not work because that tab is no
|
|
|
|
// longer the current one. For this case we manually load the URI.
|
|
|
|
if (where === "current") {
|
2017-07-19 00:07:40 +03:00
|
|
|
// Since we're going to load the search in the same browser, blur the search
|
|
|
|
// UI to prevent further interaction before we start loading.
|
2020-04-12 02:10:45 +03:00
|
|
|
this._reply(browser, "Blur");
|
2018-03-17 02:21:46 +03:00
|
|
|
browser.loadURI(submission.uri.spec, {
|
2018-03-25 15:35:17 +03:00
|
|
|
postData: submission.postData,
|
2018-08-29 17:43:27 +03:00
|
|
|
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
|
|
|
|
{
|
|
|
|
userContextId: win.gBrowser.selectedBrowser.getAttribute(
|
|
|
|
"userContextId"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
),
|
2018-03-25 15:35:17 +03:00
|
|
|
});
|
2016-05-10 17:32:31 +03:00
|
|
|
} else {
|
|
|
|
let params = {
|
|
|
|
postData: submission.postData,
|
|
|
|
inBackground: Services.prefs.getBoolPref(
|
|
|
|
"browser.tabs.loadInBackground"
|
|
|
|
),
|
|
|
|
};
|
2018-02-21 17:28:48 +03:00
|
|
|
win.openTrustedLinkIn(submission.uri.spec, where, params);
|
2016-05-10 17:32:31 +03:00
|
|
|
}
|
|
|
|
win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey, {
|
2016-10-29 00:13:00 +03:00
|
|
|
selection: data.selection,
|
|
|
|
});
|
2016-05-10 17:32:31 +03:00
|
|
|
},
|
|
|
|
|
2016-12-20 08:37:00 +03:00
|
|
|
async getSuggestions(engineName, searchString, browser) {
|
2016-05-10 17:32:31 +03:00
|
|
|
let engine = Services.search.getEngineByName(engineName);
|
|
|
|
if (!engine) {
|
|
|
|
throw new Error("Unknown engine name: " + engineName);
|
|
|
|
}
|
|
|
|
|
|
|
|
let browserData = this._suggestionDataForBrowser(browser, true);
|
|
|
|
let { controller } = browserData;
|
|
|
|
let ok = SearchSuggestionController.engineOffersSuggestions(engine);
|
|
|
|
controller.maxLocalResults = ok ? MAX_LOCAL_SUGGESTIONS : MAX_SUGGESTIONS;
|
|
|
|
controller.maxRemoteResults = ok ? MAX_SUGGESTIONS : 0;
|
2018-12-08 20:32:27 +03:00
|
|
|
let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
2016-05-10 17:32:31 +03:00
|
|
|
// fetch() rejects its promise if there's a pending request, but since we
|
|
|
|
// process our event queue serially, there's never a pending request.
|
2020-04-12 02:10:45 +03:00
|
|
|
this._currentSuggestion = { controller, browser };
|
2018-12-08 20:32:27 +03:00
|
|
|
let suggestions = await controller.fetch(searchString, priv, engine);
|
2020-05-12 20:01:14 +03:00
|
|
|
|
|
|
|
// Simplify results since we do not support rich results in this component.
|
|
|
|
suggestions.local = suggestions.local.map(e => e.value);
|
|
|
|
// We shouldn't show tail suggestions in their full-text form.
|
|
|
|
let nonTailEntries = suggestions.remote.filter(
|
|
|
|
e => !e.matchPrefix && !e.tail
|
|
|
|
);
|
|
|
|
suggestions.remote = nonTailEntries.map(e => e.value);
|
|
|
|
|
2016-05-10 17:32:31 +03:00
|
|
|
this._currentSuggestion = null;
|
|
|
|
|
|
|
|
// suggestions will be null if the request was cancelled
|
|
|
|
let result = {};
|
|
|
|
if (!suggestions) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep the form history result so RemoveFormHistoryEntry can remove entries
|
|
|
|
// from it. Keeping only one result isn't foolproof because the client may
|
|
|
|
// try to remove an entry from one set of suggestions after it has requested
|
|
|
|
// more but before it's received them. In that case, the entry may not
|
|
|
|
// appear in the new suggestions. But that should happen rarely.
|
|
|
|
browserData.previousFormHistoryResult = suggestions.formHistoryResult;
|
|
|
|
result = {
|
|
|
|
engineName,
|
|
|
|
term: suggestions.term,
|
|
|
|
local: suggestions.local,
|
|
|
|
remote: suggestions.remote,
|
|
|
|
};
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
2016-11-11 01:48:04 +03:00
|
|
|
async addFormHistoryEntry(browser, entry = "") {
|
2016-05-10 17:32:31 +03:00
|
|
|
let isPrivate = false;
|
|
|
|
try {
|
|
|
|
// isBrowserPrivate assumes that the passed-in browser has all the normal
|
|
|
|
// properties, which won't be true if the browser has been destroyed.
|
|
|
|
// That may be the case here due to the asynchronous nature of messaging.
|
2020-04-12 02:10:45 +03:00
|
|
|
isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
2016-05-10 17:32:31 +03:00
|
|
|
} catch (err) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (isPrivate || entry === "") {
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-12 02:10:45 +03:00
|
|
|
let browserData = this._suggestionDataForBrowser(browser, true);
|
2016-05-10 17:32:31 +03:00
|
|
|
FormHistory.update(
|
|
|
|
{
|
|
|
|
op: "bump",
|
|
|
|
fieldname: browserData.controller.formHistoryParam,
|
|
|
|
value: entry,
|
|
|
|
},
|
2019-07-05 10:55:19 +03:00
|
|
|
{
|
2016-05-10 17:32:31 +03:00
|
|
|
handleCompletion: () => {},
|
|
|
|
handleError: err => {
|
|
|
|
Cu.reportError("Error adding form history entry: " + err);
|
|
|
|
},
|
2019-07-05 10:55:19 +03:00
|
|
|
}
|
|
|
|
);
|
2016-05-10 17:32:31 +03:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2019-10-17 16:02:09 +03:00
|
|
|
async currentStateObj(window) {
|
2016-05-10 17:32:31 +03:00
|
|
|
let state = {
|
|
|
|
engines: [],
|
2019-09-27 17:54:38 +03:00
|
|
|
currentEngine: await this._currentEngineObj(false),
|
|
|
|
currentPrivateEngine: await this._currentEngineObj(true),
|
2016-05-10 17:32:31 +03:00
|
|
|
};
|
2019-02-22 09:42:00 +03:00
|
|
|
|
2020-01-09 18:47:46 +03:00
|
|
|
let pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
|
2016-05-10 17:32:31 +03:00
|
|
|
let hiddenList = pref ? pref.split(",") : [];
|
Bug 1524593 - nsISearchService (aka nsIBrowserSearchService, previously) refactor to be mostly an asynchronous, in preparation of WebExtension engines. r=daleharvey
This is a rollup of all the patches that have landed on the cedar project branch:
https://hg.mozilla.org/projects/cedar/rev/891252fdd0b1a3e6b129025d94952ac30d922c7e
Bug 1492475 - Part 1: Migrate most, if not all nsSearchService consumers to use async APIs. r=florian
https://hg.mozilla.org/projects/cedar/rev/79b2eb2367aab104669bbc75c3b42290f7de1570
Bug 1492475 - Part 2: Move nsIBrowserSearchService.idl to toolkit/components/search/nsISearchService.idl and update references. r=florian
https://hg.mozilla.org/projects/cedar/rev/a947d3cdf078032614edaa491ec3db1d046b55f4
Bug 1492475 - Part 3: The search service init() method should simply return a Promise. r=florian
https://hg.mozilla.org/projects/cedar/rev/c1e172dfacad4b14ebdb352bee2fd946716acd59
Bug 1492475 - Part 4: Remove the synchronous initialization flow. r=florian
https://hg.mozilla.org/projects/cedar/rev/cd41189eac88aa6023af1b0a060c15ddcd407952
Bug 1492475 - Part 5: Since async initialization of the search service now is implicit behavior, remove the distinctive verbiage used internally. r=florian
https://hg.mozilla.org/projects/cedar/rev/2ae7189dfaa63cab0e264e7a2796b1610505c40a
Bug 1492475 - Part 6: Update the cache build task to work with an actual Promise and re-initialize only once at the same time - all to fix race conditions here. r=florian
https://hg.mozilla.org/projects/cedar/rev/c8ee92973f24a44496f2bee23c13e0c74b6e11d8
Bug 1492475 - Part 7: Make the region fetch not block the init flow, to ensure it's as fast as possible. r=florian
https://hg.mozilla.org/projects/cedar/rev/c44e674e160ebab49ea5ba1ed5821bb8d3c30e53
Bug 1492475 - Part 8: Introduce an init flag, which can only be used privately, that allows to explicitly skip waiting for the region check process to complete. r=florian
https://hg.mozilla.org/projects/cedar/rev/6c79eaf1d349638258d542ced0229d786f022683
Bug 1492475 - Part 9: Update unit tests to stop using 'currentEngine', in favor of 'defaultEngine'. r=Standard8
https://hg.mozilla.org/projects/cedar/rev/21b3aa17ee43dd0efd3c08564bbc7d747d4628b9
Bug 1492475 - Part 10: Update unit tests to be fully aware of the new, async signatures of the search service API and remove sync init flow tests. r=mkaply,florian
https://hg.mozilla.org/projects/cedar/rev/ce5ba6901957903ade31888cdc6a52e2b828dac0
Bug 1492475 - Part 11: Repair incorrect usage of the `identifier` property of nsISearchEngine instances. r=florian
https://hg.mozilla.org/projects/cedar/rev/fd177a7994b250605df4b98740bdd257373e21e5
Bug 1518543 - Fix up the Android (Fennec) nsISearchService shim to work with the new asynchronous API. r=florian
https://hg.mozilla.org/projects/cedar/rev/3653d8ee22bb242b3ddc0222cb1f711b68b52f91
Bug 1523708 - Change the search service interaction in the show-heartbeat action to use the new async API. r=florian
Differential Revision: https://phabricator.services.mozilla.com/D18355
--HG--
rename : netwerk/base/nsIBrowserSearchService.idl => toolkit/components/search/nsISearchService.idl
extra : moz-landing-system : lando
2019-02-02 14:27:21 +03:00
|
|
|
for (let engine of await Services.search.getVisibleEngines()) {
|
2016-05-10 17:32:31 +03:00
|
|
|
let uri = engine.getIconURLBySize(16, 16);
|
2019-02-22 09:42:00 +03:00
|
|
|
let iconData = await this._maybeConvertURIToArrayBuffer(uri);
|
|
|
|
|
2016-05-10 17:32:31 +03:00
|
|
|
state.engines.push({
|
|
|
|
name: engine.name,
|
2019-02-22 09:42:00 +03:00
|
|
|
iconData,
|
2016-05-10 17:32:31 +03:00
|
|
|
hidden: hiddenList.includes(engine.name),
|
2020-05-04 13:49:15 +03:00
|
|
|
isAppProvided: engine.isAppProvided,
|
2016-05-10 17:32:31 +03:00
|
|
|
});
|
|
|
|
}
|
2019-10-17 16:02:09 +03:00
|
|
|
|
|
|
|
if (window) {
|
|
|
|
state.isPrivateWindow = PrivateBrowsingUtils.isContentWindowPrivate(
|
|
|
|
window
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-05-10 17:32:31 +03:00
|
|
|
return state;
|
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
_processEventQueue() {
|
2014-08-21 13:18:54 +04:00
|
|
|
if (this._currentEventPromise || !this._eventQueue.length) {
|
2014-06-17 23:35:34 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-08-21 13:18:54 +04:00
|
|
|
|
|
|
|
let event = this._eventQueue.shift();
|
|
|
|
|
2015-12-17 21:31:08 +03:00
|
|
|
this._currentEventPromise = (async () => {
|
2014-08-21 13:18:54 +04:00
|
|
|
try {
|
2020-04-12 02:10:45 +03:00
|
|
|
await this["_on" + event.type](event);
|
2014-08-21 13:18:54 +04:00
|
|
|
} catch (err) {
|
|
|
|
Cu.reportError(err);
|
|
|
|
} finally {
|
|
|
|
this._currentEventPromise = null;
|
2020-04-12 02:10:45 +03:00
|
|
|
|
2014-08-21 13:18:54 +04:00
|
|
|
this._processEventQueue();
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
},
|
2014-06-17 23:35:34 +04:00
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_cancelSuggestions(browser) {
|
2015-10-23 21:03:55 +03:00
|
|
|
let cancelled = false;
|
|
|
|
// cancel active suggestion request
|
2016-05-10 17:32:31 +03:00
|
|
|
if (
|
|
|
|
this._currentSuggestion &&
|
2020-04-12 02:10:45 +03:00
|
|
|
this._currentSuggestion.browser === browser
|
2016-05-10 17:32:31 +03:00
|
|
|
) {
|
2015-10-23 21:03:55 +03:00
|
|
|
this._currentSuggestion.controller.stop();
|
|
|
|
cancelled = true;
|
|
|
|
}
|
|
|
|
// cancel queued suggestion requests
|
|
|
|
for (let i = 0; i < this._eventQueue.length; i++) {
|
2020-04-12 02:10:45 +03:00
|
|
|
let m = this._eventQueue[i];
|
|
|
|
if (browser === m.browser && m.name === "GetSuggestions") {
|
2015-10-23 21:03:55 +03:00
|
|
|
this._eventQueue.splice(i, 1);
|
|
|
|
cancelled = true;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cancelled) {
|
2020-04-12 02:10:45 +03:00
|
|
|
this._reply(browser, "SuggestionsCancelled");
|
2015-10-23 21:03:55 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
async _onMessage(eventItem) {
|
|
|
|
let methodName = "_onMessage" + eventItem.name;
|
2014-06-17 23:35:34 +04:00
|
|
|
if (methodName in this) {
|
|
|
|
await this._initService();
|
2020-04-12 02:10:45 +03:00
|
|
|
await this[methodName](eventItem.browser, eventItem.data);
|
|
|
|
eventItem.browser.removeEventListener("SwapDocShells", eventItem, true);
|
2014-06-17 23:35:34 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageGetState(browser, data) {
|
|
|
|
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
|
|
|
this._reply(browser, "State", state);
|
2014-06-17 23:35:34 +04:00
|
|
|
});
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageGetEngine(browser, data) {
|
|
|
|
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
|
|
|
this._reply(browser, "Engine", {
|
2019-12-18 00:02:52 +03:00
|
|
|
isPrivateWindow: state.isPrivateWindow,
|
|
|
|
engine: state.isPrivateWindow
|
|
|
|
? state.currentPrivateEngine
|
|
|
|
: state.currentEngine,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageGetStrings(browser, data) {
|
|
|
|
this._reply(browser, "Strings", this.searchSuggestionUIStrings);
|
2015-06-29 23:52:20 +03:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageSearch(browser, data) {
|
|
|
|
this.performSearch(browser, data);
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageSetCurrentEngine(browser, data) {
|
2018-10-27 19:52:02 +03:00
|
|
|
Services.search.defaultEngine = Services.search.getEngineByName(data);
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageManageEngines(browser) {
|
|
|
|
browser.ownerGlobal.openPreferences("paneSearch");
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
async _onMessageGetSuggestions(browser, data) {
|
2014-08-01 23:00:44 +04:00
|
|
|
this._ensureDataHasProperties(data, ["engineName", "searchString"]);
|
2016-05-10 17:32:31 +03:00
|
|
|
let { engineName, searchString } = data;
|
|
|
|
let suggestions = await this.getSuggestions(
|
|
|
|
engineName,
|
|
|
|
searchString,
|
2020-04-12 02:10:45 +03:00
|
|
|
browser
|
2016-05-10 17:32:31 +03:00
|
|
|
);
|
2014-08-01 23:00:44 +04:00
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
this._reply(browser, "Suggestions", {
|
2014-08-01 23:00:44 +04:00
|
|
|
engineName: data.engineName,
|
|
|
|
searchString: suggestions.term,
|
|
|
|
formHistory: suggestions.local,
|
|
|
|
remote: suggestions.remote,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
async _onMessageAddFormHistoryEntry(browser, entry) {
|
|
|
|
await this.addFormHistoryEntry(browser, entry);
|
2016-05-10 17:32:31 +03:00
|
|
|
},
|
2014-08-01 23:00:44 +04:00
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageRemoveFormHistoryEntry(browser, entry) {
|
|
|
|
this.removeFormHistoryEntry(browser, entry);
|
2014-08-01 23:00:44 +04:00
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_onMessageSpeculativeConnect(browser, engineName) {
|
2014-08-01 23:00:44 +04:00
|
|
|
let engine = Services.search.getEngineByName(engineName);
|
|
|
|
if (!engine) {
|
|
|
|
throw new Error("Unknown engine name: " + engineName);
|
|
|
|
}
|
2020-04-12 02:10:45 +03:00
|
|
|
if (browser.contentWindow) {
|
2014-08-01 23:00:44 +04:00
|
|
|
engine.speculativeConnect({
|
2020-04-12 02:10:45 +03:00
|
|
|
window: browser.contentWindow,
|
|
|
|
originAttributes: browser.contentPrincipal.originAttributes,
|
2014-08-01 23:00:44 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
async _onObserve(eventItem) {
|
|
|
|
if (eventItem.data === "engine-default") {
|
2019-09-27 17:54:38 +03:00
|
|
|
let engine = await this._currentEngineObj(false);
|
2014-06-17 23:35:34 +04:00
|
|
|
this._broadcast("CurrentEngine", engine);
|
2020-04-12 02:10:45 +03:00
|
|
|
} else if (eventItem.data === "engine-default-private") {
|
2019-09-27 17:54:38 +03:00
|
|
|
let engine = await this._currentEngineObj(true);
|
|
|
|
this._broadcast("CurrentPrivateEngine", engine);
|
2019-04-17 12:45:24 +03:00
|
|
|
} else {
|
2016-05-10 17:32:31 +03:00
|
|
|
let state = await this.currentStateObj();
|
2014-06-17 23:35:34 +04:00
|
|
|
this._broadcast("CurrentState", state);
|
|
|
|
}
|
|
|
|
},
|
2014-04-25 06:09:20 +04:00
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
_suggestionDataForBrowser(browser, create = false) {
|
2014-08-01 23:00:44 +04:00
|
|
|
let data = this._suggestionMap.get(browser);
|
|
|
|
if (!data && create) {
|
|
|
|
// Since one SearchSuggestionController instance is meant to be used per
|
|
|
|
// autocomplete widget, this means that we assume each xul:browser has at
|
|
|
|
// most one such widget.
|
|
|
|
data = {
|
|
|
|
controller: new SearchSuggestionController(),
|
|
|
|
};
|
|
|
|
this._suggestionMap.set(browser, data);
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
2020-04-12 02:10:45 +03:00
|
|
|
_reply(browser, type, data) {
|
|
|
|
browser.sendMessageToActor(type, data, "ContentSearch");
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
_broadcast(type, data) {
|
2020-04-12 02:10:45 +03:00
|
|
|
for (let actor of gContentSearchActors) {
|
|
|
|
actor.sendAsyncMessage(type, data);
|
|
|
|
}
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
|
|
|
|
2019-09-27 17:54:38 +03:00
|
|
|
async _currentEngineObj(usePrivate) {
|
|
|
|
let engine =
|
|
|
|
Services.search[usePrivate ? "defaultPrivateEngine" : "defaultEngine"];
|
2014-09-26 11:37:00 +04:00
|
|
|
let favicon = engine.getIconURLBySize(16, 16);
|
2014-10-31 23:00:34 +03:00
|
|
|
let placeholder = this._stringBundle.formatStringFromName(
|
2019-06-11 18:51:51 +03:00
|
|
|
"searchWithEngine",
|
|
|
|
[engine.name]
|
|
|
|
);
|
2014-06-17 23:35:34 +04:00
|
|
|
let obj = {
|
|
|
|
name: engine.name,
|
2016-12-30 02:34:54 +03:00
|
|
|
placeholder,
|
2019-02-22 09:42:00 +03:00
|
|
|
iconData: await this._maybeConvertURIToArrayBuffer(favicon),
|
2020-05-04 13:49:15 +03:00
|
|
|
isAppProvided: engine.isAppProvided,
|
2014-06-17 23:35:34 +04:00
|
|
|
};
|
|
|
|
return obj;
|
|
|
|
},
|
2014-04-25 06:09:20 +04:00
|
|
|
|
2019-02-22 09:42:00 +03:00
|
|
|
_maybeConvertURIToArrayBuffer(uri) {
|
2014-06-17 23:35:34 +04:00
|
|
|
if (!uri) {
|
|
|
|
return Promise.resolve(null);
|
|
|
|
}
|
2019-02-22 09:42:00 +03:00
|
|
|
|
|
|
|
// The uri received here can be of two types
|
2019-08-07 19:38:54 +03:00
|
|
|
// 1 - moz-extension://[uuid]/path/to/icon.ico
|
2019-02-22 09:42:00 +03:00
|
|
|
// 2 - data:image/x-icon;base64,VERY-LONG-STRING
|
|
|
|
//
|
|
|
|
// If the URI is not a data: URI, there's no point in converting
|
|
|
|
// it to an arraybuffer (which is used to optimize passing the data
|
|
|
|
// accross processes): we can just pass the original URI, which is cheaper.
|
2019-02-27 20:37:49 +03:00
|
|
|
if (!uri.startsWith("data:")) {
|
2019-02-22 09:42:00 +03:00
|
|
|
return Promise.resolve(uri);
|
|
|
|
}
|
|
|
|
|
2014-06-17 23:35:34 +04:00
|
|
|
return new Promise(resolve => {
|
2017-09-19 19:17:10 +03:00
|
|
|
let xhr = new XMLHttpRequest();
|
2014-06-17 23:35:34 +04:00
|
|
|
xhr.open("GET", uri, true);
|
|
|
|
xhr.responseType = "arraybuffer";
|
2016-07-30 07:24:56 +03:00
|
|
|
xhr.onload = () => {
|
2014-06-17 23:35:34 +04:00
|
|
|
resolve(xhr.response);
|
2014-04-25 06:09:20 +04:00
|
|
|
};
|
2016-07-30 07:24:56 +03:00
|
|
|
xhr.onerror = xhr.onabort = xhr.ontimeout = () => {
|
|
|
|
resolve(null);
|
|
|
|
};
|
2014-06-20 01:34:12 +04:00
|
|
|
try {
|
|
|
|
// This throws if the URI is erroneously encoded.
|
|
|
|
xhr.send();
|
2016-12-31 05:47:25 +03:00
|
|
|
} catch (err) {
|
2017-05-12 15:56:12 +03:00
|
|
|
resolve(null);
|
2014-06-20 01:34:12 +04:00
|
|
|
}
|
2014-06-17 23:35:34 +04:00
|
|
|
});
|
2014-04-25 06:09:20 +04:00
|
|
|
},
|
2014-05-20 06:11:04 +04:00
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
_ensureDataHasProperties(data, requiredProperties) {
|
2014-08-01 23:00:44 +04:00
|
|
|
for (let prop of requiredProperties) {
|
|
|
|
if (!(prop in data)) {
|
|
|
|
throw new Error("Message data missing required property: " + prop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-12-30 02:34:54 +03:00
|
|
|
_initService() {
|
2014-05-20 06:11:04 +04:00
|
|
|
if (!this._initServicePromise) {
|
Bug 1524593 - nsISearchService (aka nsIBrowserSearchService, previously) refactor to be mostly an asynchronous, in preparation of WebExtension engines. r=daleharvey
This is a rollup of all the patches that have landed on the cedar project branch:
https://hg.mozilla.org/projects/cedar/rev/891252fdd0b1a3e6b129025d94952ac30d922c7e
Bug 1492475 - Part 1: Migrate most, if not all nsSearchService consumers to use async APIs. r=florian
https://hg.mozilla.org/projects/cedar/rev/79b2eb2367aab104669bbc75c3b42290f7de1570
Bug 1492475 - Part 2: Move nsIBrowserSearchService.idl to toolkit/components/search/nsISearchService.idl and update references. r=florian
https://hg.mozilla.org/projects/cedar/rev/a947d3cdf078032614edaa491ec3db1d046b55f4
Bug 1492475 - Part 3: The search service init() method should simply return a Promise. r=florian
https://hg.mozilla.org/projects/cedar/rev/c1e172dfacad4b14ebdb352bee2fd946716acd59
Bug 1492475 - Part 4: Remove the synchronous initialization flow. r=florian
https://hg.mozilla.org/projects/cedar/rev/cd41189eac88aa6023af1b0a060c15ddcd407952
Bug 1492475 - Part 5: Since async initialization of the search service now is implicit behavior, remove the distinctive verbiage used internally. r=florian
https://hg.mozilla.org/projects/cedar/rev/2ae7189dfaa63cab0e264e7a2796b1610505c40a
Bug 1492475 - Part 6: Update the cache build task to work with an actual Promise and re-initialize only once at the same time - all to fix race conditions here. r=florian
https://hg.mozilla.org/projects/cedar/rev/c8ee92973f24a44496f2bee23c13e0c74b6e11d8
Bug 1492475 - Part 7: Make the region fetch not block the init flow, to ensure it's as fast as possible. r=florian
https://hg.mozilla.org/projects/cedar/rev/c44e674e160ebab49ea5ba1ed5821bb8d3c30e53
Bug 1492475 - Part 8: Introduce an init flag, which can only be used privately, that allows to explicitly skip waiting for the region check process to complete. r=florian
https://hg.mozilla.org/projects/cedar/rev/6c79eaf1d349638258d542ced0229d786f022683
Bug 1492475 - Part 9: Update unit tests to stop using 'currentEngine', in favor of 'defaultEngine'. r=Standard8
https://hg.mozilla.org/projects/cedar/rev/21b3aa17ee43dd0efd3c08564bbc7d747d4628b9
Bug 1492475 - Part 10: Update unit tests to be fully aware of the new, async signatures of the search service API and remove sync init flow tests. r=mkaply,florian
https://hg.mozilla.org/projects/cedar/rev/ce5ba6901957903ade31888cdc6a52e2b828dac0
Bug 1492475 - Part 11: Repair incorrect usage of the `identifier` property of nsISearchEngine instances. r=florian
https://hg.mozilla.org/projects/cedar/rev/fd177a7994b250605df4b98740bdd257373e21e5
Bug 1518543 - Fix up the Android (Fennec) nsISearchService shim to work with the new asynchronous API. r=florian
https://hg.mozilla.org/projects/cedar/rev/3653d8ee22bb242b3ddc0222cb1f711b68b52f91
Bug 1523708 - Change the search service interaction in the show-heartbeat action to use the new async API. r=florian
Differential Revision: https://phabricator.services.mozilla.com/D18355
--HG--
rename : netwerk/base/nsIBrowserSearchService.idl => toolkit/components/search/nsISearchService.idl
extra : moz-landing-system : lando
2019-02-02 14:27:21 +03:00
|
|
|
this._initServicePromise = Services.search.init();
|
2014-05-20 06:11:04 +04:00
|
|
|
}
|
|
|
|
return this._initServicePromise;
|
|
|
|
},
|
2014-04-25 06:09:20 +04:00
|
|
|
};
|
2020-04-12 02:10:45 +03:00
|
|
|
|
|
|
|
class ContentSearchParent extends JSWindowActorParent {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
ContentSearch.init();
|
|
|
|
gContentSearchActors.add(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
didDestroy() {
|
|
|
|
gContentSearchActors.delete(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
receiveMessage(msg) {
|
|
|
|
// Add a temporary event handler that exists only while the message is in
|
|
|
|
// the event queue. If the message's source docshell changes browsers in
|
|
|
|
// the meantime, then we need to update the browser. event.detail will be
|
|
|
|
// the docshell's new parent <xul:browser> element.
|
|
|
|
let browser = this.browsingContext.top.embedderElement;
|
|
|
|
let eventItem = {
|
|
|
|
type: "Message",
|
|
|
|
name: msg.name,
|
|
|
|
data: msg.data,
|
|
|
|
browser,
|
|
|
|
handleEvent: event => {
|
|
|
|
let browserData = ContentSearch._suggestionMap.get(eventItem.browser);
|
|
|
|
if (browserData) {
|
|
|
|
ContentSearch._suggestionMap.delete(eventItem.browser);
|
|
|
|
ContentSearch._suggestionMap.set(event.detail, browserData);
|
|
|
|
}
|
|
|
|
browser.removeEventListener("SwapDocShells", eventItem, true);
|
|
|
|
eventItem.browser = event.detail;
|
|
|
|
eventItem.browser.addEventListener("SwapDocShells", eventItem, true);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
browser.addEventListener("SwapDocShells", eventItem, true);
|
|
|
|
|
|
|
|
// Search requests cause cancellation of all Suggestion requests from the
|
|
|
|
// same browser.
|
|
|
|
if (msg.name === "Search") {
|
|
|
|
ContentSearch._cancelSuggestions();
|
|
|
|
}
|
|
|
|
|
|
|
|
ContentSearch._eventQueue.push(eventItem);
|
|
|
|
ContentSearch._processEventQueue();
|
|
|
|
}
|
|
|
|
}
|