/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; var FindHelper = { _finder: null, _targetTab: null, _initialViewport: null, _viewportChanged: false, _result: null, // Start of nsIObserver implementation. observe: function(aMessage, aTopic, aData) { switch(aTopic) { case "FindInPage:Opened": { this._findOpened(); break; } case "Tab:Selected": { // Allow for page switching. this._uninit(); break; } case "FindInPage:Closed": this._uninit(); this._findClosed(); break; } }, /** * When the FindInPageBar opens/ becomes visible, it's time to: * 1. Add listeners for other message types sent from the FindInPageBar * 2. initialize the Finder instance, if necessary. */ _findOpened: function() { Messaging.addListener(data => this.doFind(data), "FindInPage:Find"); Messaging.addListener(data => this.findAgain(data, false), "FindInPage:Next"); Messaging.addListener(data => this.findAgain(data, true), "FindInPage:Prev"); // Initialize the finder component for the current page by performing a fake find. this._init(); this._finder.requestMatchesCount(""); }, /** * Fetch the Finder instance from the active tabs' browser and start tracking * the active viewport. */ _init: function() { // If there's no find in progress, start one. if (this._finder) { return; } this._targetTab = BrowserApp.selectedTab; try { this._finder = this._targetTab.browser.finder; } catch (e) { throw new Error("FindHelper: " + e + "\n" + "JS stack: \n" + (e.stack || Components.stack.formattedStack)); } this._finder.addResultListener(this); this._initialViewport = JSON.stringify(this._targetTab.getViewport()); this._viewportChanged = false; }, /** * Detach from the Finder instance (so stop listening for messages) and stop * tracking the active viewport. */ _uninit: function() { // If there's no find in progress, there's nothing to clean up. if (!this._finder) { return; } this._finder.removeSelection(); this._finder.removeResultListener(this); this._finder = null; this._targetTab = null; this._initialViewport = null; this._viewportChanged = false; }, /** * When the FindInPageBar closes, it's time to stop listening for its messages. */ _findClosed: function() { Messaging.removeListener("FindInPage:Find"); Messaging.removeListener("FindInPage:Next"); Messaging.removeListener("FindInPage:Prev"); }, /** * Start an asynchronous find-in-page operation, using the current Finder * instance and request to count the amount of matches. * If no Finder instance is currently active, we'll lazily initialize it here. * * @param {String} searchString Word to search for in the current document * @return {Object} Echo of the current find action */ doFind: function(searchString) { if (!this._finder) { this._init(); } this._finder.fastFind(searchString, false); return { searchString, findBackwards: false }; }, /** * Restart the same find-in-page operation as before via `doFind()`. If we * haven't called `doFind()`, we simply kick off a regular find. * * @param {String} searchString Word to search for in the current document * @param {Boolean} findBackwards Direction to search in * @return {Object} Echo of the current find action */ findAgain: function(searchString, findBackwards) { // This always happens if the user taps next/previous after re-opening the // search bar, and not only forces _init() but also an initial fastFind(STRING) // before any findAgain(DIRECTION). if (!this._finder) { return this.doFind(searchString); } this._finder.findAgain(findBackwards, false, false); return { searchString, findBackwards }; }, // Start of Finder.jsm listener implementation. /** * Pass along the count results to FindInPageBar for display. The result that * is sent to the FindInPageBar is augmented with the current find-in-page count * limit. * * @param {Object} result Result coming from the Finder instance that contains * the following properties: * - {Number} total The total amount of matches found * - {Number} current The index of current found range * in the document */ onMatchesCountResult: function(result) { this._result = result; Messaging.sendRequest(Object.assign({ type: "FindInPage:MatchesCountResult" }, this._result)); }, /** * When a find-in-page action finishes, this method is invoked. This is mainly * used at the moment to detect if the current viewport has changed, which might * be indicated by not finding a string in the current page. * * @param {Object} aData A dictionary, representing the find result, which * contains the following properties: * - {String} searchString Word that was searched for * in the current document * - {Number} result One of the following * Ci.nsITypeAheadFind.* result * indicators: FIND_FOUND, * FIND_NOTFOUND, FIND_WRAPPED, * FIND_PENDING * - {Boolean} findBackwards Whether the search direction * was backwards * - {Boolean} findAgain Whether the previous search * was repeated * - {Boolean} drawOutline Whether we may (re-)draw the * outline of a hyperlink * - {Boolean} linksOnly Whether links-only mode was * active */ onFindResult: function(aData) { if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) { if (this._viewportChanged) { if (this._targetTab != BrowserApp.selectedTab) { // this should never happen Cu.reportError("Warning: selected tab changed during find!"); // fall through and restore viewport on the initial tab anyway } this._targetTab.sendViewportUpdate(); } } else { // Disabled until bug 1014113 is fixed // ZoomHelper.zoomToRect(aData.rect); this._viewportChanged = true; } } };