summaryrefslogtreecommitdiffstats
path: root/mobile/android/chrome/content/FindHelper.js
blob: 037b182d64b767316392999e936a8b1e99c5a7a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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;
    }
  }
};