blob: 1b6f53b4394fcc2719e56abfe650e0992b972883 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2016 The Chromium Authors
paulmeyerc0b762b2016-04-13 11:55:172// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_
6#define CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_
7
Matt Starked70eae2025-07-02 04:40:078#include <cfloat>
Lei Zhang4ea8c202019-10-16 02:27:599#include <memory>
paulmeyerc8cb7cb2016-06-07 01:14:1910#include <unordered_map>
11#include <unordered_set>
paulmeyerc0b762b2016-04-13 11:55:1712#include <vector>
13
Joey Arharcac45bf2022-01-07 21:59:3114#include "base/cancelable_callback.h"
Brett Wilsoncc8623d2017-09-12 03:28:1015#include "base/containers/queue.h"
Daniel Cheng982f2b22022-08-25 23:46:1616#include "base/functional/function_ref.h"
Keishi Hattori0e45c022021-11-27 09:25:5217#include "base/memory/raw_ptr.h"
Gabriel Charetted87f10f2022-03-31 00:44:2218#include "base/time/time.h"
Christian Dullweber5e66727b2021-07-02 13:23:1519#include "build/build_config.h"
paulmeyerc8cb7cb2016-06-07 01:14:1920#include "content/public/browser/web_contents_observer.h"
paulmeyerc0b762b2016-04-13 11:55:1721#include "content/public/common/stop_find_action.h"
Rakina Zata Amni3f77dff2018-09-08 16:19:4322#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
paulmeyerc0b762b2016-04-13 11:55:1723#include "ui/gfx/geometry/rect.h"
24#include "ui/gfx/geometry/rect_f.h"
25
26namespace content {
27
Rakina Zata Amni724f3efe2018-07-23 03:19:4628class FindInPageClient;
paulmeyerc0b762b2016-04-13 11:55:1729class RenderFrameHost;
Rakina Zata Amni174ef712018-05-14 12:45:2030class RenderFrameHostImpl;
paulmeyerc0b762b2016-04-13 11:55:1731class WebContentsImpl;
32
33// FindRequestManager manages all of the find-in-page requests/replies
34// initiated/received through a WebContents. It coordinates searching across
35// multiple (potentially out-of-process) frames, handles the aggregation of find
36// results from each frame, and facilitates active match traversal. It is
paulmeyerfeafc2d2017-04-25 21:46:4037// instantiated once per top-level WebContents, and is owned by that
38// WebContents.
Lei Zhanged9be3a2021-11-17 22:01:1839class FindRequestManager {
paulmeyerc0b762b2016-04-13 11:55:1740 public:
41 explicit FindRequestManager(WebContentsImpl* web_contents);
Peter Boström828b9022021-09-21 02:28:4342
43 FindRequestManager(const FindRequestManager&) = delete;
44 FindRequestManager& operator=(const FindRequestManager&) = delete;
45
paulmeyerfeafc2d2017-04-25 21:46:4046 ~FindRequestManager();
paulmeyerc0b762b2016-04-13 11:55:1747
48 // Initiates a find operation for |search_text| with the options specified in
49 // |options|. |request_id| uniquely identifies the find request.
50 void Find(int request_id,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:5851 const std::u16string& search_text,
Joey Arharcac45bf2022-01-07 21:59:3152 blink::mojom::FindOptionsPtr options,
53 bool skip_delay = false);
paulmeyerc0b762b2016-04-13 11:55:1754
55 // Stops the active find session and clears the general highlighting of the
56 // matches. |action| determines whether the last active match (if any) will be
57 // activated, cleared, or remain highlighted.
58 void StopFinding(StopFindAction action);
59
Rakina Zata Amni724f3efe2018-07-23 03:19:4660 // Handles the final update from |rfh| for the find request with id
61 // |request_id|.
62 void HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh, int request_id);
63
64 // The number of matches on |rfh| has changed from |old_count| to |new_count|.
65 // This method updates the total number of matches and also updates
66 // |active_match_ordinal_| accordingly.
67 void UpdatedFrameNumberOfMatches(RenderFrameHostImpl* rfh,
68 unsigned int old_count,
69 unsigned int new_count);
70
Rakina Zata Amni2ac57502018-08-16 01:44:4571 bool ShouldIgnoreReply(RenderFrameHostImpl* rfh, int request_id);
72
Rakina Zata Amni724f3efe2018-07-23 03:19:4673 void SetActiveMatchRect(const gfx::Rect& active_match_rect);
74
75 void SetActiveMatchOrdinal(RenderFrameHostImpl* rfh,
76 int request_id,
77 int active_match_ordinal);
78
79 // Sends the find results (as they currently are) to the WebContents.
80 // |final_update| is true if we have received all of the updates from
81 // every frame for this request.
82 void NotifyFindReply(int request_id, bool final_update);
Rakina Zata Amni57bec362018-05-22 06:05:1083
paulmeyerc8cb7cb2016-06-07 01:14:1984 // Removes a frame from the set of frames being searched. This should be
85 // called whenever a frame is discovered to no longer exist.
86 void RemoveFrame(RenderFrameHost* rfh);
87
Rakina Zata Amniacf40492018-05-08 22:59:3388 // Tells active frame to clear the active match highlighting.
89 void ClearActiveFindMatch();
90
Joey Arhar0574fb752022-03-29 00:45:1691 // Runs the delayed find task if present. Returns true if there was a task
92 // which got run. Returns false if there was no delayed task.
93 bool CONTENT_EXPORT RunDelayedFindTaskForTesting();
94
Xiaohan Wang1ecfd002022-01-19 22:33:1095#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:1996 // Selects and zooms to the find result nearest to the point (x, y), defined
97 // in find-in-page coordinates.
paulmeyerc0b762b2016-04-13 11:55:1798 void ActivateNearestFindResult(float x, float y);
99
paulmeyerc8cb7cb2016-06-07 01:14:19100 // Called when a reply is received from a frame in response to the
Rakina Zata Amnida997262018-05-14 15:59:36101 // GetNearestFindResult mojo call.
Rakina Zata Amni57bec362018-05-22 06:05:10102 void OnGetNearestFindResultReply(RenderFrameHostImpl* rfh,
Rakina Zata Amnida997262018-05-14 15:59:36103 int request_id,
paulmeyerc8cb7cb2016-06-07 01:14:19104 float distance);
105
paulmeyerc0b762b2016-04-13 11:55:17106 // Requests the rects of the current find matches from the renderer process.
107 void RequestFindMatchRects(int current_version);
108
paulmeyerc8cb7cb2016-06-07 01:14:19109 // Called when a reply is received from a frame in response to a request for
110 // find match rects.
paulmeyerc0b762b2016-04-13 11:55:17111 void OnFindMatchRectsReply(RenderFrameHost* rfh,
112 int version,
113 const std::vector<gfx::RectF>& rects,
114 const gfx::RectF& active_rect);
115#endif
116
Ali Hijazia56154d2024-02-26 10:21:17117 const std::unordered_set<raw_ptr<RenderFrameHost, CtnExperimental>>
Ehsan Karamad6beb2ea2018-11-25 18:15:13118 render_frame_hosts_pending_initial_reply_for_testing() const {
119 return pending_initial_replies_;
120 }
121
W. James MacLean92a45192018-12-20 20:09:55122 gfx::Rect GetSelectionRectForTesting() { return selection_rect_; }
123
Miyoung Shinbf4c40c2021-10-21 11:00:23124 using CreateFindInPageClientFunction = std::unique_ptr<FindInPageClient> (*)(
125 FindRequestManager* find_request_manager,
126 RenderFrameHostImpl* rfh);
127 void SetCreateFindInPageClientFunctionForTesting(
128 CreateFindInPageClientFunction create_func) {
129 create_find_in_page_client_for_testing_ = create_func;
130 }
131
paulmeyerc0b762b2016-04-13 11:55:17132 private:
Gyuyoung Kima855ff5d2021-11-19 07:24:35133 friend class FindRequestManagerFencedFrameTest;
134
paulmeyerc0b762b2016-04-13 11:55:17135 // An invalid ID. This value is invalid for any render process ID, render
paulmeyerc8cb7cb2016-06-07 01:14:19136 // frame ID, find request ID, or find match rects version number.
paulmeyerc0b762b2016-04-13 11:55:17137 static const int kInvalidId;
138
paulmeyerfeafc2d2017-04-25 21:46:40139 class FrameObserver;
140
paulmeyerc0b762b2016-04-13 11:55:17141 // The request data for a single find request.
142 struct FindRequest {
143 // The find request ID that uniquely identifies this find request.
144 int id = kInvalidId;
145
146 // The text that is being searched for in this find request.
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58147 std::u16string search_text;
paulmeyerc0b762b2016-04-13 11:55:17148
149 // The set of find options in effect for this find request.
Rakina Zata Amni3f77dff2018-09-08 16:19:43150 blink::mojom::FindOptionsPtr options;
paulmeyerc0b762b2016-04-13 11:55:17151
Rakina Zata Amni3f77dff2018-09-08 16:19:43152 FindRequest();
paulmeyerc0b762b2016-04-13 11:55:17153 FindRequest(int id,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58154 const std::u16string& search_text,
Rakina Zata Amni3f77dff2018-09-08 16:19:43155 blink::mojom::FindOptionsPtr options);
156 FindRequest(const FindRequest& request);
157 ~FindRequest();
158
159 FindRequest& operator=(const FindRequest& request);
paulmeyerc0b762b2016-04-13 11:55:17160 };
161
paulmeyerc8cb7cb2016-06-07 01:14:19162 // Resets all of the per-session state for a new find-in-page session.
163 void Reset(const FindRequest& initial_request);
164
165 // Called internally as find requests come up in the queue.
166 void FindInternal(const FindRequest& request);
167
168 // Called when an informative response (a response with enough information to
169 // be able to route subsequent find requests) comes in for the find request
170 // with ID |request_id|. Advances the |find_request_queue_| if appropriate.
171 void AdvanceQueue(int request_id);
172
Rakina Zata Amni2ac57502018-08-16 01:44:45173 // Sends find request |request| through mojo to the RenderFrame associated
174 // with |rfh|.
175 void SendFindRequest(const FindRequest& request, RenderFrameHost* rfh);
paulmeyerc0b762b2016-04-13 11:55:17176
paulmeyerc8cb7cb2016-06-07 01:14:19177 // Returns the initial frame in search order. This will be either the first
178 // frame, if searching forward, or the last frame, if searching backward.
179 RenderFrameHost* GetInitialFrame(bool forward) const;
180
181 // Traverses the frame tree to find and return the next RenderFrameHost after
182 // |from_rfh| in search order. |forward| indicates whether the frame tree
183 // should be traversed forward (if true) or backward (if false). If
184 // |matches_only| is set, then the frame tree will be traversed until the
185 // first frame is found for which matches have been found. If |wrap| is set,
186 // then the traversal can wrap around past the last frame to the first one (or
187 // vice-versa, if |forward| == false). If no frame can be found under these
188 // conditions, nullptr is returned.
189 RenderFrameHost* Traverse(RenderFrameHost* from_rfh,
190 bool forward,
191 bool matches_only,
192 bool wrap) const;
193
194 // Adds a frame to the set of frames that are being searched. The new frame
195 // will automatically be searched when added, using the same options (stored
paulmeyer3ac612d2016-09-30 19:21:06196 // in |current_request_.options|). |force| should be set to true when a
197 // dynamic content change is suspected, which will treat the frame as a newly
198 // added frame even if it has already been searched. This will force a
199 // re-search of the frame.
200 void AddFrame(RenderFrameHost* rfh, bool force);
paulmeyerc8cb7cb2016-06-07 01:14:19201
202 // Returns whether |rfh| is in the set of frames being searched in the current
203 // find session.
Gyuyoung Kima855ff5d2021-11-19 07:24:35204 CONTENT_EXPORT bool CheckFrame(RenderFrameHost* rfh) const;
paulmeyerc8cb7cb2016-06-07 01:14:19205
206 // Computes and updates |active_match_ordinal_| based on |active_frame_| and
207 // |relative_active_match_ordinal_|.
208 void UpdateActiveMatchOrdinal();
209
210 // Called when all pending find replies have been received for the find
211 // request with ID |request_id|. The final update was received from |rfh|.
paulmeyerbbaacbe2016-08-30 18:04:13212 //
213 // Note that this is the final update for this particular find request, but
214 // not necessarily for all issued requests. If there are still pending replies
215 // expected for a previous find request, then the outgoing find reply issued
216 // from this function will not be marked final.
217 void FinalUpdateReceived(int request_id, RenderFrameHost* rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19218
Miyoung Shinbf4c40c2021-10-21 11:00:23219 std::unique_ptr<FindInPageClient> CreateFindInPageClient(
220 RenderFrameHostImpl* rfh);
221
Miyoung Shinbf4c40c2021-10-21 11:00:23222 // Traverses all RenderFrameHosts added for find-in-page and invokes the
223 // callback if the each RenderFrameHost is alive and active.
Daniel Cheng982f2b22022-08-25 23:46:16224 void ForEachAddedFindInPageRenderFrameHost(
225 base::FunctionRef<void(RenderFrameHostImpl*)> func_ref);
Miyoung Shinbf4c40c2021-10-21 11:00:23226
Joey Arharcac45bf2022-01-07 21:59:31227 void EmitFindRequest(int request_id,
228 const std::u16string& search_text,
229 blink::mojom::FindOptionsPtr options);
230
Xiaohan Wang1ecfd002022-01-19 22:33:10231#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19232 // Called when a nearest find result reply is no longer pending for a frame.
233 void RemoveNearestFindResultPendingReply(RenderFrameHost* rfh);
234
235 // Called when a find match rects reply is no longer pending for a frame.
236 void RemoveFindMatchRectsPendingReply(RenderFrameHost* rfh);
237
238 // State related to ActivateNearestFindResult requests.
239 struct ActivateNearestFindResultState {
240 // An ID to uniquely identify the current nearest find result request and
241 // its replies.
242 int current_request_id = kInvalidId;
243
Rakina Zata Amni57bec362018-05-22 06:05:10244 // The value of the requested point, in find-in-page coordinates.
245 gfx::PointF point = gfx::PointF(0.0f, 0.0f);
paulmeyerc8cb7cb2016-06-07 01:14:19246
paulmeyerc8cb7cb2016-06-07 01:14:19247 float nearest_distance = FLT_MAX;
248
249 // The frame containing the nearest result found so far.
Keishi Hattori0e45c022021-11-27 09:25:52250 raw_ptr<RenderFrameHostImpl> nearest_frame = nullptr;
paulmeyerc8cb7cb2016-06-07 01:14:19251
252 // Nearest find result replies are still pending for these frames.
Ali Hijazia56154d2024-02-26 10:21:17253 std::unordered_set<raw_ptr<RenderFrameHost, CtnExperimental>>
254 pending_replies;
paulmeyerc8cb7cb2016-06-07 01:14:19255
256 ActivateNearestFindResultState();
257 ActivateNearestFindResultState(float x, float y);
258 ~ActivateNearestFindResultState();
259
Devon Loehr11aff822025-05-30 16:25:28260 static int GetNextID();
261
paulmeyerc8cb7cb2016-06-07 01:14:19262 } activate_;
263
264 // Data for find match rects in a single frame.
265 struct FrameRects {
266 // The rects contained in a single frame.
267 std::vector<gfx::RectF> rects;
268
269 // The version number for these rects, as reported by their containing
270 // frame. This version is incremented independently in each frame.
271 int version = kInvalidId;
272
273 FrameRects();
274 FrameRects(const std::vector<gfx::RectF>& rects, int version);
275 ~FrameRects();
276 };
paulmeyerc0b762b2016-04-13 11:55:17277
278 // State related to FindMatchRects requests.
279 struct FindMatchRectsState {
paulmeyerc8cb7cb2016-06-07 01:14:19280 // The latest find match rects version known by the requester. This will be
281 // compared to |known_version_| after polling frames for updates to their
282 // match rects, in order to determine if the requester already has the
283 // latest version of rects or not.
paulmeyerc0b762b2016-04-13 11:55:17284 int request_version = kInvalidId;
paulmeyerc8cb7cb2016-06-07 01:14:19285
286 // The current overall find match rects version known by
287 // FindRequestManager. This version should be incremented whenever
288 // |frame_rects| is updated.
289 int known_version = 0;
290
291 // A map from each frame to its find match rects.
292 std::unordered_map<RenderFrameHost*, FrameRects> frame_rects;
293
294 // The active find match rect.
295 gfx::RectF active_rect;
296
297 // Find match rects replies are still pending for these frames.
Ali Hijazia56154d2024-02-26 10:21:17298 std::unordered_set<raw_ptr<RenderFrameHost, CtnExperimental>>
299 pending_replies;
paulmeyerc8cb7cb2016-06-07 01:14:19300
301 FindMatchRectsState();
302 ~FindMatchRectsState();
paulmeyerc0b762b2016-04-13 11:55:17303 } match_rects_;
304#endif
305
paulmeyerfeafc2d2017-04-25 21:46:40306 // The WebContents that owns this FindRequestManager. This also defines the
307 // scope of all find sessions. Only frames in |contents_| and any inner
308 // WebContentses within it will be searched.
Keishi Hattori0e45c022021-11-27 09:25:52309 const raw_ptr<WebContentsImpl> contents_;
paulmeyerc0b762b2016-04-13 11:55:17310
311 // The request ID of the initial find request in the current find-in-page
312 // session, which uniquely identifies this session. Request IDs are included
313 // in all find-related IPCs, which allows reply IPCs containing results from
314 // previous sessions (with |request_id| < |current_session_id_|) to be easily
315 // identified and ignored.
Lei Zhang4ea8c202019-10-16 02:27:59316 int current_session_id_ = kInvalidId;
paulmeyerc0b762b2016-04-13 11:55:17317
318 // The current find request.
319 FindRequest current_request_;
320
paulmeyerbbaacbe2016-08-30 18:04:13321 // The set of frames that are still expected to reply to a pending initial
322 // find request. Frames are removed from |pending_initial_replies_| when their
323 // reply to the initial find request is received with |final_update| set to
324 // true.
Ali Hijazia56154d2024-02-26 10:21:17325 std::unordered_set<raw_ptr<RenderFrameHost, CtnExperimental>>
326 pending_initial_replies_;
paulmeyerbbaacbe2016-08-30 18:04:13327
328 // The frame (if any) that is still expected to reply to the last pending
329 // "find next" request.
Keishi Hattori0e45c022021-11-27 09:25:52330 raw_ptr<RenderFrameHost> pending_find_next_reply_ = nullptr;
paulmeyerc8cb7cb2016-06-07 01:14:19331
332 // Indicates whether an update to the active match ordinal is expected. Once
333 // set, |pending_active_match_ordinal_| will not reset until an update to the
334 // active match ordinal is received in response to the find request with ID
335 // |current_request_.id| (the latest request).
Lei Zhang4ea8c202019-10-16 02:27:59336 bool pending_active_match_ordinal_ = false;
paulmeyerc8cb7cb2016-06-07 01:14:19337
Rakina Zata Amni724f3efe2018-07-23 03:19:46338 // The FindInPageClient associated with each frame. There will necessarily be
paulmeyerc8cb7cb2016-06-07 01:14:19339 // entries in this map for every frame that is being (or has been) searched in
340 // the current find session, and no other frames.
Rakina Zata Amni724f3efe2018-07-23 03:19:46341 std::unordered_map<RenderFrameHost*, std::unique_ptr<FindInPageClient>>
342 find_in_page_clients_;
paulmeyerc8cb7cb2016-06-07 01:14:19343
344 // The total number of matches found in the current find-in-page session. This
345 // should always be equal to the sum of all the entries in
346 // |matches_per_frame_|.
Lei Zhang4ea8c202019-10-16 02:27:59347 int number_of_matches_ = 0;
paulmeyerc0b762b2016-04-13 11:55:17348
paulmeyerc8cb7cb2016-06-07 01:14:19349 // The frame containing the active match, if one exists, or nullptr otherwise.
Keishi Hattori0e45c022021-11-27 09:25:52350 raw_ptr<RenderFrameHostImpl> active_frame_ = nullptr;
paulmeyerc8cb7cb2016-06-07 01:14:19351
352 // The active match ordinal relative to the matches found in its own frame.
Lei Zhang4ea8c202019-10-16 02:27:59353 int relative_active_match_ordinal_ = 0;
paulmeyerc8cb7cb2016-06-07 01:14:19354
paulmeyerc0b762b2016-04-13 11:55:17355 // The overall active match ordinal for the current find-in-page session.
Lei Zhang4ea8c202019-10-16 02:27:59356 int active_match_ordinal_ = 0;
paulmeyerc0b762b2016-04-13 11:55:17357
358 // The rectangle around the active match, in screen coordinates.
359 gfx::Rect selection_rect_;
paulmeyerc8cb7cb2016-06-07 01:14:19360
361 // Find requests are queued here when previous requests need to be handled
362 // before these ones can be properly routed.
Brett Wilsoncc8623d2017-09-12 03:28:10363 base::queue<FindRequest> find_request_queue_;
paulmeyerbbaacbe2016-08-30 18:04:13364
365 // Keeps track of the find request ID of the last find reply reported via
366 // NotifyFindReply().
Lei Zhang4ea8c202019-10-16 02:27:59367 int last_reported_id_ = kInvalidId;
paulmeyerfeafc2d2017-04-25 21:46:40368
369 // WebContentsObservers to observe frame changes in |contents_| and its inner
370 // WebContentses.
371 std::vector<std::unique_ptr<FrameObserver>> frame_observers_;
Joey Arhar8afb88ed2021-09-22 20:11:47372
Joey Arharcac45bf2022-01-07 21:59:31373 base::CancelableOnceClosure delayed_find_task_;
374
Miyoung Shinbf4c40c2021-10-21 11:00:23375 CreateFindInPageClientFunction create_find_in_page_client_for_testing_ =
376 nullptr;
Joey Arharcac45bf2022-01-07 21:59:31377
378 base::WeakPtrFactory<FindRequestManager> weak_factory_{this};
paulmeyerc0b762b2016-04-13 11:55:17379};
380
381} // namespace content
382
383#endif // CONTENT_BROWSER_FIND_REQUEST_MANAGER_H_