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,13 +111,17 @@ var ContentSearch = { | ||||||
|   _currentSuggestion: null, |   _currentSuggestion: null, | ||||||
| 
 | 
 | ||||||
|   init() { |   init() { | ||||||
|     Services.obs.addObserver(this, "browser-search-engine-modified"); |     if (!this.initialized) { | ||||||
|     Services.obs.addObserver(this, "browser-search-service"); |       Services.obs.addObserver(this, "browser-search-engine-modified"); | ||||||
|     Services.obs.addObserver(this, "shutdown-leaks-before-check"); |       Services.obs.addObserver(this, "browser-search-service"); | ||||||
|     Services.prefs.addObserver("browser.search.hiddenOneOffs", this); |       Services.obs.addObserver(this, "shutdown-leaks-before-check"); | ||||||
|     this._stringBundle = Services.strings.createBundle( |       Services.prefs.addObserver("browser.search.hiddenOneOffs", this); | ||||||
|       "chrome://global/locale/autocomplete.properties" |       this._stringBundle = Services.strings.createBundle( | ||||||
|     ); |         "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(); | 
 | ||||||
|     Services.obs.addObserver(function onRemove(subj, topic, data) { |     await new Promise(resolve => { | ||||||
|       if (data == "formhistory-remove") { |       Services.obs.addObserver(function onRemove(subj, topic, data) { | ||||||
|         Services.obs.removeObserver(onRemove, "satchel-storage-changed"); |         if (data == "formhistory-remove") { | ||||||
|         executeSoon(() => deferred.resolve()); |           Services.obs.removeObserver(onRemove, "satchel-storage-changed"); | ||||||
|       } |           executeSoon(resolve); | ||||||
|     }, "satchel-storage-changed"); |         } | ||||||
|     await deferred.promise; |       }, "satchel-storage-changed"); | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     // 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
	
	 Neil Deakin
						Neil Deakin