summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/find_request_manager.h
blob: 8c85cbbcc39e93ebb751bd6a30aadd09b557a7d1 (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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_
#define CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_

#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "base/containers/queue.h"
#include "content/common/content_export.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/stop_find_action.h"
#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"

namespace content {

class FindInPageClient;
class RenderFrameHost;
class RenderFrameHostImpl;
class WebContentsImpl;

// FindRequestManager manages all of the find-in-page requests/replies
// initiated/received through a WebContents. It coordinates searching across
// multiple (potentially out-of-process) frames, handles the aggregation of find
// results from each frame, and facilitates active match traversal. It is
// instantiated once per top-level WebContents, and is owned by that
// WebContents.
class CONTENT_EXPORT FindRequestManager {
 public:
  explicit FindRequestManager(WebContentsImpl* web_contents);
  ~FindRequestManager();

  // Initiates a find operation for |search_text| with the options specified in
  // |options|. |request_id| uniquely identifies the find request.
  void Find(int request_id,
            const base::string16& search_text,
            blink::mojom::FindOptionsPtr options);

  // Stops the active find session and clears the general highlighting of the
  // matches. |action| determines whether the last active match (if any) will be
  // activated, cleared, or remain highlighted.
  void StopFinding(StopFindAction action);

  // Handles the final update from |rfh| for the find request with id
  // |request_id|.
  void HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh, int request_id);

  // The number of matches on |rfh| has changed from |old_count| to |new_count|.
  // This method updates the total number of matches and also updates
  // |active_match_ordinal_| accordingly.
  void UpdatedFrameNumberOfMatches(RenderFrameHostImpl* rfh,
                                   unsigned int old_count,
                                   unsigned int new_count);

  bool ShouldIgnoreReply(RenderFrameHostImpl* rfh, int request_id);

  void SetActiveMatchRect(const gfx::Rect& active_match_rect);

  void SetActiveMatchOrdinal(RenderFrameHostImpl* rfh,
                             int request_id,
                             int active_match_ordinal);

  // Sends the find results (as they currently are) to the WebContents.
  // |final_update| is true if we have received all of the updates from
  // every frame for this request.
  void NotifyFindReply(int request_id, bool final_update);

  // Removes a frame from the set of frames being searched. This should be
  // called whenever a frame is discovered to no longer exist.
  void RemoveFrame(RenderFrameHost* rfh);

  // Tells active frame to clear the active match highlighting.
  void ClearActiveFindMatch();

#if defined(OS_ANDROID)
  // Selects and zooms to the find result nearest to the point (x, y), defined
  // in find-in-page coordinates.
  void ActivateNearestFindResult(float x, float y);

  // Called when a reply is received from a frame in response to the
  // GetNearestFindResult mojo call.
  void OnGetNearestFindResultReply(RenderFrameHostImpl* rfh,
                                   int request_id,
                                   float distance);

  // Requests the rects of the current find matches from the renderer process.
  void RequestFindMatchRects(int current_version);

  // Called when a reply is received from a frame in response to a request for
  // find match rects.
  void OnFindMatchRectsReply(RenderFrameHost* rfh,
                             int version,
                             const std::vector<gfx::RectF>& rects,
                             const gfx::RectF& active_rect);
#endif

  const std::unordered_set<RenderFrameHost*>
  render_frame_hosts_pending_initial_reply_for_testing() const {
    return pending_initial_replies_;
  }

  gfx::Rect GetSelectionRectForTesting() { return selection_rect_; }

 private:
  // An invalid ID. This value is invalid for any render process ID, render
  // frame ID, find request ID, or find match rects version number.
  static const int kInvalidId;

  class FrameObserver;

  // The request data for a single find request.
  struct FindRequest {
    // The find request ID that uniquely identifies this find request.
    int id = kInvalidId;

    // The text that is being searched for in this find request.
    base::string16 search_text;

    // The set of find options in effect for this find request.
    blink::mojom::FindOptionsPtr options;

    FindRequest();
    FindRequest(int id,
                const base::string16& search_text,
                blink::mojom::FindOptionsPtr options);
    FindRequest(const FindRequest& request);
    ~FindRequest();

    FindRequest& operator=(const FindRequest& request);
  };

  // Resets all of the per-session state for a new find-in-page session.
  void Reset(const FindRequest& initial_request);

  // Called internally as find requests come up in the queue.
  void FindInternal(const FindRequest& request);

  // Called when an informative response (a response with enough information to
  // be able to route subsequent find requests) comes in for the find request
  // with ID |request_id|. Advances the |find_request_queue_| if appropriate.
  void AdvanceQueue(int request_id);

  // Sends find request |request| through mojo to the RenderFrame associated
  // with |rfh|.
  void SendFindRequest(const FindRequest& request, RenderFrameHost* rfh);

  // Returns the initial frame in search order. This will be either the first
  // frame, if searching forward, or the last frame, if searching backward.
  RenderFrameHost* GetInitialFrame(bool forward) const;

  // Traverses the frame tree to find and return the next RenderFrameHost after
  // |from_rfh| in search order. |forward| indicates whether the frame tree
  // should be traversed forward (if true) or backward (if false). If
  // |matches_only| is set, then the frame tree will be traversed until the
  // first frame is found for which matches have been found. If |wrap| is set,
  // then the traversal can wrap around past the last frame to the first one (or
  // vice-versa, if |forward| == false). If no frame can be found under these
  // conditions, nullptr is returned.
  RenderFrameHost* Traverse(RenderFrameHost* from_rfh,
                            bool forward,
                            bool matches_only,
                            bool wrap) const;

  // Adds a frame to the set of frames that are being searched. The new frame
  // will automatically be searched when added, using the same options (stored
  // in |current_request_.options|). |force| should be set to true when a
  // dynamic content change is suspected, which will treat the frame as a newly
  // added frame even if it has already been searched. This will force a
  // re-search of the frame.
  void AddFrame(RenderFrameHost* rfh, bool force);

  // Returns whether |rfh| is in the set of frames being searched in the current
  // find session.
  bool CheckFrame(RenderFrameHost* rfh) const;

  // Computes and updates |active_match_ordinal_| based on |active_frame_| and
  // |relative_active_match_ordinal_|.
  void UpdateActiveMatchOrdinal();

  // Called when all pending find replies have been received for the find
  // request with ID |request_id|. The final update was received from |rfh|.
  //
  // Note that this is the final update for this particular find request, but
  // not necessarily for all issued requests. If there are still pending replies
  // expected for a previous find request, then the outgoing find reply issued
  // from this function will not be marked final.
  void FinalUpdateReceived(int request_id, RenderFrameHost* rfh);

#if defined(OS_ANDROID)
  // Called when a nearest find result reply is no longer pending for a frame.
  void RemoveNearestFindResultPendingReply(RenderFrameHost* rfh);

  // Called when a find match rects reply is no longer pending for a frame.
  void RemoveFindMatchRectsPendingReply(RenderFrameHost* rfh);

  // State related to ActivateNearestFindResult requests.
  struct ActivateNearestFindResultState {
    // An ID to uniquely identify the current nearest find result request and
    // its replies.
    int current_request_id = kInvalidId;

    // The value of the requested point, in find-in-page coordinates.
    gfx::PointF point = gfx::PointF(0.0f, 0.0f);

    float nearest_distance = FLT_MAX;

    // The frame containing the nearest result found so far.
    RenderFrameHostImpl* nearest_frame = nullptr;

    // Nearest find result replies are still pending for these frames.
    std::unordered_set<RenderFrameHost*> pending_replies;

    ActivateNearestFindResultState();
    ActivateNearestFindResultState(float x, float y);
    ~ActivateNearestFindResultState();

    static int GetNextID() {
      static int next_id = 0;
      return next_id++;
    }
  } activate_;

  // Data for find match rects in a single frame.
  struct FrameRects {
    // The rects contained in a single frame.
    std::vector<gfx::RectF> rects;

    // The version number for these rects, as reported by their containing
    // frame. This version is incremented independently in each frame.
    int version = kInvalidId;

    FrameRects();
    FrameRects(const std::vector<gfx::RectF>& rects, int version);
    ~FrameRects();
  };

  // State related to FindMatchRects requests.
  struct FindMatchRectsState {
    // The latest find match rects version known by the requester. This will be
    // compared to |known_version_| after polling frames for updates to their
    // match rects, in order to determine if the requester already has the
    // latest version of rects or not.
    int request_version = kInvalidId;

    // The current overall find match rects version known by
    // FindRequestManager. This version should be incremented whenever
    // |frame_rects| is updated.
    int known_version = 0;

    // A map from each frame to its find match rects.
    std::unordered_map<RenderFrameHost*, FrameRects> frame_rects;

    // The active find match rect.
    gfx::RectF active_rect;

    // Find match rects replies are still pending for these frames.
    std::unordered_set<RenderFrameHost*> pending_replies;

    FindMatchRectsState();
    ~FindMatchRectsState();
  } match_rects_;
#endif

  // The WebContents that owns this FindRequestManager. This also defines the
  // scope of all find sessions. Only frames in |contents_| and any inner
  // WebContentses within it will be searched.
  WebContentsImpl* const contents_;

  // The request ID of the initial find request in the current find-in-page
  // session, which uniquely identifies this session. Request IDs are included
  // in all find-related IPCs, which allows reply IPCs containing results from
  // previous sessions (with |request_id| < |current_session_id_|) to be easily
  // identified and ignored.
  int current_session_id_ = kInvalidId;

  // The current find request.
  FindRequest current_request_;

  // The set of frames that are still expected to reply to a pending initial
  // find request. Frames are removed from |pending_initial_replies_| when their
  // reply to the initial find request is received with |final_update| set to
  // true.
  std::unordered_set<RenderFrameHost*> pending_initial_replies_;

  // The frame (if any) that is still expected to reply to the last pending
  // "find next" request.
  RenderFrameHost* pending_find_next_reply_ = nullptr;

  // Indicates whether an update to the active match ordinal is expected. Once
  // set, |pending_active_match_ordinal_| will not reset until an update to the
  // active match ordinal is received in response to the find request with ID
  // |current_request_.id| (the latest request).
  bool pending_active_match_ordinal_ = false;

  // The FindInPageClient associated with each frame. There will necessarily be
  // entries in this map for every frame that is being (or has been) searched in
  // the current find session, and no other frames.
  std::unordered_map<RenderFrameHost*, std::unique_ptr<FindInPageClient>>
      find_in_page_clients_;

  // The total number of matches found in the current find-in-page session. This
  // should always be equal to the sum of all the entries in
  // |matches_per_frame_|.
  int number_of_matches_ = 0;

  // The frame containing the active match, if one exists, or nullptr otherwise.
  RenderFrameHostImpl* active_frame_ = nullptr;

  // The active match ordinal relative to the matches found in its own frame.
  int relative_active_match_ordinal_ = 0;

  // The overall active match ordinal for the current find-in-page session.
  int active_match_ordinal_ = 0;

  // The rectangle around the active match, in screen coordinates.
  gfx::Rect selection_rect_;

  // Find requests are queued here when previous requests need to be handled
  // before these ones can be properly routed.
  base::queue<FindRequest> find_request_queue_;

  // Keeps track of the find request ID of the last find reply reported via
  // NotifyFindReply().
  int last_reported_id_ = kInvalidId;

  // WebContentsObservers to observe frame changes in |contents_| and its inner
  // WebContentses.
  std::vector<std::unique_ptr<FrameObserver>> frame_observers_;

  DISALLOW_COPY_AND_ASSIGN(FindRequestManager);
};

}  // namespace content

#endif  // CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_