// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- // vim: set ts=2 sw=2 sts=2 et tw=80: */ // 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/. this.EXPORTED_SYMBOLS = ["RemoteFinder", "RemoteFinderListener"]; const { interfaces: Ci, classes: Cc, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Geometry.jsm"); XPCOMUtils.defineLazyGetter(this, "GetClipboardSearchString", () => Cu.import("resource://gre/modules/Finder.jsm", {}).GetClipboardSearchString ); XPCOMUtils.defineLazyGetter(this, "Rect", () => Cu.import("resource://gre/modules/Geometry.jsm", {}).Rect ); function RemoteFinder(browser) { this._listeners = new Set(); this._searchString = null; this.swapBrowser(browser); } RemoteFinder.prototype = { destroy() { this._browser.messageManager.sendAsyncMessage("Finder:Destroy"); if (this._messageManager) { this._messageManager.removeMessageListener("Finder:Result", this); this._messageManager.removeMessageListener("Finder:MatchesResult", this); this._messageManager.removeMessageListener("Finder:CurrentSelectionResult",this); this._messageManager.removeMessageListener("Finder:HighlightFinished",this); } this._listeners.clear(); this._browser = this._messageManager = null; }, swapBrowser: function(aBrowser) { if (this._messageManager) { this._messageManager.removeMessageListener("Finder:Result", this); this._messageManager.removeMessageListener("Finder:MatchesResult", this); this._messageManager.removeMessageListener("Finder:CurrentSelectionResult",this); this._messageManager.removeMessageListener("Finder:HighlightFinished",this); } else { aBrowser.messageManager.sendAsyncMessage("Finder:Initialize"); } this._browser = aBrowser; this._messageManager = this._browser.messageManager; this._messageManager.addMessageListener("Finder:Result", this); this._messageManager.addMessageListener("Finder:MatchesResult", this); this._messageManager.addMessageListener("Finder:CurrentSelectionResult", this); this._messageManager.addMessageListener("Finder:HighlightFinished", this); // Ideally listeners would have removed themselves but that doesn't happen // right now this._listeners.clear(); }, addResultListener: function (aListener) { this._listeners.add(aListener); }, removeResultListener: function (aListener) { this._listeners.delete(aListener); }, receiveMessage: function (aMessage) { // Only Finder:Result messages have the searchString field. let callback; let params; switch (aMessage.name) { case "Finder:Result": this._searchString = aMessage.data.searchString; // The rect stops being a Geometry.jsm:Rect over IPC. if (aMessage.data.rect) { aMessage.data.rect = Rect.fromRect(aMessage.data.rect); } callback = "onFindResult"; params = [ aMessage.data ]; break; case "Finder:MatchesResult": callback = "onMatchesCountResult"; params = [ aMessage.data ]; break; case "Finder:CurrentSelectionResult": callback = "onCurrentSelection"; params = [ aMessage.data.selection, aMessage.data.initial ]; break; case "Finder:HighlightFinished": callback = "onHighlightFinished"; params = [ aMessage.data ]; break; } for (let l of this._listeners) { // Don't let one callback throwing stop us calling the rest try { l[callback].apply(l, params); } catch (e) { if (!l[callback]) { Cu.reportError(`Missing ${callback} callback on RemoteFinderListener`); } Cu.reportError(e); } } }, get searchString() { return this._searchString; }, get clipboardSearchString() { return GetClipboardSearchString(this._browser.loadContext); }, setSearchStringToSelection() { this._browser.messageManager.sendAsyncMessage("Finder:SetSearchStringToSelection", {}); }, set caseSensitive(aSensitive) { this._browser.messageManager.sendAsyncMessage("Finder:CaseSensitive", { caseSensitive: aSensitive }); }, set entireWord(aEntireWord) { this._browser.messageManager.sendAsyncMessage("Finder:EntireWord", { entireWord: aEntireWord }); }, getInitialSelection: function() { this._browser.messageManager.sendAsyncMessage("Finder:GetInitialSelection", {}); }, fastFind: function (aSearchString, aLinksOnly, aDrawOutline) { this._browser.messageManager.sendAsyncMessage("Finder:FastFind", { searchString: aSearchString, linksOnly: aLinksOnly, drawOutline: aDrawOutline }); }, findAgain: function (aFindBackwards, aLinksOnly, aDrawOutline) { this._browser.messageManager.sendAsyncMessage("Finder:FindAgain", { findBackwards: aFindBackwards, linksOnly: aLinksOnly, drawOutline: aDrawOutline }); }, highlight: function (aHighlight, aWord, aLinksOnly) { this._browser.messageManager.sendAsyncMessage("Finder:Highlight", { highlight: aHighlight, linksOnly: aLinksOnly, word: aWord }); }, enableSelection: function () { this._browser.messageManager.sendAsyncMessage("Finder:EnableSelection"); }, removeSelection: function () { this._browser.messageManager.sendAsyncMessage("Finder:RemoveSelection"); }, focusContent: function () { // Allow Finder listeners to cancel focusing the content. for (let l of this._listeners) { try { if ("shouldFocusContent" in l && !l.shouldFocusContent()) return; } catch (ex) { Cu.reportError(ex); } } this._browser.focus(); this._browser.messageManager.sendAsyncMessage("Finder:FocusContent"); }, onFindbarClose: function () { this._browser.messageManager.sendAsyncMessage("Finder:FindbarClose"); }, onModalHighlightChange: function(aUseModalHighlight) { this._browser.messageManager.sendAsyncMessage("Finder:ModalHighlightChange", { useModalHighlight: aUseModalHighlight }); }, onHighlightAllChange: function(aHighlightAll) { this._browser.messageManager.sendAsyncMessage("Finder:HighlightAllChange", { highlightAll: aHighlightAll }); }, keyPress: function (aEvent) { this._browser.messageManager.sendAsyncMessage("Finder:KeyPress", { keyCode: aEvent.keyCode, ctrlKey: aEvent.ctrlKey, metaKey: aEvent.metaKey, altKey: aEvent.altKey, shiftKey: aEvent.shiftKey }); }, requestMatchesCount: function (aSearchString, aMatchLimit, aLinksOnly) { this._browser.messageManager.sendAsyncMessage("Finder:MatchesCount", { searchString: aSearchString, matchLimit: aMatchLimit, linksOnly: aLinksOnly }); } } function RemoteFinderListener(global) { let {Finder} = Cu.import("resource://gre/modules/Finder.jsm", {}); this._finder = new Finder(global.docShell); this._finder.addResultListener(this); this._global = global; for (let msg of this.MESSAGES) { global.addMessageListener(msg, this); } } RemoteFinderListener.prototype = { MESSAGES: [ "Finder:CaseSensitive", "Finder:Destroy", "Finder:EntireWord", "Finder:FastFind", "Finder:FindAgain", "Finder:SetSearchStringToSelection", "Finder:GetInitialSelection", "Finder:Highlight", "Finder:HighlightAllChange", "Finder:EnableSelection", "Finder:RemoveSelection", "Finder:FocusContent", "Finder:FindbarClose", "Finder:KeyPress", "Finder:MatchesCount", "Finder:ModalHighlightChange" ], onFindResult: function (aData) { this._global.sendAsyncMessage("Finder:Result", aData); }, // When the child receives messages with results of requestMatchesCount, // it passes them forward to the parent. onMatchesCountResult: function (aData) { this._global.sendAsyncMessage("Finder:MatchesResult", aData); }, onHighlightFinished: function(aData) { this._global.sendAsyncMessage("Finder:HighlightFinished", aData); }, receiveMessage: function (aMessage) { let data = aMessage.data; switch (aMessage.name) { case "Finder:Destroy": this._finder.destroy(); break; case "Finder:CaseSensitive": this._finder.caseSensitive = data.caseSensitive; break; case "Finder:EntireWord": this._finder.entireWord = data.entireWord; break; case "Finder:SetSearchStringToSelection": { let selection = this._finder.setSearchStringToSelection(); this._global.sendAsyncMessage("Finder:CurrentSelectionResult", { selection: selection, initial: false }); break; } case "Finder:GetInitialSelection": { let selection = this._finder.getActiveSelectionText(); this._global.sendAsyncMessage("Finder:CurrentSelectionResult", { selection: selection, initial: true }); break; } case "Finder:FastFind": this._finder.fastFind(data.searchString, data.linksOnly, data.drawOutline); break; case "Finder:FindAgain": this._finder.findAgain(data.findBackwards, data.linksOnly, data.drawOutline); break; case "Finder:Highlight": this._finder.highlight(data.highlight, data.word, data.linksOnly); break; case "Finder:HighlightAllChange": this._finder.onHighlightAllChange(data.highlightAll); break; case "Finder:EnableSelection": this._finder.enableSelection(); break; case "Finder:RemoveSelection": this._finder.removeSelection(); break; case "Finder:FocusContent": this._finder.focusContent(); break; case "Finder:FindbarClose": this._finder.onFindbarClose(); break; case "Finder:KeyPress": this._finder.keyPress(data); break; case "Finder:MatchesCount": this._finder.requestMatchesCount(data.searchString, data.matchLimit, data.linksOnly); break; case "Finder:ModalHighlightChange": this._finder.onModalHighlightChange(data.useModalHighlight); break; } } };