diff options
Diffstat (limited to 'mobile/android/chrome/content/FindHelper.js')
-rw-r--r-- | mobile/android/chrome/content/FindHelper.js | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/mobile/android/chrome/content/FindHelper.js b/mobile/android/chrome/content/FindHelper.js new file mode 100644 index 0000000000..037b182d64 --- /dev/null +++ b/mobile/android/chrome/content/FindHelper.js @@ -0,0 +1,197 @@ +/* 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; + } + } +}; |