mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 18:31:29 +00:00
Bug 1614738, convert ContentSearch to be a JSWindowActor, r=adw
Differential Revision: https://phabricator.services.mozilla.com/D68237 --HG-- rename : browser/modules/ContentSearch.jsm => browser/actors/ContentSearchParent.jsm extra : moz-landing-system : lando
This commit is contained in:
parent
76b0d3e957
commit
1351306d5f
@ -6,24 +6,19 @@
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ContentSearchChild"];
|
||||
|
||||
const { ActorChild } = ChromeUtils.import(
|
||||
"resource://gre/modules/ActorChild.jsm"
|
||||
);
|
||||
|
||||
class ContentSearchChild extends ActorChild {
|
||||
class ContentSearchChild extends JSWindowActorChild {
|
||||
handleEvent(event) {
|
||||
this._sendMsg(event.detail.type, event.detail.data);
|
||||
// The event gets translated into a message that
|
||||
// is then sent to the parent.
|
||||
if (event.type == "ContentSearchClient") {
|
||||
this.sendAsyncMessage(event.detail.type, event.detail.data);
|
||||
}
|
||||
}
|
||||
|
||||
receiveMessage(msg) {
|
||||
this._fireEvent(msg.data.type, msg.data.data);
|
||||
}
|
||||
|
||||
_sendMsg(type, data = null) {
|
||||
this.mm.sendAsyncMessage("ContentSearch", {
|
||||
type,
|
||||
data,
|
||||
});
|
||||
// The message gets translated into an event that
|
||||
// is then sent to the content.
|
||||
this._fireEvent(msg.name, msg.data);
|
||||
}
|
||||
|
||||
_fireEvent(type, data = null) {
|
||||
@ -34,10 +29,10 @@ class ContentSearchChild extends ActorChild {
|
||||
data,
|
||||
},
|
||||
},
|
||||
this.content
|
||||
this.contentWindow
|
||||
);
|
||||
this.content.dispatchEvent(
|
||||
new this.content.CustomEvent("ContentSearchService", event)
|
||||
this.contentWindow.dispatchEvent(
|
||||
new this.contentWindow.CustomEvent("ContentSearchService", event)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ContentSearch"];
|
||||
var EXPORTED_SYMBOLS = ["ContentSearchParent", "ContentSearch"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
@ -28,18 +28,13 @@ ChromeUtils.defineModuleGetter(
|
||||
"resource://gre/modules/SearchSuggestionController.jsm"
|
||||
);
|
||||
|
||||
const INBOUND_MESSAGE = "ContentSearch";
|
||||
const OUTBOUND_MESSAGE = INBOUND_MESSAGE;
|
||||
const MAX_LOCAL_SUGGESTIONS = 3;
|
||||
const MAX_SUGGESTIONS = 6;
|
||||
|
||||
// Set of all ContentSearch actors, used to broadcast messages to all of them.
|
||||
let gContentSearchActors = new Set();
|
||||
|
||||
/**
|
||||
* ContentSearch receives messages named INBOUND_MESSAGE and sends messages
|
||||
* named OUTBOUND_MESSAGE. The data of each message is expected to look like
|
||||
* { type, data }. type is the message's type (or subtype if you consider the
|
||||
* type of the message itself to be INBOUND_MESSAGE), and data is data that is
|
||||
* specific to the type.
|
||||
*
|
||||
* Inbound messages have the following types:
|
||||
*
|
||||
* AddFormHistoryEntry
|
||||
@ -95,7 +90,9 @@ const MAX_SUGGESTIONS = 6;
|
||||
* data: null
|
||||
*/
|
||||
|
||||
var ContentSearch = {
|
||||
let ContentSearch = {
|
||||
initialized: false,
|
||||
|
||||
// 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.
|
||||
@ -114,13 +111,17 @@ var ContentSearch = {
|
||||
_currentSuggestion: null,
|
||||
|
||||
init() {
|
||||
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"
|
||||
);
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
get searchSuggestionUIStrings() {
|
||||
@ -147,6 +148,10 @@ var ContentSearch = {
|
||||
},
|
||||
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return new Promise();
|
||||
}
|
||||
|
||||
if (this._destroyedPromise) {
|
||||
return this._destroyedPromise;
|
||||
}
|
||||
@ -160,48 +165,6 @@ var ContentSearch = {
|
||||
return this._destroyedPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the search input in the page with the given message manager.
|
||||
* @param messageManager
|
||||
* The MessageManager object of the selected browser.
|
||||
*/
|
||||
focusInput(messageManager) {
|
||||
messageManager.sendAsyncMessage(OUTBOUND_MESSAGE, {
|
||||
type: "FocusInput",
|
||||
});
|
||||
},
|
||||
|
||||
// Listeners and observers are added in BrowserGlue.jsm
|
||||
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 msg.target. event.detail will be
|
||||
// the docshell's new parent <xul:browser> element.
|
||||
msg.handleEvent = event => {
|
||||
let browserData = this._suggestionMap.get(msg.target);
|
||||
if (browserData) {
|
||||
this._suggestionMap.delete(msg.target);
|
||||
this._suggestionMap.set(event.detail, browserData);
|
||||
}
|
||||
msg.target.removeEventListener("SwapDocShells", msg, true);
|
||||
msg.target = event.detail;
|
||||
msg.target.addEventListener("SwapDocShells", msg, true);
|
||||
};
|
||||
msg.target.addEventListener("SwapDocShells", msg, true);
|
||||
|
||||
// Search requests cause cancellation of all Suggestion requests from the
|
||||
// same browser.
|
||||
if (msg.data.type === "Search") {
|
||||
this._cancelSuggestions(msg);
|
||||
}
|
||||
|
||||
this._eventQueue.push({
|
||||
type: "Message",
|
||||
data: msg,
|
||||
});
|
||||
this._processEventQueue();
|
||||
},
|
||||
|
||||
observe(subj, topic, data) {
|
||||
switch (topic) {
|
||||
case "browser-search-service":
|
||||
@ -226,8 +189,8 @@ var ContentSearch = {
|
||||
}
|
||||
},
|
||||
|
||||
removeFormHistoryEntry(msg, entry) {
|
||||
let browserData = this._suggestionDataForBrowser(msg.target);
|
||||
removeFormHistoryEntry(browser, entry) {
|
||||
let browserData = this._suggestionDataForBrowser(browser);
|
||||
if (browserData && browserData.previousFormHistoryResult) {
|
||||
let { previousFormHistoryResult } = browserData;
|
||||
for (let i = 0; i < previousFormHistoryResult.matchCount; i++) {
|
||||
@ -239,7 +202,7 @@ var ContentSearch = {
|
||||
}
|
||||
},
|
||||
|
||||
performSearch(msg, data) {
|
||||
performSearch(browser, data) {
|
||||
this._ensureDataHasProperties(data, [
|
||||
"engineName",
|
||||
"searchString",
|
||||
@ -252,7 +215,6 @@ var ContentSearch = {
|
||||
"",
|
||||
data.searchPurpose
|
||||
);
|
||||
let browser = msg.target;
|
||||
let win = browser.ownerGlobal;
|
||||
if (!win) {
|
||||
// The browser may have been closed between the time its content sent the
|
||||
@ -269,7 +231,7 @@ var ContentSearch = {
|
||||
if (where === "current") {
|
||||
// Since we're going to load the search in the same browser, blur the search
|
||||
// UI to prevent further interaction before we start loading.
|
||||
this._reply(msg, "Blur");
|
||||
this._reply(browser, "Blur");
|
||||
browser.loadURI(submission.uri.spec, {
|
||||
postData: submission.postData,
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
|
||||
@ -308,7 +270,7 @@ var ContentSearch = {
|
||||
let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
||||
// fetch() rejects its promise if there's a pending request, but since we
|
||||
// process our event queue serially, there's never a pending request.
|
||||
this._currentSuggestion = { controller, target: browser };
|
||||
this._currentSuggestion = { controller, browser };
|
||||
let suggestions = await controller.fetch(searchString, priv, engine);
|
||||
this._currentSuggestion = null;
|
||||
|
||||
@ -339,14 +301,14 @@ var ContentSearch = {
|
||||
// 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.
|
||||
isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser.target);
|
||||
isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
if (isPrivate || entry === "") {
|
||||
return false;
|
||||
}
|
||||
let browserData = this._suggestionDataForBrowser(browser.target, true);
|
||||
let browserData = this._suggestionDataForBrowser(browser, true);
|
||||
FormHistory.update(
|
||||
{
|
||||
op: "bump",
|
||||
@ -402,60 +364,59 @@ var ContentSearch = {
|
||||
|
||||
this._currentEventPromise = (async () => {
|
||||
try {
|
||||
await this["_on" + event.type](event.data);
|
||||
await this["_on" + event.type](event);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
} finally {
|
||||
this._currentEventPromise = null;
|
||||
|
||||
this._processEventQueue();
|
||||
}
|
||||
})();
|
||||
},
|
||||
|
||||
_cancelSuggestions(msg) {
|
||||
_cancelSuggestions(browser) {
|
||||
let cancelled = false;
|
||||
// cancel active suggestion request
|
||||
if (
|
||||
this._currentSuggestion &&
|
||||
this._currentSuggestion.target === msg.target
|
||||
this._currentSuggestion.browser === browser
|
||||
) {
|
||||
this._currentSuggestion.controller.stop();
|
||||
cancelled = true;
|
||||
}
|
||||
// cancel queued suggestion requests
|
||||
for (let i = 0; i < this._eventQueue.length; i++) {
|
||||
let m = this._eventQueue[i].data;
|
||||
if (msg.target === m.target && m.data.type === "GetSuggestions") {
|
||||
let m = this._eventQueue[i];
|
||||
if (browser === m.browser && m.name === "GetSuggestions") {
|
||||
this._eventQueue.splice(i, 1);
|
||||
cancelled = true;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (cancelled) {
|
||||
this._reply(msg, "SuggestionsCancelled");
|
||||
this._reply(browser, "SuggestionsCancelled");
|
||||
}
|
||||
},
|
||||
|
||||
async _onMessage(msg) {
|
||||
let methodName = "_onMessage" + msg.data.type;
|
||||
async _onMessage(eventItem) {
|
||||
let methodName = "_onMessage" + eventItem.name;
|
||||
if (methodName in this) {
|
||||
await this._initService();
|
||||
await this[methodName](msg, msg.data.data);
|
||||
if (!Cu.isDeadWrapper(msg.target)) {
|
||||
msg.target.removeEventListener("SwapDocShells", msg, true);
|
||||
}
|
||||
await this[methodName](eventItem.browser, eventItem.data);
|
||||
eventItem.browser.removeEventListener("SwapDocShells", eventItem, true);
|
||||
}
|
||||
},
|
||||
|
||||
_onMessageGetState(msg, data) {
|
||||
return this.currentStateObj(msg.target.ownerGlobal).then(state => {
|
||||
this._reply(msg, "State", state);
|
||||
_onMessageGetState(browser, data) {
|
||||
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
||||
this._reply(browser, "State", state);
|
||||
});
|
||||
},
|
||||
|
||||
_onMessageGetEngine(msg, data) {
|
||||
return this.currentStateObj(msg.target.ownerGlobal).then(state => {
|
||||
this._reply(msg, "Engine", {
|
||||
_onMessageGetEngine(browser, data) {
|
||||
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
||||
this._reply(browser, "Engine", {
|
||||
isPrivateWindow: state.isPrivateWindow,
|
||||
engine: state.isPrivateWindow
|
||||
? state.currentPrivateEngine
|
||||
@ -464,32 +425,32 @@ var ContentSearch = {
|
||||
});
|
||||
},
|
||||
|
||||
_onMessageGetStrings(msg, data) {
|
||||
this._reply(msg, "Strings", this.searchSuggestionUIStrings);
|
||||
_onMessageGetStrings(browser, data) {
|
||||
this._reply(browser, "Strings", this.searchSuggestionUIStrings);
|
||||
},
|
||||
|
||||
_onMessageSearch(msg, data) {
|
||||
this.performSearch(msg, data);
|
||||
_onMessageSearch(browser, data) {
|
||||
this.performSearch(browser, data);
|
||||
},
|
||||
|
||||
_onMessageSetCurrentEngine(msg, data) {
|
||||
_onMessageSetCurrentEngine(browser, data) {
|
||||
Services.search.defaultEngine = Services.search.getEngineByName(data);
|
||||
},
|
||||
|
||||
_onMessageManageEngines(msg) {
|
||||
msg.target.ownerGlobal.openPreferences("paneSearch");
|
||||
_onMessageManageEngines(browser) {
|
||||
browser.ownerGlobal.openPreferences("paneSearch");
|
||||
},
|
||||
|
||||
async _onMessageGetSuggestions(msg, data) {
|
||||
async _onMessageGetSuggestions(browser, data) {
|
||||
this._ensureDataHasProperties(data, ["engineName", "searchString"]);
|
||||
let { engineName, searchString } = data;
|
||||
let suggestions = await this.getSuggestions(
|
||||
engineName,
|
||||
searchString,
|
||||
msg.target
|
||||
browser
|
||||
);
|
||||
|
||||
this._reply(msg, "Suggestions", {
|
||||
this._reply(browser, "Suggestions", {
|
||||
engineName: data.engineName,
|
||||
searchString: suggestions.term,
|
||||
formHistory: suggestions.local,
|
||||
@ -497,32 +458,32 @@ var ContentSearch = {
|
||||
});
|
||||
},
|
||||
|
||||
async _onMessageAddFormHistoryEntry(msg, entry) {
|
||||
await this.addFormHistoryEntry(msg, entry);
|
||||
async _onMessageAddFormHistoryEntry(browser, entry) {
|
||||
await this.addFormHistoryEntry(browser, entry);
|
||||
},
|
||||
|
||||
_onMessageRemoveFormHistoryEntry(msg, entry) {
|
||||
this.removeFormHistoryEntry(msg, entry);
|
||||
_onMessageRemoveFormHistoryEntry(browser, entry) {
|
||||
this.removeFormHistoryEntry(browser, entry);
|
||||
},
|
||||
|
||||
_onMessageSpeculativeConnect(msg, engineName) {
|
||||
_onMessageSpeculativeConnect(browser, engineName) {
|
||||
let engine = Services.search.getEngineByName(engineName);
|
||||
if (!engine) {
|
||||
throw new Error("Unknown engine name: " + engineName);
|
||||
}
|
||||
if (msg.target.contentWindow) {
|
||||
if (browser.contentWindow) {
|
||||
engine.speculativeConnect({
|
||||
window: msg.target.contentWindow,
|
||||
originAttributes: msg.target.contentPrincipal.originAttributes,
|
||||
window: browser.contentWindow,
|
||||
originAttributes: browser.contentPrincipal.originAttributes,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async _onObserve(data) {
|
||||
if (data === "engine-default") {
|
||||
async _onObserve(eventItem) {
|
||||
if (eventItem.data === "engine-default") {
|
||||
let engine = await this._currentEngineObj(false);
|
||||
this._broadcast("CurrentEngine", engine);
|
||||
} else if (data === "engine-default-private") {
|
||||
} else if (eventItem.data === "engine-default-private") {
|
||||
let engine = await this._currentEngineObj(true);
|
||||
this._broadcast("CurrentPrivateEngine", engine);
|
||||
} else {
|
||||
@ -545,26 +506,14 @@ var ContentSearch = {
|
||||
return data;
|
||||
},
|
||||
|
||||
_reply(msg, type, data) {
|
||||
// We reply asyncly to messages, and by the time we reply the browser we're
|
||||
// responding to may have been destroyed. messageManager is null then.
|
||||
if (!Cu.isDeadWrapper(msg.target) && msg.target.messageManager) {
|
||||
msg.target.messageManager.sendAsyncMessage(...this._msgArgs(type, data));
|
||||
}
|
||||
_reply(browser, type, data) {
|
||||
browser.sendMessageToActor(type, data, "ContentSearch");
|
||||
},
|
||||
|
||||
_broadcast(type, data) {
|
||||
Services.mm.broadcastAsyncMessage(...this._msgArgs(type, data));
|
||||
},
|
||||
|
||||
_msgArgs(type, data) {
|
||||
return [
|
||||
OUTBOUND_MESSAGE,
|
||||
{
|
||||
type,
|
||||
data,
|
||||
},
|
||||
];
|
||||
for (let actor of gContentSearchActors) {
|
||||
actor.sendAsyncMessage(type, data);
|
||||
}
|
||||
},
|
||||
|
||||
async _currentEngineObj(usePrivate) {
|
||||
@ -633,3 +582,49 @@ var ContentSearch = {
|
||||
return this._initServicePromise;
|
||||
},
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Firefox", "General")
|
||||
|
||||
with Files("ContentSearch*.jsm"):
|
||||
BUG_COMPONENT = ("Firefox", "Search")
|
||||
|
||||
with Files("LightweightThemeChild.jsm"):
|
||||
BUG_COMPONENT = ("WebExtensions", "Themes")
|
||||
|
||||
@ -33,6 +36,7 @@ FINAL_TARGET_FILES.actors += [
|
||||
'ContentMetaChild.jsm',
|
||||
'ContentMetaParent.jsm',
|
||||
'ContentSearchChild.jsm',
|
||||
'ContentSearchParent.jsm',
|
||||
'ContextMenuChild.jsm',
|
||||
'ContextMenuParent.jsm',
|
||||
'DOMFullscreenChild.jsm',
|
||||
|
@ -684,7 +684,7 @@ var gDidInitialSetUp = false;
|
||||
async function setUp(aNoEngine) {
|
||||
if (!gDidInitialSetUp) {
|
||||
var { ContentSearch } = ChromeUtils.import(
|
||||
"resource:///modules/ContentSearch.jsm"
|
||||
"resource:///actors/ContentSearchParent.jsm"
|
||||
);
|
||||
let originalOnMessageSearch = ContentSearch._onMessageSearch;
|
||||
let originalOnMessageManageEngines = ContentSearch._onMessageManageEngines;
|
||||
|
@ -187,16 +187,13 @@
|
||||
}
|
||||
|
||||
function waitForContentSearchEvent(messageType, cb) {
|
||||
let mm = content.SpecialPowers.Cc[
|
||||
"@mozilla.org/globalmessagemanager;1"
|
||||
].getService();
|
||||
mm.addMessageListener("ContentSearch", function listener(aMsg) {
|
||||
if (aMsg.data.type != messageType) {
|
||||
return;
|
||||
function listener(event) {
|
||||
if (event.detail.type == messageType) {
|
||||
removeEventListener("ContentSearchClient", listener, true);
|
||||
cb(event.detail.data);
|
||||
}
|
||||
mm.removeMessageListener("ContentSearch", listener);
|
||||
cb(aMsg.data.data);
|
||||
});
|
||||
}
|
||||
addEventListener("ContentSearchClient", listener, true);
|
||||
}
|
||||
|
||||
function currentState() {
|
||||
|
@ -154,6 +154,25 @@ let ACTORS = {
|
||||
},
|
||||
},
|
||||
|
||||
ContentSearch: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/ContentSearchParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///actors/ContentSearchChild.jsm",
|
||||
matches: [
|
||||
"about:home",
|
||||
"about:newtab",
|
||||
"about:welcome",
|
||||
"about:privatebrowsing",
|
||||
"chrome://mochitests/content/*",
|
||||
],
|
||||
events: {
|
||||
ContentSearchClient: { capture: true, wantUntrusted: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
ContextMenu: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/ContextMenuParent.jsm",
|
||||
@ -396,24 +415,6 @@ let LEGACY_ACTORS = {
|
||||
},
|
||||
},
|
||||
|
||||
ContentSearch: {
|
||||
child: {
|
||||
module: "resource:///actors/ContentSearchChild.jsm",
|
||||
group: "browsers",
|
||||
matches: [
|
||||
"about:home",
|
||||
"about:newtab",
|
||||
"about:welcome",
|
||||
"about:privatebrowsing",
|
||||
"chrome://mochitests/content/*",
|
||||
],
|
||||
events: {
|
||||
ContentSearchClient: { capture: true, wantUntrusted: true },
|
||||
},
|
||||
messages: ["ContentSearch"],
|
||||
},
|
||||
},
|
||||
|
||||
URIFixup: {
|
||||
child: {
|
||||
module: "resource:///actors/URIFixupChild.jsm",
|
||||
@ -644,7 +645,6 @@ let initializedModules = {};
|
||||
"resource://gre/modules/ContentPrefServiceParent.jsm",
|
||||
"alwaysInit",
|
||||
],
|
||||
["ContentSearch", "resource:///modules/ContentSearch.jsm", "init"],
|
||||
["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
|
||||
].forEach(([name, resource, init]) => {
|
||||
XPCOMUtils.defineLazyGetter(this, name, () => {
|
||||
@ -723,7 +723,6 @@ const listeners = {
|
||||
"AboutLogins:TestOnlyResetOSAuth": ["AboutLoginsParent"],
|
||||
"AboutLogins:UpdateLogin": ["AboutLoginsParent"],
|
||||
"AboutLogins:VulnerableLogins": ["AboutLoginsParent"],
|
||||
ContentSearch: ["ContentSearch"],
|
||||
"Reader:FaviconRequest": ["ReaderParent"],
|
||||
"Reader:UpdateReaderButton": ["ReaderParent"],
|
||||
},
|
||||
|
@ -58,9 +58,6 @@ with Files("*Telemetry.jsm"):
|
||||
with Files("ContentCrashHandlers.jsm"):
|
||||
BUG_COMPONENT = ("Toolkit", "Crash Reporting")
|
||||
|
||||
with Files("ContentSearch.jsm"):
|
||||
BUG_COMPONENT = ("Firefox", "Search")
|
||||
|
||||
with Files("EveryWindow.jsm"):
|
||||
BUG_COMPONENT = ("Firefox", "General")
|
||||
|
||||
@ -135,7 +132,6 @@ EXTRA_JS_MODULES += [
|
||||
'BrowserWindowTracker.jsm',
|
||||
'ContentCrashHandlers.jsm',
|
||||
'ContentObservers.js',
|
||||
'ContentSearch.jsm',
|
||||
'Discovery.jsm',
|
||||
'EveryWindow.jsm',
|
||||
'ExtensionsUI.jsm',
|
||||
|
@ -8,7 +8,6 @@ prefs =
|
||||
[browser_BrowserWindowTracker.js]
|
||||
[browser_ContentSearch.js]
|
||||
support-files =
|
||||
contentSearch.js
|
||||
contentSearchBadImage.xml
|
||||
contentSearchSuggestions.sjs
|
||||
contentSearchSuggestions.xml
|
||||
|
@ -2,9 +2,8 @@
|
||||
* 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/. */
|
||||
|
||||
const TEST_MSG = "ContentSearchTest";
|
||||
const CONTENT_SEARCH_MSG = "ContentSearch";
|
||||
const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
|
||||
const SERVICE_EVENT_TYPE = "ContentSearchService";
|
||||
const CLIENT_EVENT_TYPE = "ContentSearchClient";
|
||||
|
||||
/* import-globals-from ../../../components/search/test/browser/head.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
@ -15,6 +14,20 @@ Services.scriptloader.loadSubScript(
|
||||
var arrayBufferIconTested = false;
|
||||
var plainURIIconTested = false;
|
||||
|
||||
function sendEventToContent(browser, data) {
|
||||
return SpecialPowers.spawn(
|
||||
browser,
|
||||
[CLIENT_EVENT_TYPE, data],
|
||||
(eventName, eventData) => {
|
||||
content.dispatchEvent(
|
||||
new content.CustomEvent(eventName, {
|
||||
detail: Cu.cloneInto(eventData, content),
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
const originalEngine = await Services.search.getDefault();
|
||||
const originalPrivateEngine = await Services.search.getDefaultPrivate();
|
||||
@ -51,11 +64,12 @@ add_task(async function setup() {
|
||||
});
|
||||
|
||||
add_task(async function GetState() {
|
||||
let { mm } = await addTab();
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
let { browser } = await addTab();
|
||||
let statePromise = await waitForTestMsg(browser, "State");
|
||||
sendEventToContent(browser, {
|
||||
type: "GetState",
|
||||
});
|
||||
let msg = await waitForTestMsg(mm, "State");
|
||||
let msg = await statePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "State",
|
||||
data: await currentStateObj(false),
|
||||
@ -66,33 +80,35 @@ add_task(async function GetState() {
|
||||
});
|
||||
|
||||
add_task(async function SetDefaultEngine() {
|
||||
let { mm } = await addTab();
|
||||
let { browser } = await addTab();
|
||||
let newDefaultEngine = await Services.search.getEngineByName("FooChromeIcon");
|
||||
let oldDefaultEngine = await Services.search.getDefault();
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
let searchPromise = await waitForTestMsg(browser, "CurrentEngine");
|
||||
sendEventToContent(browser, {
|
||||
type: "SetCurrentEngine",
|
||||
data: newDefaultEngine.name,
|
||||
});
|
||||
let deferred = PromiseUtils.defer();
|
||||
Services.obs.addObserver(function obs(subj, topic, data) {
|
||||
info("Test observed " + data);
|
||||
if (data == "engine-default") {
|
||||
ok(true, "Test observed engine-default");
|
||||
Services.obs.removeObserver(obs, "browser-search-engine-modified");
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "browser-search-engine-modified");
|
||||
let searchPromise = waitForTestMsg(mm, "CurrentEngine");
|
||||
let deferredPromise = new Promise(resolve => {
|
||||
Services.obs.addObserver(function obs(subj, topic, data) {
|
||||
info("Test observed " + data);
|
||||
if (data == "engine-default") {
|
||||
ok(true, "Test observed engine-default");
|
||||
Services.obs.removeObserver(obs, "browser-search-engine-modified");
|
||||
resolve();
|
||||
}
|
||||
}, "browser-search-engine-modified");
|
||||
});
|
||||
info("Waiting for test to observe engine-default...");
|
||||
await deferred.promise;
|
||||
let msg = await searchPromise;
|
||||
await deferredPromise;
|
||||
let msg = await searchPromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentEngine",
|
||||
data: await constructEngineObj(newDefaultEngine),
|
||||
});
|
||||
|
||||
let enginePromise = await waitForTestMsg(browser, "CurrentEngine");
|
||||
await Services.search.setDefault(oldDefaultEngine);
|
||||
msg = await waitForTestMsg(mm, "CurrentEngine");
|
||||
msg = await enginePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentEngine",
|
||||
data: await constructEngineObj(oldDefaultEngine),
|
||||
@ -103,10 +119,10 @@ add_task(async function SetDefaultEngine() {
|
||||
// as it doesn't need to, so we just test updating the default here.
|
||||
add_task(async function setDefaultEnginePrivate() {
|
||||
const engine = await Services.search.getEngineByName("FooChromeIcon");
|
||||
const { mm } = await addTab();
|
||||
let msgPromise = waitForTestMsg(mm, "CurrentPrivateEngine");
|
||||
const { browser } = await addTab();
|
||||
let enginePromise = await waitForTestMsg(browser, "CurrentPrivateEngine");
|
||||
await Services.search.setDefaultPrivate(engine);
|
||||
let msg = await msgPromise;
|
||||
let msg = await enginePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentPrivateEngine",
|
||||
data: await constructEngineObj(engine),
|
||||
@ -114,17 +130,19 @@ add_task(async function setDefaultEnginePrivate() {
|
||||
});
|
||||
|
||||
add_task(async function modifyEngine() {
|
||||
let { mm } = await addTab();
|
||||
let { browser } = await addTab();
|
||||
let engine = await Services.search.getDefault();
|
||||
let oldAlias = engine.alias;
|
||||
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
engine.alias = "ContentSearchTest";
|
||||
let msg = await waitForTestMsg(mm, "CurrentState");
|
||||
let msg = await statePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentState",
|
||||
data: await currentStateObj(),
|
||||
});
|
||||
statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
engine.alias = oldAlias;
|
||||
msg = await waitForTestMsg(mm, "CurrentState");
|
||||
msg = await statePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentState",
|
||||
data: await currentStateObj(),
|
||||
@ -132,16 +150,18 @@ add_task(async function modifyEngine() {
|
||||
});
|
||||
|
||||
add_task(async function test_hideEngine() {
|
||||
let { mm } = await addTab();
|
||||
let { browser } = await addTab();
|
||||
let engine = await Services.search.getEngineByName("Foo \u2661");
|
||||
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
Services.prefs.setStringPref("browser.search.hiddenOneOffs", engine.name);
|
||||
let msg = await waitForTestMsg(mm, "CurrentState");
|
||||
let msg = await statePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentState",
|
||||
data: await currentStateObj(undefined, "Foo \u2661"),
|
||||
});
|
||||
statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
Services.prefs.clearUserPref("browser.search.hiddenOneOffs");
|
||||
msg = await waitForTestMsg(mm, "CurrentState");
|
||||
msg = await statePromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "CurrentState",
|
||||
data: await currentStateObj(),
|
||||
@ -188,11 +208,11 @@ add_task(async function searchInBackgroundTab() {
|
||||
});
|
||||
|
||||
add_task(async function badImage() {
|
||||
let { mm } = await addTab();
|
||||
let { browser } = await addTab();
|
||||
// If the bad image URI caused an exception to be thrown within ContentSearch,
|
||||
// then we'll hang waiting for the CurrentState responses triggered by the new
|
||||
// engine. That's what we're testing, and obviously it shouldn't happen.
|
||||
let vals = await waitForNewEngine(mm, "contentSearchBadImage.xml", 1);
|
||||
let vals = await waitForNewEngine(browser, "contentSearchBadImage.xml", 1);
|
||||
let engine = vals[0];
|
||||
let finalCurrentStateMsg = vals[vals.length - 1];
|
||||
let expectedCurrentState = await currentStateObj();
|
||||
@ -212,37 +232,43 @@ add_task(async function badImage() {
|
||||
});
|
||||
// Removing the engine triggers a final CurrentState message. Wait for it so
|
||||
// it doesn't trip up subsequent tests.
|
||||
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
await Services.search.removeEngine(engine);
|
||||
await waitForTestMsg(mm, "CurrentState");
|
||||
await statePromise.donePromise;
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function GetSuggestions_AddFormHistoryEntry_RemoveFormHistoryEntry() {
|
||||
let { mm } = await addTab();
|
||||
let { browser } = await addTab();
|
||||
|
||||
// Add the test engine that provides suggestions.
|
||||
let vals = await waitForNewEngine(mm, "contentSearchSuggestions.xml", 0);
|
||||
let vals = await waitForNewEngine(
|
||||
browser,
|
||||
"contentSearchSuggestions.xml",
|
||||
0
|
||||
);
|
||||
let engine = vals[0];
|
||||
|
||||
let searchStr = "browser_ContentSearch.js-suggestions-";
|
||||
|
||||
// Add a form history suggestion and wait for Satchel to notify about it.
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
sendEventToContent(browser, {
|
||||
type: "AddFormHistoryEntry",
|
||||
data: searchStr + "form",
|
||||
});
|
||||
let deferred = PromiseUtils.defer();
|
||||
Services.obs.addObserver(function onAdd(subj, topic, data) {
|
||||
if (data == "formhistory-add") {
|
||||
Services.obs.removeObserver(onAdd, "satchel-storage-changed");
|
||||
executeSoon(() => deferred.resolve());
|
||||
}
|
||||
}, "satchel-storage-changed");
|
||||
await deferred.promise;
|
||||
await new Promise(resolve => {
|
||||
Services.obs.addObserver(function onAdd(subj, topic, data) {
|
||||
if (data == "formhistory-add") {
|
||||
Services.obs.removeObserver(onAdd, "satchel-storage-changed");
|
||||
executeSoon(resolve);
|
||||
}
|
||||
}, "satchel-storage-changed");
|
||||
});
|
||||
|
||||
// Send GetSuggestions using the test engine. Its suggestions should appear
|
||||
// in the remote suggestions in the Suggestions response below.
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
let suggestionsPromise = await waitForTestMsg(browser, "Suggestions");
|
||||
sendEventToContent(browser, {
|
||||
type: "GetSuggestions",
|
||||
data: {
|
||||
engineName: engine.name,
|
||||
@ -251,7 +277,7 @@ add_task(
|
||||
});
|
||||
|
||||
// Check the Suggestions response.
|
||||
let msg = await waitForTestMsg(mm, "Suggestions");
|
||||
let msg = await suggestionsPromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "Suggestions",
|
||||
data: {
|
||||
@ -263,21 +289,23 @@ add_task(
|
||||
});
|
||||
|
||||
// Delete the form history suggestion and wait for Satchel to notify about it.
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
sendEventToContent(browser, {
|
||||
type: "RemoveFormHistoryEntry",
|
||||
data: searchStr + "form",
|
||||
});
|
||||
deferred = PromiseUtils.defer();
|
||||
Services.obs.addObserver(function onRemove(subj, topic, data) {
|
||||
if (data == "formhistory-remove") {
|
||||
Services.obs.removeObserver(onRemove, "satchel-storage-changed");
|
||||
executeSoon(() => deferred.resolve());
|
||||
}
|
||||
}, "satchel-storage-changed");
|
||||
await deferred.promise;
|
||||
|
||||
await new Promise(resolve => {
|
||||
Services.obs.addObserver(function onRemove(subj, topic, data) {
|
||||
if (data == "formhistory-remove") {
|
||||
Services.obs.removeObserver(onRemove, "satchel-storage-changed");
|
||||
executeSoon(resolve);
|
||||
}
|
||||
}, "satchel-storage-changed");
|
||||
});
|
||||
|
||||
// Send GetSuggestions again.
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
suggestionsPromise = await waitForTestMsg(browser, "Suggestions");
|
||||
sendEventToContent(browser, {
|
||||
type: "GetSuggestions",
|
||||
data: {
|
||||
engineName: engine.name,
|
||||
@ -286,7 +314,7 @@ add_task(
|
||||
});
|
||||
|
||||
// The formHistory suggestions in the Suggestions response should be empty.
|
||||
msg = await waitForTestMsg(mm, "Suggestions");
|
||||
msg = await suggestionsPromise.donePromise;
|
||||
checkMsg(msg, {
|
||||
type: "Suggestions",
|
||||
data: {
|
||||
@ -298,15 +326,15 @@ add_task(
|
||||
});
|
||||
|
||||
// Finally, clean up by removing the test engine.
|
||||
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||
await Services.search.removeEngine(engine);
|
||||
await waitForTestMsg(mm, "CurrentState");
|
||||
await statePromise.donePromise;
|
||||
}
|
||||
);
|
||||
|
||||
async function performSearch(browser, data, expectedURL) {
|
||||
let mm = browser.messageManager;
|
||||
let stoppedPromise = BrowserTestUtils.browserStopped(browser, expectedURL);
|
||||
mm.sendAsyncMessage(TEST_MSG, {
|
||||
sendEventToContent(browser, {
|
||||
type: "Search",
|
||||
data,
|
||||
expectedURL,
|
||||
@ -362,44 +390,69 @@ function checkArrayBuffers(actual, expected) {
|
||||
}
|
||||
|
||||
function checkMsg(actualMsg, expectedMsgData) {
|
||||
let actualMsgData = actualMsg.data;
|
||||
SimpleTest.isDeeply(actualMsg.data, expectedMsgData, "Checking message");
|
||||
SimpleTest.isDeeply(actualMsg, expectedMsgData, "Checking message");
|
||||
|
||||
// Engines contain ArrayBuffers which we have to compare byte by byte and
|
||||
// not as Objects (like SimpleTest.isDeeply does).
|
||||
checkArrayBuffers(actualMsgData, expectedMsgData);
|
||||
checkArrayBuffers(actualMsg, expectedMsgData);
|
||||
}
|
||||
|
||||
function waitForTestMsg(mm, type) {
|
||||
return new Promise(resolve => {
|
||||
info("Waiting for " + TEST_MSG + " message " + type + "...");
|
||||
mm.addMessageListener(TEST_MSG, function onMsg(msg) {
|
||||
info("Received " + TEST_MSG + " message " + msg.data.type + "\n");
|
||||
if (msg.data.type == type) {
|
||||
mm.removeMessageListener(TEST_MSG, onMsg);
|
||||
resolve(msg);
|
||||
async function waitForTestMsg(browser, type, count = 1) {
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[SERVICE_EVENT_TYPE, type, count],
|
||||
(childEvent, childType, childCount) => {
|
||||
content.eventDetails = [];
|
||||
function listener(event) {
|
||||
if (event.detail.type != childType) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.eventDetails.push(event.detail);
|
||||
|
||||
if (--childCount > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.removeEventListener(childEvent, listener, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
content.addEventListener(childEvent, listener, true);
|
||||
}
|
||||
);
|
||||
|
||||
let donePromise = SpecialPowers.spawn(
|
||||
browser,
|
||||
[type, count],
|
||||
async (childType, childCount) => {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return content.eventDetails.length == childCount;
|
||||
}, "Expected " + childType + " event");
|
||||
|
||||
return childCount > 1 ? content.eventDetails : content.eventDetails[0];
|
||||
}
|
||||
);
|
||||
|
||||
return { donePromise };
|
||||
}
|
||||
|
||||
function waitForNewEngine(mm, basename, numImages) {
|
||||
async function waitForNewEngine(browser, basename, numImages) {
|
||||
info("Waiting for engine to be added: " + basename);
|
||||
|
||||
// Wait for the search events triggered by adding the new engine.
|
||||
// engine-added engine-loaded
|
||||
let expectedSearchEvents = ["CurrentState", "CurrentState"];
|
||||
let count = 2;
|
||||
// engine-changed for each of the images
|
||||
for (let i = 0; i < numImages; i++) {
|
||||
expectedSearchEvents.push("CurrentState");
|
||||
count++;
|
||||
}
|
||||
let eventPromises = expectedSearchEvents.map(e => waitForTestMsg(mm, e));
|
||||
|
||||
let statePromise = await waitForTestMsg(browser, "CurrentState", count);
|
||||
|
||||
// Wait for addEngine().
|
||||
let url = getRootDirectory(gTestPath) + basename;
|
||||
return Promise.all(
|
||||
[Services.search.addEngine(url, "", false)].concat(eventPromises)
|
||||
);
|
||||
let engine = await Services.search.addEngine(url, "", false);
|
||||
let results = await statePromise.donePromise;
|
||||
return [engine, ...results];
|
||||
}
|
||||
|
||||
async function addTab() {
|
||||
@ -409,10 +462,7 @@ async function addTab() {
|
||||
);
|
||||
registerCleanupFunction(() => gBrowser.removeTab(tab));
|
||||
|
||||
let url = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript(url, false);
|
||||
return { browser: tab.linkedBrowser, mm };
|
||||
return { browser: tab.linkedBrowser };
|
||||
}
|
||||
|
||||
var currentStateObj = async function(isPrivateWindowValue, hiddenEngine = "") {
|
||||
|
@ -1,33 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
const TEST_MSG = "ContentSearchTest";
|
||||
const SERVICE_EVENT_TYPE = "ContentSearchService";
|
||||
const CLIENT_EVENT_TYPE = "ContentSearchClient";
|
||||
|
||||
// Forward events from the in-content service to the test.
|
||||
content.addEventListener(SERVICE_EVENT_TYPE, event => {
|
||||
// The event dispatch code in content.js clones the event detail into the
|
||||
// content scope. That's generally the right thing, but causes us to end
|
||||
// up with an XrayWrapper to it here, which will screw us up when trying to
|
||||
// serialize the object in sendAsyncMessage. Waive Xrays for the benefit of
|
||||
// the test machinery.
|
||||
sendAsyncMessage(TEST_MSG, Cu.waiveXrays(event.detail));
|
||||
});
|
||||
|
||||
// Forward messages from the test to the in-content service.
|
||||
addMessageListener(TEST_MSG, msg => {
|
||||
(async function() {
|
||||
// If the message is a search, stop the page from loading and then tell the
|
||||
// test that it loaded.
|
||||
|
||||
content.dispatchEvent(
|
||||
new content.CustomEvent(CLIENT_EVENT_TYPE, {
|
||||
detail: Cu.cloneInto(msg.data, content),
|
||||
})
|
||||
);
|
||||
})();
|
||||
});
|
@ -18,11 +18,6 @@ ChromeUtils.defineModuleGetter(
|
||||
"AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ContentSearch",
|
||||
"resource:///modules/ContentSearch.jsm"
|
||||
);
|
||||
|
||||
const SIMPLETEST_OVERRIDES = [
|
||||
"ok",
|
||||
|
Loading…
Reference in New Issue
Block a user