forked from mirrors/gecko-dev
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
11 changed files with 294 additions and 297 deletions
|
|
@ -6,24 +6,19 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContentSearchChild"];
|
var EXPORTED_SYMBOLS = ["ContentSearchChild"];
|
||||||
|
|
||||||
const { ActorChild } = ChromeUtils.import(
|
class ContentSearchChild extends JSWindowActorChild {
|
||||||
"resource://gre/modules/ActorChild.jsm"
|
|
||||||
);
|
|
||||||
|
|
||||||
class ContentSearchChild extends ActorChild {
|
|
||||||
handleEvent(event) {
|
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) {
|
receiveMessage(msg) {
|
||||||
this._fireEvent(msg.data.type, msg.data.data);
|
// The message gets translated into an event that
|
||||||
}
|
// is then sent to the content.
|
||||||
|
this._fireEvent(msg.name, msg.data);
|
||||||
_sendMsg(type, data = null) {
|
|
||||||
this.mm.sendAsyncMessage("ContentSearch", {
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fireEvent(type, data = null) {
|
_fireEvent(type, data = null) {
|
||||||
|
|
@ -34,10 +29,10 @@ class ContentSearchChild extends ActorChild {
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
this.content
|
this.contentWindow
|
||||||
);
|
);
|
||||||
this.content.dispatchEvent(
|
this.contentWindow.dispatchEvent(
|
||||||
new this.content.CustomEvent("ContentSearchService", event)
|
new this.contentWindow.CustomEvent("ContentSearchService", event)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContentSearch"];
|
var EXPORTED_SYMBOLS = ["ContentSearchParent", "ContentSearch"];
|
||||||
|
|
||||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const { XPCOMUtils } = ChromeUtils.import(
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
|
@ -28,18 +28,13 @@ ChromeUtils.defineModuleGetter(
|
||||||
"resource://gre/modules/SearchSuggestionController.jsm"
|
"resource://gre/modules/SearchSuggestionController.jsm"
|
||||||
);
|
);
|
||||||
|
|
||||||
const INBOUND_MESSAGE = "ContentSearch";
|
|
||||||
const OUTBOUND_MESSAGE = INBOUND_MESSAGE;
|
|
||||||
const MAX_LOCAL_SUGGESTIONS = 3;
|
const MAX_LOCAL_SUGGESTIONS = 3;
|
||||||
const MAX_SUGGESTIONS = 6;
|
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:
|
* Inbound messages have the following types:
|
||||||
*
|
*
|
||||||
* AddFormHistoryEntry
|
* AddFormHistoryEntry
|
||||||
|
|
@ -95,7 +90,9 @@ const MAX_SUGGESTIONS = 6;
|
||||||
* data: null
|
* data: null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ContentSearch = {
|
let ContentSearch = {
|
||||||
|
initialized: false,
|
||||||
|
|
||||||
// Inbound events are queued and processed in FIFO order instead of handling
|
// Inbound events are queued and processed in FIFO order instead of handling
|
||||||
// them immediately, which would result in non-FIFO responses due to the
|
// them immediately, which would result in non-FIFO responses due to the
|
||||||
// asynchrononicity added by converting image data URIs to ArrayBuffers.
|
// asynchrononicity added by converting image data URIs to ArrayBuffers.
|
||||||
|
|
@ -114,6 +111,7 @@ var ContentSearch = {
|
||||||
_currentSuggestion: null,
|
_currentSuggestion: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
if (!this.initialized) {
|
||||||
Services.obs.addObserver(this, "browser-search-engine-modified");
|
Services.obs.addObserver(this, "browser-search-engine-modified");
|
||||||
Services.obs.addObserver(this, "browser-search-service");
|
Services.obs.addObserver(this, "browser-search-service");
|
||||||
Services.obs.addObserver(this, "shutdown-leaks-before-check");
|
Services.obs.addObserver(this, "shutdown-leaks-before-check");
|
||||||
|
|
@ -121,6 +119,9 @@ var ContentSearch = {
|
||||||
this._stringBundle = Services.strings.createBundle(
|
this._stringBundle = Services.strings.createBundle(
|
||||||
"chrome://global/locale/autocomplete.properties"
|
"chrome://global/locale/autocomplete.properties"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get searchSuggestionUIStrings() {
|
get searchSuggestionUIStrings() {
|
||||||
|
|
@ -147,6 +148,10 @@ var ContentSearch = {
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return new Promise();
|
||||||
|
}
|
||||||
|
|
||||||
if (this._destroyedPromise) {
|
if (this._destroyedPromise) {
|
||||||
return this._destroyedPromise;
|
return this._destroyedPromise;
|
||||||
}
|
}
|
||||||
|
|
@ -160,48 +165,6 @@ var ContentSearch = {
|
||||||
return this._destroyedPromise;
|
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) {
|
observe(subj, topic, data) {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
case "browser-search-service":
|
case "browser-search-service":
|
||||||
|
|
@ -226,8 +189,8 @@ var ContentSearch = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeFormHistoryEntry(msg, entry) {
|
removeFormHistoryEntry(browser, entry) {
|
||||||
let browserData = this._suggestionDataForBrowser(msg.target);
|
let browserData = this._suggestionDataForBrowser(browser);
|
||||||
if (browserData && browserData.previousFormHistoryResult) {
|
if (browserData && browserData.previousFormHistoryResult) {
|
||||||
let { previousFormHistoryResult } = browserData;
|
let { previousFormHistoryResult } = browserData;
|
||||||
for (let i = 0; i < previousFormHistoryResult.matchCount; i++) {
|
for (let i = 0; i < previousFormHistoryResult.matchCount; i++) {
|
||||||
|
|
@ -239,7 +202,7 @@ var ContentSearch = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
performSearch(msg, data) {
|
performSearch(browser, data) {
|
||||||
this._ensureDataHasProperties(data, [
|
this._ensureDataHasProperties(data, [
|
||||||
"engineName",
|
"engineName",
|
||||||
"searchString",
|
"searchString",
|
||||||
|
|
@ -252,7 +215,6 @@ var ContentSearch = {
|
||||||
"",
|
"",
|
||||||
data.searchPurpose
|
data.searchPurpose
|
||||||
);
|
);
|
||||||
let browser = msg.target;
|
|
||||||
let win = browser.ownerGlobal;
|
let win = browser.ownerGlobal;
|
||||||
if (!win) {
|
if (!win) {
|
||||||
// The browser may have been closed between the time its content sent the
|
// The browser may have been closed between the time its content sent the
|
||||||
|
|
@ -269,7 +231,7 @@ var ContentSearch = {
|
||||||
if (where === "current") {
|
if (where === "current") {
|
||||||
// Since we're going to load the search in the same browser, blur the search
|
// Since we're going to load the search in the same browser, blur the search
|
||||||
// UI to prevent further interaction before we start loading.
|
// UI to prevent further interaction before we start loading.
|
||||||
this._reply(msg, "Blur");
|
this._reply(browser, "Blur");
|
||||||
browser.loadURI(submission.uri.spec, {
|
browser.loadURI(submission.uri.spec, {
|
||||||
postData: submission.postData,
|
postData: submission.postData,
|
||||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
|
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
|
||||||
|
|
@ -308,7 +270,7 @@ var ContentSearch = {
|
||||||
let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
||||||
// fetch() rejects its promise if there's a pending request, but since we
|
// fetch() rejects its promise if there's a pending request, but since we
|
||||||
// process our event queue serially, there's never a pending request.
|
// 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);
|
let suggestions = await controller.fetch(searchString, priv, engine);
|
||||||
this._currentSuggestion = null;
|
this._currentSuggestion = null;
|
||||||
|
|
||||||
|
|
@ -339,14 +301,14 @@ var ContentSearch = {
|
||||||
// isBrowserPrivate assumes that the passed-in browser has all the normal
|
// isBrowserPrivate assumes that the passed-in browser has all the normal
|
||||||
// properties, which won't be true if the browser has been destroyed.
|
// 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.
|
// That may be the case here due to the asynchronous nature of messaging.
|
||||||
isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser.target);
|
isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isPrivate || entry === "") {
|
if (isPrivate || entry === "") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let browserData = this._suggestionDataForBrowser(browser.target, true);
|
let browserData = this._suggestionDataForBrowser(browser, true);
|
||||||
FormHistory.update(
|
FormHistory.update(
|
||||||
{
|
{
|
||||||
op: "bump",
|
op: "bump",
|
||||||
|
|
@ -402,60 +364,59 @@ var ContentSearch = {
|
||||||
|
|
||||||
this._currentEventPromise = (async () => {
|
this._currentEventPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
await this["_on" + event.type](event.data);
|
await this["_on" + event.type](event);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Cu.reportError(err);
|
Cu.reportError(err);
|
||||||
} finally {
|
} finally {
|
||||||
this._currentEventPromise = null;
|
this._currentEventPromise = null;
|
||||||
|
|
||||||
this._processEventQueue();
|
this._processEventQueue();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
|
|
||||||
_cancelSuggestions(msg) {
|
_cancelSuggestions(browser) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
// cancel active suggestion request
|
// cancel active suggestion request
|
||||||
if (
|
if (
|
||||||
this._currentSuggestion &&
|
this._currentSuggestion &&
|
||||||
this._currentSuggestion.target === msg.target
|
this._currentSuggestion.browser === browser
|
||||||
) {
|
) {
|
||||||
this._currentSuggestion.controller.stop();
|
this._currentSuggestion.controller.stop();
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
}
|
}
|
||||||
// cancel queued suggestion requests
|
// cancel queued suggestion requests
|
||||||
for (let i = 0; i < this._eventQueue.length; i++) {
|
for (let i = 0; i < this._eventQueue.length; i++) {
|
||||||
let m = this._eventQueue[i].data;
|
let m = this._eventQueue[i];
|
||||||
if (msg.target === m.target && m.data.type === "GetSuggestions") {
|
if (browser === m.browser && m.name === "GetSuggestions") {
|
||||||
this._eventQueue.splice(i, 1);
|
this._eventQueue.splice(i, 1);
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
this._reply(msg, "SuggestionsCancelled");
|
this._reply(browser, "SuggestionsCancelled");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async _onMessage(msg) {
|
async _onMessage(eventItem) {
|
||||||
let methodName = "_onMessage" + msg.data.type;
|
let methodName = "_onMessage" + eventItem.name;
|
||||||
if (methodName in this) {
|
if (methodName in this) {
|
||||||
await this._initService();
|
await this._initService();
|
||||||
await this[methodName](msg, msg.data.data);
|
await this[methodName](eventItem.browser, eventItem.data);
|
||||||
if (!Cu.isDeadWrapper(msg.target)) {
|
eventItem.browser.removeEventListener("SwapDocShells", eventItem, true);
|
||||||
msg.target.removeEventListener("SwapDocShells", msg, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageGetState(msg, data) {
|
_onMessageGetState(browser, data) {
|
||||||
return this.currentStateObj(msg.target.ownerGlobal).then(state => {
|
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
||||||
this._reply(msg, "State", state);
|
this._reply(browser, "State", state);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageGetEngine(msg, data) {
|
_onMessageGetEngine(browser, data) {
|
||||||
return this.currentStateObj(msg.target.ownerGlobal).then(state => {
|
return this.currentStateObj(browser.ownerGlobal).then(state => {
|
||||||
this._reply(msg, "Engine", {
|
this._reply(browser, "Engine", {
|
||||||
isPrivateWindow: state.isPrivateWindow,
|
isPrivateWindow: state.isPrivateWindow,
|
||||||
engine: state.isPrivateWindow
|
engine: state.isPrivateWindow
|
||||||
? state.currentPrivateEngine
|
? state.currentPrivateEngine
|
||||||
|
|
@ -464,32 +425,32 @@ var ContentSearch = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageGetStrings(msg, data) {
|
_onMessageGetStrings(browser, data) {
|
||||||
this._reply(msg, "Strings", this.searchSuggestionUIStrings);
|
this._reply(browser, "Strings", this.searchSuggestionUIStrings);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageSearch(msg, data) {
|
_onMessageSearch(browser, data) {
|
||||||
this.performSearch(msg, data);
|
this.performSearch(browser, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageSetCurrentEngine(msg, data) {
|
_onMessageSetCurrentEngine(browser, data) {
|
||||||
Services.search.defaultEngine = Services.search.getEngineByName(data);
|
Services.search.defaultEngine = Services.search.getEngineByName(data);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageManageEngines(msg) {
|
_onMessageManageEngines(browser) {
|
||||||
msg.target.ownerGlobal.openPreferences("paneSearch");
|
browser.ownerGlobal.openPreferences("paneSearch");
|
||||||
},
|
},
|
||||||
|
|
||||||
async _onMessageGetSuggestions(msg, data) {
|
async _onMessageGetSuggestions(browser, data) {
|
||||||
this._ensureDataHasProperties(data, ["engineName", "searchString"]);
|
this._ensureDataHasProperties(data, ["engineName", "searchString"]);
|
||||||
let { engineName, searchString } = data;
|
let { engineName, searchString } = data;
|
||||||
let suggestions = await this.getSuggestions(
|
let suggestions = await this.getSuggestions(
|
||||||
engineName,
|
engineName,
|
||||||
searchString,
|
searchString,
|
||||||
msg.target
|
browser
|
||||||
);
|
);
|
||||||
|
|
||||||
this._reply(msg, "Suggestions", {
|
this._reply(browser, "Suggestions", {
|
||||||
engineName: data.engineName,
|
engineName: data.engineName,
|
||||||
searchString: suggestions.term,
|
searchString: suggestions.term,
|
||||||
formHistory: suggestions.local,
|
formHistory: suggestions.local,
|
||||||
|
|
@ -497,32 +458,32 @@ var ContentSearch = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async _onMessageAddFormHistoryEntry(msg, entry) {
|
async _onMessageAddFormHistoryEntry(browser, entry) {
|
||||||
await this.addFormHistoryEntry(msg, entry);
|
await this.addFormHistoryEntry(browser, entry);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageRemoveFormHistoryEntry(msg, entry) {
|
_onMessageRemoveFormHistoryEntry(browser, entry) {
|
||||||
this.removeFormHistoryEntry(msg, entry);
|
this.removeFormHistoryEntry(browser, entry);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageSpeculativeConnect(msg, engineName) {
|
_onMessageSpeculativeConnect(browser, engineName) {
|
||||||
let engine = Services.search.getEngineByName(engineName);
|
let engine = Services.search.getEngineByName(engineName);
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
throw new Error("Unknown engine name: " + engineName);
|
throw new Error("Unknown engine name: " + engineName);
|
||||||
}
|
}
|
||||||
if (msg.target.contentWindow) {
|
if (browser.contentWindow) {
|
||||||
engine.speculativeConnect({
|
engine.speculativeConnect({
|
||||||
window: msg.target.contentWindow,
|
window: browser.contentWindow,
|
||||||
originAttributes: msg.target.contentPrincipal.originAttributes,
|
originAttributes: browser.contentPrincipal.originAttributes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async _onObserve(data) {
|
async _onObserve(eventItem) {
|
||||||
if (data === "engine-default") {
|
if (eventItem.data === "engine-default") {
|
||||||
let engine = await this._currentEngineObj(false);
|
let engine = await this._currentEngineObj(false);
|
||||||
this._broadcast("CurrentEngine", engine);
|
this._broadcast("CurrentEngine", engine);
|
||||||
} else if (data === "engine-default-private") {
|
} else if (eventItem.data === "engine-default-private") {
|
||||||
let engine = await this._currentEngineObj(true);
|
let engine = await this._currentEngineObj(true);
|
||||||
this._broadcast("CurrentPrivateEngine", engine);
|
this._broadcast("CurrentPrivateEngine", engine);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -545,26 +506,14 @@ var ContentSearch = {
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
_reply(msg, type, data) {
|
_reply(browser, type, data) {
|
||||||
// We reply asyncly to messages, and by the time we reply the browser we're
|
browser.sendMessageToActor(type, data, "ContentSearch");
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_broadcast(type, data) {
|
_broadcast(type, data) {
|
||||||
Services.mm.broadcastAsyncMessage(...this._msgArgs(type, data));
|
for (let actor of gContentSearchActors) {
|
||||||
},
|
actor.sendAsyncMessage(type, data);
|
||||||
|
}
|
||||||
_msgArgs(type, data) {
|
|
||||||
return [
|
|
||||||
OUTBOUND_MESSAGE,
|
|
||||||
{
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async _currentEngineObj(usePrivate) {
|
async _currentEngineObj(usePrivate) {
|
||||||
|
|
@ -633,3 +582,49 @@ var ContentSearch = {
|
||||||
return this._initServicePromise;
|
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("**"):
|
with Files("**"):
|
||||||
BUG_COMPONENT = ("Firefox", "General")
|
BUG_COMPONENT = ("Firefox", "General")
|
||||||
|
|
||||||
|
with Files("ContentSearch*.jsm"):
|
||||||
|
BUG_COMPONENT = ("Firefox", "Search")
|
||||||
|
|
||||||
with Files("LightweightThemeChild.jsm"):
|
with Files("LightweightThemeChild.jsm"):
|
||||||
BUG_COMPONENT = ("WebExtensions", "Themes")
|
BUG_COMPONENT = ("WebExtensions", "Themes")
|
||||||
|
|
||||||
|
|
@ -33,6 +36,7 @@ FINAL_TARGET_FILES.actors += [
|
||||||
'ContentMetaChild.jsm',
|
'ContentMetaChild.jsm',
|
||||||
'ContentMetaParent.jsm',
|
'ContentMetaParent.jsm',
|
||||||
'ContentSearchChild.jsm',
|
'ContentSearchChild.jsm',
|
||||||
|
'ContentSearchParent.jsm',
|
||||||
'ContextMenuChild.jsm',
|
'ContextMenuChild.jsm',
|
||||||
'ContextMenuParent.jsm',
|
'ContextMenuParent.jsm',
|
||||||
'DOMFullscreenChild.jsm',
|
'DOMFullscreenChild.jsm',
|
||||||
|
|
|
||||||
|
|
@ -684,7 +684,7 @@ var gDidInitialSetUp = false;
|
||||||
async function setUp(aNoEngine) {
|
async function setUp(aNoEngine) {
|
||||||
if (!gDidInitialSetUp) {
|
if (!gDidInitialSetUp) {
|
||||||
var { ContentSearch } = ChromeUtils.import(
|
var { ContentSearch } = ChromeUtils.import(
|
||||||
"resource:///modules/ContentSearch.jsm"
|
"resource:///actors/ContentSearchParent.jsm"
|
||||||
);
|
);
|
||||||
let originalOnMessageSearch = ContentSearch._onMessageSearch;
|
let originalOnMessageSearch = ContentSearch._onMessageSearch;
|
||||||
let originalOnMessageManageEngines = ContentSearch._onMessageManageEngines;
|
let originalOnMessageManageEngines = ContentSearch._onMessageManageEngines;
|
||||||
|
|
|
||||||
|
|
@ -187,16 +187,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForContentSearchEvent(messageType, cb) {
|
function waitForContentSearchEvent(messageType, cb) {
|
||||||
let mm = content.SpecialPowers.Cc[
|
function listener(event) {
|
||||||
"@mozilla.org/globalmessagemanager;1"
|
if (event.detail.type == messageType) {
|
||||||
].getService();
|
removeEventListener("ContentSearchClient", listener, true);
|
||||||
mm.addMessageListener("ContentSearch", function listener(aMsg) {
|
cb(event.detail.data);
|
||||||
if (aMsg.data.type != messageType) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
mm.removeMessageListener("ContentSearch", listener);
|
}
|
||||||
cb(aMsg.data.data);
|
addEventListener("ContentSearchClient", listener, true);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentState() {
|
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: {
|
ContextMenu: {
|
||||||
parent: {
|
parent: {
|
||||||
moduleURI: "resource:///actors/ContextMenuParent.jsm",
|
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: {
|
URIFixup: {
|
||||||
child: {
|
child: {
|
||||||
module: "resource:///actors/URIFixupChild.jsm",
|
module: "resource:///actors/URIFixupChild.jsm",
|
||||||
|
|
@ -644,7 +645,6 @@ let initializedModules = {};
|
||||||
"resource://gre/modules/ContentPrefServiceParent.jsm",
|
"resource://gre/modules/ContentPrefServiceParent.jsm",
|
||||||
"alwaysInit",
|
"alwaysInit",
|
||||||
],
|
],
|
||||||
["ContentSearch", "resource:///modules/ContentSearch.jsm", "init"],
|
|
||||||
["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
|
["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
|
||||||
].forEach(([name, resource, init]) => {
|
].forEach(([name, resource, init]) => {
|
||||||
XPCOMUtils.defineLazyGetter(this, name, () => {
|
XPCOMUtils.defineLazyGetter(this, name, () => {
|
||||||
|
|
@ -723,7 +723,6 @@ const listeners = {
|
||||||
"AboutLogins:TestOnlyResetOSAuth": ["AboutLoginsParent"],
|
"AboutLogins:TestOnlyResetOSAuth": ["AboutLoginsParent"],
|
||||||
"AboutLogins:UpdateLogin": ["AboutLoginsParent"],
|
"AboutLogins:UpdateLogin": ["AboutLoginsParent"],
|
||||||
"AboutLogins:VulnerableLogins": ["AboutLoginsParent"],
|
"AboutLogins:VulnerableLogins": ["AboutLoginsParent"],
|
||||||
ContentSearch: ["ContentSearch"],
|
|
||||||
"Reader:FaviconRequest": ["ReaderParent"],
|
"Reader:FaviconRequest": ["ReaderParent"],
|
||||||
"Reader:UpdateReaderButton": ["ReaderParent"],
|
"Reader:UpdateReaderButton": ["ReaderParent"],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,6 @@ with Files("*Telemetry.jsm"):
|
||||||
with Files("ContentCrashHandlers.jsm"):
|
with Files("ContentCrashHandlers.jsm"):
|
||||||
BUG_COMPONENT = ("Toolkit", "Crash Reporting")
|
BUG_COMPONENT = ("Toolkit", "Crash Reporting")
|
||||||
|
|
||||||
with Files("ContentSearch.jsm"):
|
|
||||||
BUG_COMPONENT = ("Firefox", "Search")
|
|
||||||
|
|
||||||
with Files("EveryWindow.jsm"):
|
with Files("EveryWindow.jsm"):
|
||||||
BUG_COMPONENT = ("Firefox", "General")
|
BUG_COMPONENT = ("Firefox", "General")
|
||||||
|
|
||||||
|
|
@ -135,7 +132,6 @@ EXTRA_JS_MODULES += [
|
||||||
'BrowserWindowTracker.jsm',
|
'BrowserWindowTracker.jsm',
|
||||||
'ContentCrashHandlers.jsm',
|
'ContentCrashHandlers.jsm',
|
||||||
'ContentObservers.js',
|
'ContentObservers.js',
|
||||||
'ContentSearch.jsm',
|
|
||||||
'Discovery.jsm',
|
'Discovery.jsm',
|
||||||
'EveryWindow.jsm',
|
'EveryWindow.jsm',
|
||||||
'ExtensionsUI.jsm',
|
'ExtensionsUI.jsm',
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ prefs =
|
||||||
[browser_BrowserWindowTracker.js]
|
[browser_BrowserWindowTracker.js]
|
||||||
[browser_ContentSearch.js]
|
[browser_ContentSearch.js]
|
||||||
support-files =
|
support-files =
|
||||||
contentSearch.js
|
|
||||||
contentSearchBadImage.xml
|
contentSearchBadImage.xml
|
||||||
contentSearchSuggestions.sjs
|
contentSearchSuggestions.sjs
|
||||||
contentSearchSuggestions.xml
|
contentSearchSuggestions.xml
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
const TEST_MSG = "ContentSearchTest";
|
const SERVICE_EVENT_TYPE = "ContentSearchService";
|
||||||
const CONTENT_SEARCH_MSG = "ContentSearch";
|
const CLIENT_EVENT_TYPE = "ContentSearchClient";
|
||||||
const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
|
|
||||||
|
|
||||||
/* import-globals-from ../../../components/search/test/browser/head.js */
|
/* import-globals-from ../../../components/search/test/browser/head.js */
|
||||||
Services.scriptloader.loadSubScript(
|
Services.scriptloader.loadSubScript(
|
||||||
|
|
@ -15,6 +14,20 @@ Services.scriptloader.loadSubScript(
|
||||||
var arrayBufferIconTested = false;
|
var arrayBufferIconTested = false;
|
||||||
var plainURIIconTested = 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() {
|
add_task(async function setup() {
|
||||||
const originalEngine = await Services.search.getDefault();
|
const originalEngine = await Services.search.getDefault();
|
||||||
const originalPrivateEngine = await Services.search.getDefaultPrivate();
|
const originalPrivateEngine = await Services.search.getDefaultPrivate();
|
||||||
|
|
@ -51,11 +64,12 @@ add_task(async function setup() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function GetState() {
|
add_task(async function GetState() {
|
||||||
let { mm } = await addTab();
|
let { browser } = await addTab();
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
let statePromise = await waitForTestMsg(browser, "State");
|
||||||
|
sendEventToContent(browser, {
|
||||||
type: "GetState",
|
type: "GetState",
|
||||||
});
|
});
|
||||||
let msg = await waitForTestMsg(mm, "State");
|
let msg = await statePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "State",
|
type: "State",
|
||||||
data: await currentStateObj(false),
|
data: await currentStateObj(false),
|
||||||
|
|
@ -66,33 +80,35 @@ add_task(async function GetState() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function SetDefaultEngine() {
|
add_task(async function SetDefaultEngine() {
|
||||||
let { mm } = await addTab();
|
let { browser } = await addTab();
|
||||||
let newDefaultEngine = await Services.search.getEngineByName("FooChromeIcon");
|
let newDefaultEngine = await Services.search.getEngineByName("FooChromeIcon");
|
||||||
let oldDefaultEngine = await Services.search.getDefault();
|
let oldDefaultEngine = await Services.search.getDefault();
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
let searchPromise = await waitForTestMsg(browser, "CurrentEngine");
|
||||||
|
sendEventToContent(browser, {
|
||||||
type: "SetCurrentEngine",
|
type: "SetCurrentEngine",
|
||||||
data: newDefaultEngine.name,
|
data: newDefaultEngine.name,
|
||||||
});
|
});
|
||||||
let deferred = PromiseUtils.defer();
|
let deferredPromise = new Promise(resolve => {
|
||||||
Services.obs.addObserver(function obs(subj, topic, data) {
|
Services.obs.addObserver(function obs(subj, topic, data) {
|
||||||
info("Test observed " + data);
|
info("Test observed " + data);
|
||||||
if (data == "engine-default") {
|
if (data == "engine-default") {
|
||||||
ok(true, "Test observed engine-default");
|
ok(true, "Test observed engine-default");
|
||||||
Services.obs.removeObserver(obs, "browser-search-engine-modified");
|
Services.obs.removeObserver(obs, "browser-search-engine-modified");
|
||||||
deferred.resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}, "browser-search-engine-modified");
|
}, "browser-search-engine-modified");
|
||||||
let searchPromise = waitForTestMsg(mm, "CurrentEngine");
|
});
|
||||||
info("Waiting for test to observe engine-default...");
|
info("Waiting for test to observe engine-default...");
|
||||||
await deferred.promise;
|
await deferredPromise;
|
||||||
let msg = await searchPromise;
|
let msg = await searchPromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentEngine",
|
type: "CurrentEngine",
|
||||||
data: await constructEngineObj(newDefaultEngine),
|
data: await constructEngineObj(newDefaultEngine),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let enginePromise = await waitForTestMsg(browser, "CurrentEngine");
|
||||||
await Services.search.setDefault(oldDefaultEngine);
|
await Services.search.setDefault(oldDefaultEngine);
|
||||||
msg = await waitForTestMsg(mm, "CurrentEngine");
|
msg = await enginePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentEngine",
|
type: "CurrentEngine",
|
||||||
data: await constructEngineObj(oldDefaultEngine),
|
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.
|
// as it doesn't need to, so we just test updating the default here.
|
||||||
add_task(async function setDefaultEnginePrivate() {
|
add_task(async function setDefaultEnginePrivate() {
|
||||||
const engine = await Services.search.getEngineByName("FooChromeIcon");
|
const engine = await Services.search.getEngineByName("FooChromeIcon");
|
||||||
const { mm } = await addTab();
|
const { browser } = await addTab();
|
||||||
let msgPromise = waitForTestMsg(mm, "CurrentPrivateEngine");
|
let enginePromise = await waitForTestMsg(browser, "CurrentPrivateEngine");
|
||||||
await Services.search.setDefaultPrivate(engine);
|
await Services.search.setDefaultPrivate(engine);
|
||||||
let msg = await msgPromise;
|
let msg = await enginePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentPrivateEngine",
|
type: "CurrentPrivateEngine",
|
||||||
data: await constructEngineObj(engine),
|
data: await constructEngineObj(engine),
|
||||||
|
|
@ -114,17 +130,19 @@ add_task(async function setDefaultEnginePrivate() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function modifyEngine() {
|
add_task(async function modifyEngine() {
|
||||||
let { mm } = await addTab();
|
let { browser } = await addTab();
|
||||||
let engine = await Services.search.getDefault();
|
let engine = await Services.search.getDefault();
|
||||||
let oldAlias = engine.alias;
|
let oldAlias = engine.alias;
|
||||||
|
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
engine.alias = "ContentSearchTest";
|
engine.alias = "ContentSearchTest";
|
||||||
let msg = await waitForTestMsg(mm, "CurrentState");
|
let msg = await statePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentState",
|
type: "CurrentState",
|
||||||
data: await currentStateObj(),
|
data: await currentStateObj(),
|
||||||
});
|
});
|
||||||
|
statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
engine.alias = oldAlias;
|
engine.alias = oldAlias;
|
||||||
msg = await waitForTestMsg(mm, "CurrentState");
|
msg = await statePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentState",
|
type: "CurrentState",
|
||||||
data: await currentStateObj(),
|
data: await currentStateObj(),
|
||||||
|
|
@ -132,16 +150,18 @@ add_task(async function modifyEngine() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function test_hideEngine() {
|
add_task(async function test_hideEngine() {
|
||||||
let { mm } = await addTab();
|
let { browser } = await addTab();
|
||||||
let engine = await Services.search.getEngineByName("Foo \u2661");
|
let engine = await Services.search.getEngineByName("Foo \u2661");
|
||||||
|
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
Services.prefs.setStringPref("browser.search.hiddenOneOffs", engine.name);
|
Services.prefs.setStringPref("browser.search.hiddenOneOffs", engine.name);
|
||||||
let msg = await waitForTestMsg(mm, "CurrentState");
|
let msg = await statePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentState",
|
type: "CurrentState",
|
||||||
data: await currentStateObj(undefined, "Foo \u2661"),
|
data: await currentStateObj(undefined, "Foo \u2661"),
|
||||||
});
|
});
|
||||||
|
statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
Services.prefs.clearUserPref("browser.search.hiddenOneOffs");
|
Services.prefs.clearUserPref("browser.search.hiddenOneOffs");
|
||||||
msg = await waitForTestMsg(mm, "CurrentState");
|
msg = await statePromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "CurrentState",
|
type: "CurrentState",
|
||||||
data: await currentStateObj(),
|
data: await currentStateObj(),
|
||||||
|
|
@ -188,11 +208,11 @@ add_task(async function searchInBackgroundTab() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function badImage() {
|
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,
|
// 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
|
// 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.
|
// 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 engine = vals[0];
|
||||||
let finalCurrentStateMsg = vals[vals.length - 1];
|
let finalCurrentStateMsg = vals[vals.length - 1];
|
||||||
let expectedCurrentState = await currentStateObj();
|
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
|
// Removing the engine triggers a final CurrentState message. Wait for it so
|
||||||
// it doesn't trip up subsequent tests.
|
// it doesn't trip up subsequent tests.
|
||||||
|
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
await Services.search.removeEngine(engine);
|
await Services.search.removeEngine(engine);
|
||||||
await waitForTestMsg(mm, "CurrentState");
|
await statePromise.donePromise;
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(
|
add_task(
|
||||||
async function GetSuggestions_AddFormHistoryEntry_RemoveFormHistoryEntry() {
|
async function GetSuggestions_AddFormHistoryEntry_RemoveFormHistoryEntry() {
|
||||||
let { mm } = await addTab();
|
let { browser } = await addTab();
|
||||||
|
|
||||||
// Add the test engine that provides suggestions.
|
// 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 engine = vals[0];
|
||||||
|
|
||||||
let searchStr = "browser_ContentSearch.js-suggestions-";
|
let searchStr = "browser_ContentSearch.js-suggestions-";
|
||||||
|
|
||||||
// Add a form history suggestion and wait for Satchel to notify about it.
|
// Add a form history suggestion and wait for Satchel to notify about it.
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
sendEventToContent(browser, {
|
||||||
type: "AddFormHistoryEntry",
|
type: "AddFormHistoryEntry",
|
||||||
data: searchStr + "form",
|
data: searchStr + "form",
|
||||||
});
|
});
|
||||||
let deferred = PromiseUtils.defer();
|
await new Promise(resolve => {
|
||||||
Services.obs.addObserver(function onAdd(subj, topic, data) {
|
Services.obs.addObserver(function onAdd(subj, topic, data) {
|
||||||
if (data == "formhistory-add") {
|
if (data == "formhistory-add") {
|
||||||
Services.obs.removeObserver(onAdd, "satchel-storage-changed");
|
Services.obs.removeObserver(onAdd, "satchel-storage-changed");
|
||||||
executeSoon(() => deferred.resolve());
|
executeSoon(resolve);
|
||||||
}
|
}
|
||||||
}, "satchel-storage-changed");
|
}, "satchel-storage-changed");
|
||||||
await deferred.promise;
|
});
|
||||||
|
|
||||||
// Send GetSuggestions using the test engine. Its suggestions should appear
|
// Send GetSuggestions using the test engine. Its suggestions should appear
|
||||||
// in the remote suggestions in the Suggestions response below.
|
// in the remote suggestions in the Suggestions response below.
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
let suggestionsPromise = await waitForTestMsg(browser, "Suggestions");
|
||||||
|
sendEventToContent(browser, {
|
||||||
type: "GetSuggestions",
|
type: "GetSuggestions",
|
||||||
data: {
|
data: {
|
||||||
engineName: engine.name,
|
engineName: engine.name,
|
||||||
|
|
@ -251,7 +277,7 @@ add_task(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check the Suggestions response.
|
// Check the Suggestions response.
|
||||||
let msg = await waitForTestMsg(mm, "Suggestions");
|
let msg = await suggestionsPromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "Suggestions",
|
type: "Suggestions",
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -263,21 +289,23 @@ add_task(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete the form history suggestion and wait for Satchel to notify about it.
|
// Delete the form history suggestion and wait for Satchel to notify about it.
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
sendEventToContent(browser, {
|
||||||
type: "RemoveFormHistoryEntry",
|
type: "RemoveFormHistoryEntry",
|
||||||
data: searchStr + "form",
|
data: searchStr + "form",
|
||||||
});
|
});
|
||||||
deferred = PromiseUtils.defer();
|
|
||||||
|
await new Promise(resolve => {
|
||||||
Services.obs.addObserver(function onRemove(subj, topic, data) {
|
Services.obs.addObserver(function onRemove(subj, topic, data) {
|
||||||
if (data == "formhistory-remove") {
|
if (data == "formhistory-remove") {
|
||||||
Services.obs.removeObserver(onRemove, "satchel-storage-changed");
|
Services.obs.removeObserver(onRemove, "satchel-storage-changed");
|
||||||
executeSoon(() => deferred.resolve());
|
executeSoon(resolve);
|
||||||
}
|
}
|
||||||
}, "satchel-storage-changed");
|
}, "satchel-storage-changed");
|
||||||
await deferred.promise;
|
});
|
||||||
|
|
||||||
// Send GetSuggestions again.
|
// Send GetSuggestions again.
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
suggestionsPromise = await waitForTestMsg(browser, "Suggestions");
|
||||||
|
sendEventToContent(browser, {
|
||||||
type: "GetSuggestions",
|
type: "GetSuggestions",
|
||||||
data: {
|
data: {
|
||||||
engineName: engine.name,
|
engineName: engine.name,
|
||||||
|
|
@ -286,7 +314,7 @@ add_task(
|
||||||
});
|
});
|
||||||
|
|
||||||
// The formHistory suggestions in the Suggestions response should be empty.
|
// The formHistory suggestions in the Suggestions response should be empty.
|
||||||
msg = await waitForTestMsg(mm, "Suggestions");
|
msg = await suggestionsPromise.donePromise;
|
||||||
checkMsg(msg, {
|
checkMsg(msg, {
|
||||||
type: "Suggestions",
|
type: "Suggestions",
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -298,15 +326,15 @@ add_task(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finally, clean up by removing the test engine.
|
// Finally, clean up by removing the test engine.
|
||||||
|
let statePromise = await waitForTestMsg(browser, "CurrentState");
|
||||||
await Services.search.removeEngine(engine);
|
await Services.search.removeEngine(engine);
|
||||||
await waitForTestMsg(mm, "CurrentState");
|
await statePromise.donePromise;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
async function performSearch(browser, data, expectedURL) {
|
async function performSearch(browser, data, expectedURL) {
|
||||||
let mm = browser.messageManager;
|
|
||||||
let stoppedPromise = BrowserTestUtils.browserStopped(browser, expectedURL);
|
let stoppedPromise = BrowserTestUtils.browserStopped(browser, expectedURL);
|
||||||
mm.sendAsyncMessage(TEST_MSG, {
|
sendEventToContent(browser, {
|
||||||
type: "Search",
|
type: "Search",
|
||||||
data,
|
data,
|
||||||
expectedURL,
|
expectedURL,
|
||||||
|
|
@ -362,44 +390,69 @@ function checkArrayBuffers(actual, expected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMsg(actualMsg, expectedMsgData) {
|
function checkMsg(actualMsg, expectedMsgData) {
|
||||||
let actualMsgData = actualMsg.data;
|
SimpleTest.isDeeply(actualMsg, expectedMsgData, "Checking message");
|
||||||
SimpleTest.isDeeply(actualMsg.data, expectedMsgData, "Checking message");
|
|
||||||
|
|
||||||
// Engines contain ArrayBuffers which we have to compare byte by byte and
|
// Engines contain ArrayBuffers which we have to compare byte by byte and
|
||||||
// not as Objects (like SimpleTest.isDeeply does).
|
// not as Objects (like SimpleTest.isDeeply does).
|
||||||
checkArrayBuffers(actualMsgData, expectedMsgData);
|
checkArrayBuffers(actualMsg, expectedMsgData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForTestMsg(mm, type) {
|
async function waitForTestMsg(browser, type, count = 1) {
|
||||||
return new Promise(resolve => {
|
await SpecialPowers.spawn(
|
||||||
info("Waiting for " + TEST_MSG + " message " + type + "...");
|
browser,
|
||||||
mm.addMessageListener(TEST_MSG, function onMsg(msg) {
|
[SERVICE_EVENT_TYPE, type, count],
|
||||||
info("Received " + TEST_MSG + " message " + msg.data.type + "\n");
|
(childEvent, childType, childCount) => {
|
||||||
if (msg.data.type == type) {
|
content.eventDetails = [];
|
||||||
mm.removeMessageListener(TEST_MSG, onMsg);
|
function listener(event) {
|
||||||
resolve(msg);
|
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);
|
info("Waiting for engine to be added: " + basename);
|
||||||
|
|
||||||
// Wait for the search events triggered by adding the new engine.
|
// Wait for the search events triggered by adding the new engine.
|
||||||
// engine-added engine-loaded
|
// engine-added engine-loaded
|
||||||
let expectedSearchEvents = ["CurrentState", "CurrentState"];
|
let count = 2;
|
||||||
// engine-changed for each of the images
|
// engine-changed for each of the images
|
||||||
for (let i = 0; i < numImages; i++) {
|
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().
|
// Wait for addEngine().
|
||||||
let url = getRootDirectory(gTestPath) + basename;
|
let url = getRootDirectory(gTestPath) + basename;
|
||||||
return Promise.all(
|
let engine = await Services.search.addEngine(url, "", false);
|
||||||
[Services.search.addEngine(url, "", false)].concat(eventPromises)
|
let results = await statePromise.donePromise;
|
||||||
);
|
return [engine, ...results];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addTab() {
|
async function addTab() {
|
||||||
|
|
@ -409,10 +462,7 @@ async function addTab() {
|
||||||
);
|
);
|
||||||
registerCleanupFunction(() => gBrowser.removeTab(tab));
|
registerCleanupFunction(() => gBrowser.removeTab(tab));
|
||||||
|
|
||||||
let url = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
|
return { browser: tab.linkedBrowser };
|
||||||
let mm = tab.linkedBrowser.messageManager;
|
|
||||||
mm.loadFrameScript(url, false);
|
|
||||||
return { browser: tab.linkedBrowser, mm };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentStateObj = async function(isPrivateWindowValue, hiddenEngine = "") {
|
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",
|
"AddonManager",
|
||||||
"resource://gre/modules/AddonManager.jsm"
|
"resource://gre/modules/AddonManager.jsm"
|
||||||
);
|
);
|
||||||
ChromeUtils.defineModuleGetter(
|
|
||||||
this,
|
|
||||||
"ContentSearch",
|
|
||||||
"resource:///modules/ContentSearch.jsm"
|
|
||||||
);
|
|
||||||
|
|
||||||
const SIMPLETEST_OVERRIDES = [
|
const SIMPLETEST_OVERRIDES = [
|
||||||
"ok",
|
"ok",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue