blob: 33adf341692624cc850607c3c238855e5035fdd3 [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#include "content/browser/find_request_manager.h"
6
Peter Kasting1557e5f2025-01-28 01:14:087#include <algorithm>
Lei Zhang4ea8c202019-10-16 02:27:598#include <utility>
paulmeyerfeafc2d2017-04-25 21:46:409
Lei Zhangd4f2c7ad2021-05-13 20:10:1210#include "base/containers/contains.h"
Brett Wilsoncc8623d2017-09-12 03:28:1011#include "base/containers/queue.h"
Avi Drissmanadac21992023-01-11 23:46:3912#include "base/functional/bind.h"
Keishi Hattori0e45c022021-11-27 09:25:5213#include "base/memory/raw_ptr.h"
Daniel Cheng4f1c5332025-06-19 19:37:2114#include "base/notimplemented.h"
Sean Maher5b9af51f2022-11-21 15:32:4715#include "base/task/single_thread_task_runner.h"
Xiaohan Wang1ecfd002022-01-19 22:33:1016#include "build/build_config.h"
Rakina Zata Amni724f3efe2018-07-23 03:19:4617#include "content/browser/find_in_page_client.h"
danakje94b7c842020-09-16 18:47:4318#include "content/browser/renderer_host/render_frame_host_impl.h"
paulmeyerc0b762b2016-04-13 11:55:1719#include "content/browser/web_contents/web_contents_impl.h"
Lei Zhang7c46fde2021-10-18 19:09:2220#include "content/public/browser/content_browser_client.h"
Christian Dullweber5e66727b2021-07-02 13:23:1521#include "content/public/browser/render_frame_host.h"
Lei Zhang7c46fde2021-10-18 19:09:2222#include "content/public/common/content_client.h"
Kevin McNee2585e732024-10-28 22:11:1923#include "content/public/common/content_features.h"
paulmeyerc0b762b2016-04-13 11:55:1724
25namespace content {
26
paulmeyerc8cb7cb2016-06-07 01:14:1927namespace {
28
Harkiran Bolaria37e212682021-10-14 19:44:3729// The following functions allow traversal over all RenderFrameHosts, including
Jeremy Romanc0c69be2023-11-21 19:14:5230// those across WebContentses.
paulmeyerfeafc2d2017-04-25 21:46:4031//
W. James MacLean2539adb32019-12-13 00:40:4432// An inner WebContents may be embedded in an outer WebContents via an inner
33// WebContentsTreeNode of the outer WebContents's WebContentsTreeNode.
Harkiran Bolaria37e212682021-10-14 19:44:3734std::vector<RenderFrameHostImpl*> GetChildren(RenderFrameHostImpl* rfh) {
35 std::vector<RenderFrameHostImpl*> children;
36 children.reserve(rfh->child_count());
37 for (size_t i = 0; i != rfh->child_count(); ++i) {
Rakina Zata Amni3d10bb3c2019-04-19 03:12:0738 if (auto* contents = static_cast<WebContentsImpl*>(
Harkiran Bolaria37e212682021-10-14 19:44:3739 WebContentsImpl::FromOuterFrameTreeNode(rfh->child_at(i)))) {
Jeremy Romanc0c69be2023-11-21 19:14:5240 // If the child is used for an inner WebContents then add the inner
41 // WebContents.
42 children.push_back(
43 contents->GetPrimaryFrameTree().root()->current_frame_host());
Rakina Zata Amni3d10bb3c2019-04-19 03:12:0744 } else {
Harkiran Bolaria37e212682021-10-14 19:44:3745 children.push_back(rfh->child_at(i)->current_frame_host());
paulmeyerfeafc2d2017-04-25 21:46:4046 }
47 }
48
49 return children;
50}
51
Harkiran Bolaria37e212682021-10-14 19:44:3752// Returns the first child RenderFrameHostImpl under |rfh|, if |rfh| has a
53// child, or nullptr otherwise.
54RenderFrameHostImpl* GetFirstChild(RenderFrameHostImpl* rfh) {
55 auto children = GetChildren(rfh);
paulmeyerfeafc2d2017-04-25 21:46:4056 if (!children.empty())
57 return children.front();
58 return nullptr;
59}
60
Harkiran Bolaria37e212682021-10-14 19:44:3761// Returns the last child RenderFrameHostImpl under |rfh|, if |rfh| has a
62// child, or nullptr otherwise.
63RenderFrameHostImpl* GetLastChild(RenderFrameHostImpl* rfh) {
64 auto children = GetChildren(rfh);
paulmeyerfeafc2d2017-04-25 21:46:4065 if (!children.empty())
66 return children.back();
67 return nullptr;
68}
69
Harkiran Bolaria37e212682021-10-14 19:44:3770// Returns the deepest last child frame under |rfh| in the frame tree.
71RenderFrameHostImpl* GetDeepestLastChild(RenderFrameHostImpl* rfh) {
72 while (RenderFrameHostImpl* last_child = GetLastChild(rfh))
73 rfh = last_child;
74 return rfh;
paulmeyerc8cb7cb2016-06-07 01:14:1975}
paulmeyerc8cb7cb2016-06-07 01:14:1976
Harkiran Bolaria37e212682021-10-14 19:44:3777// Returns the parent RenderFrameHost of |rfh|, if |rfh| has a parent, or
paulmeyerfeafc2d2017-04-25 21:46:4078// nullptr otherwise.
Harkiran Bolaria37e212682021-10-14 19:44:3779RenderFrameHostImpl* GetAncestor(RenderFrameHostImpl* rfh) {
80 if (!rfh)
Lei Zhang4ea8c202019-10-16 02:27:5981 return nullptr;
Rakina Zata Amni3d10bb3c2019-04-19 03:12:0782
Harkiran Bolaria37e212682021-10-14 19:44:3783 return rfh->GetParentOrOuterDocumentOrEmbedder();
paulmeyerfeafc2d2017-04-25 21:46:4084}
85
Harkiran Bolaria37e212682021-10-14 19:44:3786// Returns the previous sibling RenderFrameHostImpl of |rfh|, if one exists,
87// or nullptr otherwise.
88RenderFrameHostImpl* GetPreviousSibling(RenderFrameHostImpl* rfh) {
89 if (rfh->PreviousSibling()) {
90 return rfh->PreviousSibling()->current_frame_host();
91 }
paulmeyerfeafc2d2017-04-25 21:46:4092
93 // The previous sibling may be in another WebContents.
Harkiran Bolaria37e212682021-10-14 19:44:3794 if (RenderFrameHostImpl* parent = GetAncestor(rfh)) {
paulmeyerfeafc2d2017-04-25 21:46:4095 auto children = GetChildren(parent);
Peter Kasting1557e5f2025-01-28 01:14:0896 auto it = std::ranges::find(children, rfh);
Harkiran Bolaria37e212682021-10-14 19:44:3797 // It is odd that this rfh may not be a child of its parent, but this is
paulmeyerfeafc2d2017-04-25 21:46:4098 // actually possible during teardown, hence the need for the check for
99 // "it != children.end()".
100 if (it != children.end() && it != children.begin())
101 return *--it;
102 }
103
104 return nullptr;
105}
106
Harkiran Bolaria37e212682021-10-14 19:44:37107// Returns the next sibling RenderFrameHostImpl of |rfh|, if one exists, or
108// nullptr otherwise.
109RenderFrameHostImpl* GetNextSibling(RenderFrameHostImpl* rfh) {
110 if (rfh->NextSibling())
111 return rfh->NextSibling()->current_frame_host();
paulmeyerfeafc2d2017-04-25 21:46:40112
113 // The next sibling may be in another WebContents.
Harkiran Bolaria37e212682021-10-14 19:44:37114 if (RenderFrameHostImpl* parent = GetAncestor(rfh)) {
paulmeyerfeafc2d2017-04-25 21:46:40115 auto children = GetChildren(parent);
Peter Kasting1557e5f2025-01-28 01:14:08116 auto it = std::ranges::find(children, rfh);
Harkiran Bolaria37e212682021-10-14 19:44:37117 // It is odd that this RenderFrameHost may not be a child of its parent, but
118 // this is actually possible during teardown, hence the need for the check
119 // for "it != children.end()".
paulmeyerfeafc2d2017-04-25 21:46:40120 if (it != children.end() && ++it != children.end())
121 return *it;
122 }
123
124 return nullptr;
125}
126
Harkiran Bolaria37e212682021-10-14 19:44:37127// Returns the RenderFrameHostImpl directly after |rfh| in the rfh tree in
128// search order, or nullptr if one does not exist. If |wrap| is set, then
129// wrapping between the first and last frames is permitted. Note that this
130// traversal follows the same ordering as in
131// blink::FrameTree::traverseNextWithWrap().
132RenderFrameHostImpl* TraverseNext(RenderFrameHostImpl* rfh, bool wrap) {
133 if (RenderFrameHostImpl* first_child = GetFirstChild(rfh))
paulmeyerfeafc2d2017-04-25 21:46:40134 return first_child;
paulmeyerc8cb7cb2016-06-07 01:14:19135
Harkiran Bolaria37e212682021-10-14 19:44:37136 RenderFrameHostImpl* sibling = GetNextSibling(rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19137 while (!sibling) {
Harkiran Bolaria37e212682021-10-14 19:44:37138 RenderFrameHostImpl* parent = GetAncestor(rfh);
paulmeyerfeafc2d2017-04-25 21:46:40139 if (!parent)
Harkiran Bolaria37e212682021-10-14 19:44:37140 return wrap ? rfh : nullptr;
141 rfh = parent;
142 sibling = GetNextSibling(rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19143 }
144 return sibling;
145}
146
Harkiran Bolaria37e212682021-10-14 19:44:37147// Returns the RenderFrameHostImpl directly before |rfh| in the frame tree in
148// search order, or nullptr if one does not exist. If |wrap| is set, then
149// wrapping between the first and last frames is permitted. Note that this
150// traversal follows the same ordering as in
151// blink::FrameTree::traversePreviousWithWrap().
152RenderFrameHostImpl* TraversePrevious(RenderFrameHostImpl* rfh, bool wrap) {
153 if (RenderFrameHostImpl* previous_sibling = GetPreviousSibling(rfh))
paulmeyerc8cb7cb2016-06-07 01:14:19154 return GetDeepestLastChild(previous_sibling);
Harkiran Bolaria37e212682021-10-14 19:44:37155 if (RenderFrameHostImpl* parent = GetAncestor(rfh))
paulmeyerfeafc2d2017-04-25 21:46:40156 return parent;
Harkiran Bolaria37e212682021-10-14 19:44:37157 return wrap ? GetDeepestLastChild(rfh) : nullptr;
paulmeyerc8cb7cb2016-06-07 01:14:19158}
159
paulmeyerfeafc2d2017-04-25 21:46:40160// The same as either TraverseNext() or TraversePrevious(), depending on
paulmeyerc8cb7cb2016-06-07 01:14:19161// |forward|.
Harkiran Bolaria37e212682021-10-14 19:44:37162RenderFrameHostImpl* TraverseFrame(RenderFrameHostImpl* rfh,
163 bool forward,
164 bool wrap) {
165 return forward ? TraverseNext(rfh, wrap) : TraversePrevious(rfh, wrap);
paulmeyerc8cb7cb2016-06-07 01:14:19166}
167
Lei Zhang7c46fde2021-10-18 19:09:22168bool IsFindInPageDisabled(RenderFrameHost* rfh) {
169 return rfh && GetContentClient()->browser()->IsFindInPageDisabledForOrigin(
170 rfh->GetLastCommittedOrigin());
171}
172
Miyoung Shin0e9b16b2022-10-03 11:05:17173bool IsUnattachedGuestView(RenderFrameHost* rfh) {
Kevin McNee2585e732024-10-28 22:11:19174 if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
175 NOTIMPLEMENTED();
Miyoung Shin0e9b16b2022-10-03 11:05:17176 return false;
Kevin McNee2585e732024-10-28 22:11:19177 } else {
178 WebContentsImpl* web_contents =
179 static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh));
180 if (!web_contents->IsGuest()) {
181 return false;
182 }
Miyoung Shin0e9b16b2022-10-03 11:05:17183
Kevin McNee2585e732024-10-28 22:11:19184 return !web_contents->GetOuterWebContents();
185 }
Miyoung Shin0e9b16b2022-10-03 11:05:17186}
187
Joey Arharcac45bf2022-01-07 21:59:31188// kMinKeystrokesWithoutDelay should be high enough that script in the page
189// can't provide every possible search result at the same time.
190constexpr int kMinKeystrokesWithoutDelay = 4;
191
192// The delay for very short queries, before sending find requests. This should
193// be higher than the duration in between two keystrokes. This is based on
194// WebCore.FindInPage.DurationBetweenKeystrokes metrics, this is higher than
195// 90% of them.
196constexpr int kDelayMs = 400;
197
paulmeyerc8cb7cb2016-06-07 01:14:19198} // namespace
199
Miyoung Shinbf4c40c2021-10-21 11:00:23200// Observes searched WebContentses for RenderFrameHost state updates, including
201// deletion and loads.
paulmeyerfeafc2d2017-04-25 21:46:40202class FindRequestManager::FrameObserver : public WebContentsObserver {
203 public:
Miyoung Shinbf4c40c2021-10-21 11:00:23204 FrameObserver(WebContents* web_contents, FindRequestManager* manager)
paulmeyerfeafc2d2017-04-25 21:46:40205 : WebContentsObserver(web_contents), manager_(manager) {}
206
Peter Boström828b9022021-09-21 02:28:43207 FrameObserver(const FrameObserver&) = delete;
208 FrameObserver& operator=(const FrameObserver&) = delete;
209
Lei Zhang4ea8c202019-10-16 02:27:59210 ~FrameObserver() override = default;
paulmeyerfeafc2d2017-04-25 21:46:40211
Miyoung Shinbf4c40c2021-10-21 11:00:23212 void RenderFrameDeleted(RenderFrameHost* rfh) override {
213 manager_->RemoveFrame(rfh);
214 }
215
216 void RenderFrameHostStateChanged(
217 RenderFrameHost* rfh,
218 RenderFrameHost::LifecycleState old_state,
219 RenderFrameHost::LifecycleState new_state) override {
220 if (manager_->current_session_id_ == kInvalidId ||
221 IsFindInPageDisabled(rfh)) {
222 return;
223 }
224
225 if (new_state == RenderFrameHost::LifecycleState::kActive) {
226 // Add the RFH to the current find-in-page session when its status
227 // changes to active since this is when the document becomes part of the
228 // primary page (i.e prerendered pages getting activated, or pages in
229 // BackForwardCache getting restored), so that we can get the results from
230 // all frames in the primary page.
231 manager_->AddFrame(rfh, false /* force */);
232 } else if (old_state == RenderFrameHost::LifecycleState::kActive) {
233 // Remove the RFH from the current find-in-page session if it stops being
234 // part of the primary page.
235 manager_->RemoveFrame(rfh);
236 }
237 }
238
paulmeyerfeafc2d2017-04-25 21:46:40239 void DidFinishLoad(RenderFrameHost* rfh, const GURL& validated_url) override {
240 if (manager_->current_session_id_ == kInvalidId)
241 return;
242
243 manager_->RemoveFrame(rfh);
Carlos Caballero301e1752021-03-25 13:20:58244 // Make sure RenderFrameDeleted will be called to clean up
Nasko Oskov8b04f402022-05-26 23:29:56245 DCHECK(rfh->IsRenderFrameLive());
Lei Zhang7c46fde2021-10-18 19:09:22246
247 if (IsFindInPageDisabled(rfh))
248 return;
249
250 manager_->AddFrame(rfh, /*force=*/true);
paulmeyerfeafc2d2017-04-25 21:46:40251 }
252
paulmeyerfeafc2d2017-04-25 21:46:40253 private:
254 // The FindRequestManager that owns this FrameObserver.
Keishi Hattori0e45c022021-11-27 09:25:52255 const raw_ptr<FindRequestManager> manager_;
paulmeyerfeafc2d2017-04-25 21:46:40256};
257
Joey Arhar0574fb752022-03-29 00:45:16258bool FindRequestManager::RunDelayedFindTaskForTesting() {
259 if (!delayed_find_task_.IsCancelled()) {
260 delayed_find_task_.callback().Run();
261 return true;
262 }
263 return false;
264}
265
Lei Zhang4ea8c202019-10-16 02:27:59266FindRequestManager::FindRequest::FindRequest() = default;
Rakina Zata Amni3f77dff2018-09-08 16:19:43267
268FindRequestManager::FindRequest::FindRequest(
269 int id,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58270 const std::u16string& search_text,
Rakina Zata Amni3f77dff2018-09-08 16:19:43271 blink::mojom::FindOptionsPtr options)
272 : id(id), search_text(search_text), options(std::move(options)) {}
273
274FindRequestManager::FindRequest::FindRequest(const FindRequest& request)
275 : id(request.id),
276 search_text(request.search_text),
277 options(request.options.Clone()) {}
278
Lei Zhang4ea8c202019-10-16 02:27:59279FindRequestManager::FindRequest::~FindRequest() = default;
Rakina Zata Amni3f77dff2018-09-08 16:19:43280
281FindRequestManager::FindRequest& FindRequestManager::FindRequest::operator=(
282 const FindRequest& request) {
283 id = request.id;
284 search_text = request.search_text;
285 options = request.options.Clone();
286 return *this;
287}
288
Xiaohan Wang1ecfd002022-01-19 22:33:10289#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19290FindRequestManager::ActivateNearestFindResultState::
291ActivateNearestFindResultState() = default;
292FindRequestManager::ActivateNearestFindResultState::
Rakina Zata Amni57bec362018-05-22 06:05:10293 ActivateNearestFindResultState(float x, float y)
294 : current_request_id(GetNextID()), point(x, y) {}
paulmeyerc8cb7cb2016-06-07 01:14:19295FindRequestManager::ActivateNearestFindResultState::
Lei Zhang4ea8c202019-10-16 02:27:59296 ~ActivateNearestFindResultState() = default;
paulmeyerc8cb7cb2016-06-07 01:14:19297
Devon Loehr11aff822025-05-30 16:25:28298int FindRequestManager::ActivateNearestFindResultState::GetNextID() {
299 static int next_id = 0;
300 return next_id++;
301}
302
paulmeyerc8cb7cb2016-06-07 01:14:19303FindRequestManager::FrameRects::FrameRects() = default;
304FindRequestManager::FrameRects::FrameRects(const std::vector<gfx::RectF>& rects,
305 int version)
306 : rects(rects), version(version) {}
Lei Zhang4ea8c202019-10-16 02:27:59307FindRequestManager::FrameRects::~FrameRects() = default;
paulmeyerc8cb7cb2016-06-07 01:14:19308
309FindRequestManager::FindMatchRectsState::FindMatchRectsState() = default;
Lei Zhang4ea8c202019-10-16 02:27:59310FindRequestManager::FindMatchRectsState::~FindMatchRectsState() = default;
paulmeyerc8cb7cb2016-06-07 01:14:19311#endif
312
paulmeyerc0b762b2016-04-13 11:55:17313// static
314const int FindRequestManager::kInvalidId = -1;
315
316FindRequestManager::FindRequestManager(WebContentsImpl* web_contents)
Lei Zhang4ea8c202019-10-16 02:27:59317 : contents_(web_contents) {}
paulmeyerc0b762b2016-04-13 11:55:17318
Lei Zhang4ea8c202019-10-16 02:27:59319FindRequestManager::~FindRequestManager() = default;
paulmeyerc0b762b2016-04-13 11:55:17320
321void FindRequestManager::Find(int request_id,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58322 const std::u16string& search_text,
Joey Arharcac45bf2022-01-07 21:59:31323 blink::mojom::FindOptionsPtr options,
324 bool skip_delay) {
paulmeyerc0b762b2016-04-13 11:55:17325 // Every find request must have a unique ID, and these IDs must strictly
326 // increase so that newer requests always have greater IDs than older
327 // requests.
328 DCHECK_GT(request_id, current_request_.id);
329 DCHECK_GT(request_id, current_session_id_);
330
Joey Arharcac45bf2022-01-07 21:59:31331 if (skip_delay) {
332 delayed_find_task_.Cancel();
333 EmitFindRequest(request_id, search_text, std::move(options));
334 return;
335 }
336
337 if (!options->new_session) {
338 // If the user presses enter while we are waiting for a delayed find, then
339 // run the find now to improve responsiveness.
340 if (!delayed_find_task_.IsCancelled()) {
341 delayed_find_task_.callback().Run();
342 } else {
343 EmitFindRequest(request_id, search_text, std::move(options));
344 }
345 return;
346 }
347
348 if (search_text.length() < kMinKeystrokesWithoutDelay) {
349 delayed_find_task_.Reset(base::BindOnce(
350 &FindRequestManager::EmitFindRequest, weak_factory_.GetWeakPtr(),
351 request_id, search_text, std::move(options)));
Sean Maher5b9af51f2022-11-21 15:32:47352 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Joey Arharcac45bf2022-01-07 21:59:31353 FROM_HERE, delayed_find_task_.callback(), base::Milliseconds(kDelayMs));
354 return;
355 }
356
357 // If we aren't going to delay, then clear any previous attempts to delay.
358 delayed_find_task_.Cancel();
359
360 EmitFindRequest(request_id, search_text, std::move(options));
361}
362
363void FindRequestManager::EmitFindRequest(int request_id,
364 const std::u16string& search_text,
365 blink::mojom::FindOptionsPtr options) {
paulmeyerc8cb7cb2016-06-07 01:14:19366 // If this is a new find session, clear any queued requests from last session.
Russell Davis8a36226c2020-06-05 17:09:50367 if (options->new_session)
Brett Wilsoncc8623d2017-09-12 03:28:10368 find_request_queue_ = base::queue<FindRequest>();
paulmeyerc0b762b2016-04-13 11:55:17369
Rakina Zata Amni3f77dff2018-09-08 16:19:43370 find_request_queue_.emplace(request_id, search_text, std::move(options));
paulmeyerc8cb7cb2016-06-07 01:14:19371 if (find_request_queue_.size() == 1)
372 FindInternal(find_request_queue_.front());
Artur3bb150aa2022-10-18 02:36:05373 if (request_id == current_session_id_)
374 find_request_queue_.pop();
paulmeyerc0b762b2016-04-13 11:55:17375}
376
Miyoung Shinbf4c40c2021-10-21 11:00:23377void FindRequestManager::ForEachAddedFindInPageRenderFrameHost(
Daniel Cheng982f2b22022-08-25 23:46:16378 base::FunctionRef<void(RenderFrameHostImpl*)> func_ref) {
Peter Kastingd2876a22024-12-03 01:12:55379 contents_->GetPrimaryMainFrame()->ForEachRenderFrameHostImpl(
Daniel Cheng982f2b22022-08-25 23:46:16380 [this, func_ref](RenderFrameHostImpl* rfh) {
381 if (!CheckFrame(rfh))
Miyoung Shinbf4c40c2021-10-21 11:00:23382 return;
Miyoung Shinbf4c40c2021-10-21 11:00:23383 DCHECK(rfh->IsRenderFrameLive());
384 DCHECK(rfh->IsActive());
Daniel Cheng982f2b22022-08-25 23:46:16385 func_ref(rfh);
386 });
Miyoung Shinbf4c40c2021-10-21 11:00:23387}
388
paulmeyerc0b762b2016-04-13 11:55:17389void FindRequestManager::StopFinding(StopFindAction action) {
Joey Arhar0574fb752022-03-29 00:45:16390 // Cancel any delayed find-in-page requests
391 delayed_find_task_.Cancel();
392
Daniel Cheng982f2b22022-08-25 23:46:16393 ForEachAddedFindInPageRenderFrameHost([action](RenderFrameHostImpl* rfh) {
394 rfh->GetFindInPage()->StopFinding(
395 // TODO(dcheng): Use typemapping or use the Mojo enum directly.
396 static_cast<blink::mojom::StopFindAction>(action));
397 });
paulmeyerc8cb7cb2016-06-07 01:14:19398
paulmeyerc0b762b2016-04-13 11:55:17399 current_session_id_ = kInvalidId;
Xiaohan Wang1ecfd002022-01-19 22:33:10400#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19401 // It is important that these pending replies are cleared whenever a find
402 // session ends, so that subsequent replies for the old session are ignored.
403 activate_.pending_replies.clear();
404 match_rects_.pending_replies.clear();
405#endif
paulmeyerc0b762b2016-04-13 11:55:17406}
407
Rakina Zata Amni2ac57502018-08-16 01:44:45408bool FindRequestManager::ShouldIgnoreReply(RenderFrameHostImpl* rfh,
409 int request_id) {
Makoto Shimazufc0e1ea2018-08-14 08:12:52410 // Ignore stale replies from abandoned find sessions or dead frames.
Rakina Zata Amni2ac57502018-08-16 01:44:45411 return current_session_id_ == kInvalidId ||
412 request_id < current_session_id_ || !CheckFrame(rfh);
Makoto Shimazufc0e1ea2018-08-14 08:12:52413}
414
Rakina Zata Amni724f3efe2018-07-23 03:19:46415void FindRequestManager::HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh,
416 int request_id) {
paulmeyerc8cb7cb2016-06-07 01:14:19417 // This is the final update for this frame for the current find operation.
paulmeyerbbaacbe2016-08-30 18:04:13418 pending_initial_replies_.erase(rfh);
419 if (request_id == current_session_id_ && !pending_initial_replies_.empty()) {
paulmeyerc8cb7cb2016-06-07 01:14:19420 NotifyFindReply(request_id, false /* final_update */);
421 return;
422 }
paulmeyerc8cb7cb2016-06-07 01:14:19423
Rakina Zata Amni724f3efe2018-07-23 03:19:46424 // This is the final update for all frames for the current find operation.
paulmeyerbbaacbe2016-08-30 18:04:13425 if (request_id == current_request_.id && request_id != current_session_id_) {
Russell Davis8a36226c2020-06-05 17:09:50426 DCHECK(!current_request_.options->new_session);
paulmeyerbbaacbe2016-08-30 18:04:13427 DCHECK_EQ(pending_find_next_reply_, rfh);
428 pending_find_next_reply_ = nullptr;
429 }
430
431 FinalUpdateReceived(request_id, rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19432}
433
Rakina Zata Amni724f3efe2018-07-23 03:19:46434void FindRequestManager::UpdatedFrameNumberOfMatches(RenderFrameHostImpl* rfh,
435 unsigned int old_count,
436 unsigned int new_count) {
437 if (old_count == new_count)
438 return;
439
440 // Change the number of matches for this frame in the global count.
441 number_of_matches_ -= old_count;
442 number_of_matches_ += new_count;
443
444 // All matches may have been removed since the last find reply.
445 if (rfh == active_frame_ && !new_count)
446 relative_active_match_ordinal_ = 0;
447
448 // The active match ordinal may need updating since the number of matches
449 // before the active match may have changed.
450 UpdateActiveMatchOrdinal();
451}
452
453void FindRequestManager::SetActiveMatchRect(
454 const gfx::Rect& active_match_rect) {
455 selection_rect_ = active_match_rect;
456}
457
458void FindRequestManager::SetActiveMatchOrdinal(RenderFrameHostImpl* rfh,
459 int request_id,
460 int active_match_ordinal) {
Rakina Zata Amni2ac57502018-08-16 01:44:45461 if (active_match_ordinal > 0) {
462 // Call SetFocusedFrame on the WebContents associated with |rfh| (which
463 // might not be the same as |contents_|, as a WebContents might have
464 // inner WebContents). We need to focus on the frame where the active
465 // match is in, which should be in the |rfh|'s associated WebContents.
466 WebContentsImpl* web_contents =
467 static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh));
Rakina Zata Amnicca4889e2021-09-28 23:25:55468 // Do not focus inactive RenderFrameHost.
469 if (!rfh->IsActive())
470 return;
Rakina Zata Amni77b7a4f02018-08-28 16:40:55471 web_contents->SetFocusedFrame(rfh->frame_tree_node(),
Sharon Yangefe52632022-03-08 23:06:06472 rfh->GetSiteInstance()->group());
Rakina Zata Amni2ac57502018-08-16 01:44:45473 }
Rakina Zata Amni724f3efe2018-07-23 03:19:46474 if (rfh == active_frame_) {
475 active_match_ordinal_ +=
476 active_match_ordinal - relative_active_match_ordinal_;
477 relative_active_match_ordinal_ = active_match_ordinal;
478 } else {
479 if (active_frame_) {
480 // The new active match is in a different frame than the previous, so
481 // the previous active frame needs to be informed (to clear its active
482 // match highlighting).
483 ClearActiveFindMatch();
484 }
485 active_frame_ = rfh;
486 relative_active_match_ordinal_ = active_match_ordinal;
487 UpdateActiveMatchOrdinal();
488 }
489 if (pending_active_match_ordinal_ && request_id == current_request_.id)
490 pending_active_match_ordinal_ = false;
491 AdvanceQueue(request_id);
Rakina Zata Amni57bec362018-05-22 06:05:10492}
493
paulmeyerc8cb7cb2016-06-07 01:14:19494void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) {
paulmeyerc8cb7cb2016-06-07 01:14:19495 // If matches are counted for the frame that is being removed, decrement the
496 // match total before erasing that entry.
Rakina Zata Amni724f3efe2018-07-23 03:19:46497 auto it = find_in_page_clients_.find(rfh);
498 if (it != find_in_page_clients_.end()) {
499 number_of_matches_ -= it->second->number_of_matches();
500 find_in_page_clients_.erase(it);
Rakina Zata Amni2a6bc4b2023-11-16 00:04:18501 } else {
502 // If there's no FindInPageClient for `rfh`, the state related to it must
503 // have been cleared already.
504 return;
paulmeyerc8cb7cb2016-06-07 01:14:19505 }
506
Gyuyoung Kima855ff5d2021-11-19 07:24:35507 // If this is a primary main frame, then clear the search queue as well, since
508 // we shouldn't be dispatching any more requests. Note that if any other frame
509 // is removed, we can target any queued requests to the focused frame or
510 // primary main frame. However, if the primary main frame is removed we will
511 // not have a valid RenderFrameHost to target for the request queue.
512 if (rfh->IsInPrimaryMainFrame())
Vladimir Levin34a865b2020-01-08 21:54:29513 find_request_queue_ = base::queue<FindRequest>();
514
paulmeyerc8cb7cb2016-06-07 01:14:19515 // Update the active match ordinal, since it may have changed.
516 if (active_frame_ == rfh) {
517 active_frame_ = nullptr;
518 relative_active_match_ordinal_ = 0;
paulmeyerbbaacbe2016-08-30 18:04:13519 selection_rect_ = gfx::Rect();
paulmeyerc8cb7cb2016-06-07 01:14:19520 }
521 UpdateActiveMatchOrdinal();
522
Xiaohan Wang1ecfd002022-01-19 22:33:10523#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19524 // The removed frame may contain the nearest find result known so far. Note
525 // that once all queried frames have responded, if this result was the overall
526 // nearest, then no activation will occur.
527 if (rfh == activate_.nearest_frame)
528 activate_.nearest_frame = nullptr;
529
530 // Match rects in the removed frame are no longer relevant.
Lei Zhang4ea8c202019-10-16 02:27:59531 if (match_rects_.frame_rects.erase(rfh) != 0)
paulmeyerc8cb7cb2016-06-07 01:14:19532 ++match_rects_.known_version;
paulmeyerc8cb7cb2016-06-07 01:14:19533
534 // A reply should not be expected from the removed frame.
535 RemoveNearestFindResultPendingReply(rfh);
536 RemoveFindMatchRectsPendingReply(rfh);
537#endif
538
Rakina Zata Amni2a6bc4b2023-11-16 00:04:18539 if (current_session_id_ == kInvalidId) {
540 // Just remove `rfh` from things that might point to it, but don't trigger
541 // any extra processing as there is no current find session ongoing.
542 pending_initial_replies_.erase(rfh);
543 if (pending_find_next_reply_ == rfh) {
544 pending_find_next_reply_ = nullptr;
545 }
546 return;
547 }
548
549 // Make sure to always clear the highlighted selection. It is useful in case
550 // the user goes back to the same page using the BackForwardCache.
551 static_cast<RenderFrameHostImpl*>(rfh)->GetFindInPage()->StopFinding(
552 blink::mojom::StopFindAction::kStopFindActionClearSelection);
553
paulmeyerbbaacbe2016-08-30 18:04:13554 // If no pending find replies are expected for the removed frame, then just
555 // report the updated results.
Lei Zhang4ea8c202019-10-16 02:27:59556 if (!base::Contains(pending_initial_replies_, rfh) &&
557 pending_find_next_reply_ != rfh) {
paulmeyerbbaacbe2016-08-30 18:04:13558 bool final_update =
559 pending_initial_replies_.empty() && !pending_find_next_reply_;
560 NotifyFindReply(current_session_id_, final_update);
561 return;
562 }
563
Lei Zhang4ea8c202019-10-16 02:27:59564 if (pending_initial_replies_.erase(rfh) != 0) {
paulmeyerc8cb7cb2016-06-07 01:14:19565 // A reply should not be expected from the removed frame.
paulmeyerbbaacbe2016-08-30 18:04:13566 if (pending_initial_replies_.empty()) {
567 FinalUpdateReceived(current_session_id_, rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19568 }
569 }
570
paulmeyerbbaacbe2016-08-30 18:04:13571 if (pending_find_next_reply_ == rfh) {
572 // A reply should not be expected from the removed frame.
573 pending_find_next_reply_ = nullptr;
574 FinalUpdateReceived(current_request_.id, rfh);
575 }
paulmeyerc0b762b2016-04-13 11:55:17576}
577
Rakina Zata Amniacf40492018-05-08 22:59:33578void FindRequestManager::ClearActiveFindMatch() {
Rakina Zata Amni174ef712018-05-14 12:45:20579 active_frame_->GetFindInPage()->ClearActiveFindMatch();
Rakina Zata Amniacf40492018-05-08 22:59:33580}
581
Xiaohan Wang1ecfd002022-01-19 22:33:10582#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19583void FindRequestManager::ActivateNearestFindResult(float x, float y) {
paulmeyerc0b762b2016-04-13 11:55:17584 if (current_session_id_ == kInvalidId)
585 return;
586
paulmeyerc8cb7cb2016-06-07 01:14:19587 activate_ = ActivateNearestFindResultState(x, y);
588
589 // Request from each frame the distance to the nearest find result (in that
590 // frame) from the point (x, y), defined in find-in-page coordinates.
Daniel Cheng982f2b22022-08-25 23:46:16591 ForEachAddedFindInPageRenderFrameHost([this](RenderFrameHostImpl* rfh) {
592 activate_.pending_replies.insert(rfh);
593 // Lifetime of FindRequestManager > RenderFrameHost > Mojo
594 // connection, so it's safe to bind |this| and |rfh|.
595 rfh->GetFindInPage()->GetNearestFindResult(
596 activate_.point,
597 base::BindOnce(&FindRequestManager::OnGetNearestFindResultReply,
598 base::Unretained(this), rfh,
599 activate_.current_request_id));
600 });
paulmeyerc8cb7cb2016-06-07 01:14:19601}
602
Rakina Zata Amni57bec362018-05-22 06:05:10603void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHostImpl* rfh,
paulmeyerc8cb7cb2016-06-07 01:14:19604 int request_id,
605 float distance) {
606 if (request_id != activate_.current_request_id ||
Lei Zhang4ea8c202019-10-16 02:27:59607 !base::Contains(activate_.pending_replies, rfh)) {
paulmeyerc8cb7cb2016-06-07 01:14:19608 return;
609 }
610
611 // Check if this frame has a nearer find result than the current nearest.
612 if (distance < activate_.nearest_distance) {
613 activate_.nearest_frame = rfh;
614 activate_.nearest_distance = distance;
615 }
616
617 RemoveNearestFindResultPendingReply(rfh);
paulmeyerc0b762b2016-04-13 11:55:17618}
619
620void FindRequestManager::RequestFindMatchRects(int current_version) {
paulmeyerc8cb7cb2016-06-07 01:14:19621 match_rects_.pending_replies.clear();
paulmeyerc0b762b2016-04-13 11:55:17622 match_rects_.request_version = current_version;
WangHui66701ae2020-11-09 20:01:29623 match_rects_.active_rect = gfx::RectF();
paulmeyerc8cb7cb2016-06-07 01:14:19624
625 // Request the latest find match rects from each frame.
Daniel Cheng982f2b22022-08-25 23:46:16626 ForEachAddedFindInPageRenderFrameHost([this](RenderFrameHostImpl* rfh) {
627 match_rects_.pending_replies.insert(rfh);
628 auto it = match_rects_.frame_rects.find(rfh);
629 int version = (it != match_rects_.frame_rects.end()) ? it->second.version
630 : kInvalidId;
631 // Lifetime of FindRequestManager > RenderFrameHost > Mojo
632 // connection, so it's safe to bind |this| and |rfh|.
633 rfh->GetFindInPage()->FindMatchRects(
634 version, base::BindOnce(&FindRequestManager::OnFindMatchRectsReply,
635 base::Unretained(this), rfh));
636 });
paulmeyerc0b762b2016-04-13 11:55:17637}
638
639void FindRequestManager::OnFindMatchRectsReply(
640 RenderFrameHost* rfh,
641 int version,
642 const std::vector<gfx::RectF>& rects,
643 const gfx::RectF& active_rect) {
paulmeyerc8cb7cb2016-06-07 01:14:19644 auto it = match_rects_.frame_rects.find(rfh);
645 if (it == match_rects_.frame_rects.end() || it->second.version != version) {
646 // New version of rects has been received, so update the data.
647 match_rects_.frame_rects[rfh] = FrameRects(rects, version);
648 ++match_rects_.known_version;
649 }
650 if (!active_rect.IsEmpty())
651 match_rects_.active_rect = active_rect;
652 RemoveFindMatchRectsPendingReply(rfh);
paulmeyerc0b762b2016-04-13 11:55:17653}
654#endif
655
656void FindRequestManager::Reset(const FindRequest& initial_request) {
657 current_session_id_ = initial_request.id;
658 current_request_ = initial_request;
paulmeyerbbaacbe2016-08-30 18:04:13659 pending_initial_replies_.clear();
660 pending_find_next_reply_ = nullptr;
paulmeyerc8cb7cb2016-06-07 01:14:19661 pending_active_match_ordinal_ = true;
Rakina Zata Amni724f3efe2018-07-23 03:19:46662 find_in_page_clients_.clear();
paulmeyerc0b762b2016-04-13 11:55:17663 number_of_matches_ = 0;
paulmeyerc8cb7cb2016-06-07 01:14:19664 active_frame_ = nullptr;
665 relative_active_match_ordinal_ = 0;
paulmeyerc0b762b2016-04-13 11:55:17666 active_match_ordinal_ = 0;
667 selection_rect_ = gfx::Rect();
paulmeyerbbaacbe2016-08-30 18:04:13668 last_reported_id_ = kInvalidId;
paulmeyerfeafc2d2017-04-25 21:46:40669 frame_observers_.clear();
Xiaohan Wang1ecfd002022-01-19 22:33:10670#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19671 activate_ = ActivateNearestFindResultState();
672 match_rects_.pending_replies.clear();
paulmeyerc0b762b2016-04-13 11:55:17673#endif
674}
675
paulmeyerc8cb7cb2016-06-07 01:14:19676void FindRequestManager::FindInternal(const FindRequest& request) {
677 DCHECK_GT(request.id, current_request_.id);
678 DCHECK_GT(request.id, current_session_id_);
679
Russell Davis8a36226c2020-06-05 17:09:50680 if (!request.options->new_session) {
paulmeyerc8cb7cb2016-06-07 01:14:19681 // This is a find next operation.
682
683 // This implies that there is an ongoing find session with the same search
684 // text.
685 DCHECK_GE(current_session_id_, 0);
686 DCHECK_EQ(request.search_text, current_request_.search_text);
687
688 // The find next request will be directed at the focused frame if there is
689 // one, or the first frame with matches otherwise.
paulmeyerfeafc2d2017-04-25 21:46:40690 RenderFrameHost* target_rfh =
691 contents_->GetFocusedWebContents()->GetFocusedFrame();
paulmeyerc8cb7cb2016-06-07 01:14:19692 if (!target_rfh || !CheckFrame(target_rfh))
Rakina Zata Amni3f77dff2018-09-08 16:19:43693 target_rfh = GetInitialFrame(request.options->forward);
paulmeyerc8cb7cb2016-06-07 01:14:19694
Rakina Zata Amni2ac57502018-08-16 01:44:45695 SendFindRequest(request, target_rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19696 current_request_ = request;
697 pending_active_match_ordinal_ = true;
698 return;
699 }
700
701 // This is an initial find operation.
702 Reset(request);
Lei Zhang7c46fde2021-10-18 19:09:22703
Miyoung Shinbf4c40c2021-10-21 11:00:23704 // Add and observe eligible RFHs in the WebContents. And, use
Peter Kastingd2876a22024-12-03 01:12:55705 // ForEachRenderFrameHostImpl instead of ForEachAddedFindInPageRenderFrameHost
Miyoung Shinbf4c40c2021-10-21 11:00:23706 // because that calls CheckFrame() which will only be true if we've called
707 // AddFrame() for the frame.
Peter Kastingd2876a22024-12-03 01:12:55708 contents_->GetPrimaryMainFrame()->ForEachRenderFrameHostImpl(
Daniel Cheng982f2b22022-08-25 23:46:16709 [this](RenderFrameHostImpl* rfh) {
Miyoung Shinbf4c40c2021-10-21 11:00:23710 auto* wc = WebContents::FromRenderFrameHost(rfh);
Miyoung Shinbf4c40c2021-10-21 11:00:23711 // Make sure each WebContents is only added once.
712 if (rfh->IsInPrimaryMainFrame()) {
Daniel Cheng982f2b22022-08-25 23:46:16713 frame_observers_.push_back(std::make_unique<FrameObserver>(wc, this));
Miyoung Shinbf4c40c2021-10-21 11:00:23714 }
715 if (IsFindInPageDisabled(rfh))
716 return;
Daniel Cheng982f2b22022-08-25 23:46:16717 AddFrame(rfh, false /* force */);
718 });
paulmeyerc8cb7cb2016-06-07 01:14:19719}
720
721void FindRequestManager::AdvanceQueue(int request_id) {
722 if (find_request_queue_.empty() ||
723 request_id != find_request_queue_.front().id) {
724 return;
725 }
726
727 find_request_queue_.pop();
728 if (!find_request_queue_.empty())
729 FindInternal(find_request_queue_.front());
730}
731
Rakina Zata Amni2ac57502018-08-16 01:44:45732void FindRequestManager::SendFindRequest(const FindRequest& request,
733 RenderFrameHost* rfh) {
paulmeyerbbaacbe2016-08-30 18:04:13734 DCHECK(CheckFrame(rfh));
735 DCHECK(rfh->IsRenderFrameLive());
Miyoung Shinbf4c40c2021-10-21 11:00:23736 DCHECK(rfh->IsActive());
paulmeyerbbaacbe2016-08-30 18:04:13737
Russell Davis8a36226c2020-06-05 17:09:50738 if (request.options->new_session)
paulmeyerbbaacbe2016-08-30 18:04:13739 pending_initial_replies_.insert(rfh);
Russell Davis8a36226c2020-06-05 17:09:50740 else
741 pending_find_next_reply_ = rfh;
paulmeyerbbaacbe2016-08-30 18:04:13742
Rakina Zata Amni2ac57502018-08-16 01:44:45743 static_cast<RenderFrameHostImpl*>(rfh)->GetFindInPage()->Find(
Rakina Zata Amni3f77dff2018-09-08 16:19:43744 request.id, base::UTF16ToUTF8(request.search_text),
745 request.options.Clone());
paulmeyerc0b762b2016-04-13 11:55:17746}
747
paulmeyerbbaacbe2016-08-30 18:04:13748void FindRequestManager::NotifyFindReply(int request_id, bool final_update) {
paulmeyerc0b762b2016-04-13 11:55:17749 if (request_id == kInvalidId) {
Peter Boströmfc7ddc182024-10-31 19:37:21750 NOTREACHED();
paulmeyerc0b762b2016-04-13 11:55:17751 }
752
paulmeyerbbaacbe2016-08-30 18:04:13753 // Ensure that replies are not reported with IDs lower than the ID of the
754 // latest request we have results from.
755 if (request_id < last_reported_id_)
756 request_id = last_reported_id_;
757 else
758 last_reported_id_ = request_id;
759
paulmeyerc0b762b2016-04-13 11:55:17760 contents_->NotifyFindReply(request_id, number_of_matches_, selection_rect_,
761 active_match_ordinal_, final_update);
762}
763
paulmeyerc8cb7cb2016-06-07 01:14:19764RenderFrameHost* FindRequestManager::GetInitialFrame(bool forward) const {
Carlos Caballero6a99dac2021-11-03 10:41:17765 RenderFrameHost* rfh =
766 contents_->GetPrimaryFrameTree().root()->current_frame_host();
paulmeyerc8cb7cb2016-06-07 01:14:19767 if (!forward)
Harkiran Bolaria37e212682021-10-14 19:44:37768 rfh = GetDeepestLastChild(static_cast<RenderFrameHostImpl*>(rfh));
paulmeyerc8cb7cb2016-06-07 01:14:19769
770 return rfh;
771}
772
773RenderFrameHost* FindRequestManager::Traverse(RenderFrameHost* from_rfh,
774 bool forward,
775 bool matches_only,
776 bool wrap) const {
paulmeyerfeafc2d2017-04-25 21:46:40777 DCHECK(from_rfh);
Rakina Zata Amnie6da4982019-02-07 05:40:04778 // If |from_rfh| is being detached, it might already be removed from
779 // its parent's list of children, meaning we can't traverse it correctly.
Harkiran Bolaria37e212682021-10-14 19:44:37780 // We also don't traverse when |from_rfh| is in back-forward cache or is being
781 // prerendered, as we don't allow any updates in these states.
Lei Zhang4ea8c202019-10-16 02:27:59782 auto* from_rfh_impl = static_cast<RenderFrameHostImpl*>(from_rfh);
Sreeja Kamishettyf352e302020-06-10 13:21:24783 if (from_rfh_impl->IsPendingDeletion() ||
Harkiran Bolaria37e212682021-10-14 19:44:37784 from_rfh_impl->IsInBackForwardCache() ||
785 from_rfh_impl->lifecycle_state() ==
786 RenderFrameHostImpl::LifecycleStateImpl::kPrerendering) {
Rakina Zata Amnie6da4982019-02-07 05:40:04787 return nullptr;
Sreeja Kamishettyf352e302020-06-10 13:21:24788 }
789
Harkiran Bolaria37e212682021-10-14 19:44:37790 RenderFrameHostImpl* rfh = from_rfh_impl;
791 RenderFrameHostImpl* last_frame = rfh;
792 while ((rfh = TraverseFrame(rfh, forward, wrap)) != nullptr) {
793 if (!CheckFrame(rfh)) {
Rakina Zata Amnie6da4982019-02-07 05:40:04794 // If we're in the same frame as before, we might got into an infinite
795 // loop.
Harkiran Bolaria37e212682021-10-14 19:44:37796 if (last_frame == rfh)
Rakina Zata Amnie6da4982019-02-07 05:40:04797 break;
Harkiran Bolaria37e212682021-10-14 19:44:37798 last_frame = rfh;
paulmeyerc8cb7cb2016-06-07 01:14:19799 continue;
Rakina Zata Amnie6da4982019-02-07 05:40:04800 }
Harkiran Bolaria37e212682021-10-14 19:44:37801 RenderFrameHost* current_rfh = rfh;
Rakina Zata Amni724f3efe2018-07-23 03:19:46802 if (!matches_only ||
803 find_in_page_clients_.find(current_rfh)->second->number_of_matches() ||
Lei Zhang4ea8c202019-10-16 02:27:59804 base::Contains(pending_initial_replies_, current_rfh)) {
paulmeyerc8cb7cb2016-06-07 01:14:19805 // Note that if there is still a pending reply expected for this frame,
806 // then it may have unaccounted matches and will not be skipped via
807 // |matches_only|.
Harkiran Bolaria37e212682021-10-14 19:44:37808 return rfh;
paulmeyerc8cb7cb2016-06-07 01:14:19809 }
Harkiran Bolaria37e212682021-10-14 19:44:37810 if (wrap && rfh == from_rfh)
paulmeyerc8cb7cb2016-06-07 01:14:19811 return nullptr;
812 }
813
814 return nullptr;
815}
816
paulmeyer3ac612d2016-09-30 19:21:06817void FindRequestManager::AddFrame(RenderFrameHost* rfh, bool force) {
Miyoung Shin0e9b16b2022-10-03 11:05:17818 if (!rfh || !rfh->IsRenderFrameLive() || !rfh->IsActive() ||
819 IsUnattachedGuestView(rfh)) {
paulmeyerc8cb7cb2016-06-07 01:14:19820 return;
Miyoung Shin0e9b16b2022-10-03 11:05:17821 }
paulmeyerc8cb7cb2016-06-07 01:14:19822
paulmeyer3ac612d2016-09-30 19:21:06823 // A frame that is already being searched should not normally be added again.
824 DCHECK(force || !CheckFrame(rfh));
paulmeyerc8cb7cb2016-06-07 01:14:19825
Lei Zhang7c46fde2021-10-18 19:09:22826 DCHECK(!IsFindInPageDisabled(rfh));
827
Miyoung Shinbf4c40c2021-10-21 11:00:23828 find_in_page_clients_[rfh] =
829 CreateFindInPageClient(static_cast<RenderFrameHostImpl*>(rfh));
paulmeyerc8cb7cb2016-06-07 01:14:19830
831 FindRequest request = current_request_;
832 request.id = current_session_id_;
Russell Davis8a36226c2020-06-05 17:09:50833 request.options->new_session = true;
Rakina Zata Amni3f77dff2018-09-08 16:19:43834 request.options->force = force;
Rakina Zata Amni2ac57502018-08-16 01:44:45835 SendFindRequest(request, rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19836}
837
838bool FindRequestManager::CheckFrame(RenderFrameHost* rfh) const {
Alison Gale81f4f2c72024-04-22 19:33:31839 // TODO(crbug.com/40196212): Convert IsFindInPageDisabled to a DCHECK when we
Miyoung Shinbf4c40c2021-10-21 11:00:23840 // replace DidFinishLoad with DidFinishNavigation in FrameObserver.
841 if (!rfh || !base::Contains(find_in_page_clients_, rfh) ||
842 IsFindInPageDisabled(rfh)) {
Lei Zhang7c46fde2021-10-18 19:09:22843 return false;
Miyoung Shinbf4c40c2021-10-21 11:00:23844 }
Lei Zhang7c46fde2021-10-18 19:09:22845
Miyoung Shinbf4c40c2021-10-21 11:00:23846 DCHECK(rfh->IsActive());
Lei Zhang7c46fde2021-10-18 19:09:22847 return true;
paulmeyerc8cb7cb2016-06-07 01:14:19848}
849
850void FindRequestManager::UpdateActiveMatchOrdinal() {
851 active_match_ordinal_ = 0;
852
853 if (!active_frame_ || !relative_active_match_ordinal_) {
854 DCHECK(!active_frame_ && !relative_active_match_ordinal_);
855 return;
856 }
857
858 // Traverse the frame tree backwards (in search order) and count all of the
859 // matches in frames before the frame with the active match, in order to
860 // determine the overall active match ordinal.
861 RenderFrameHost* frame = active_frame_;
862 while ((frame = Traverse(frame,
863 false /* forward */,
864 true /* matches_only */,
865 false /* wrap */)) != nullptr) {
Rakina Zata Amni724f3efe2018-07-23 03:19:46866 active_match_ordinal_ += find_in_page_clients_[frame]->number_of_matches();
paulmeyerc8cb7cb2016-06-07 01:14:19867 }
868 active_match_ordinal_ += relative_active_match_ordinal_;
869}
870
paulmeyerbbaacbe2016-08-30 18:04:13871void FindRequestManager::FinalUpdateReceived(int request_id,
872 RenderFrameHost* rfh) {
paulmeyerc8cb7cb2016-06-07 01:14:19873 if (!number_of_matches_ ||
Russell Davisdd2b6782020-09-21 23:55:56874 !current_request_.options->find_match ||
paulmeyerbbaacbe2016-08-30 18:04:13875 (active_match_ordinal_ && !pending_active_match_ordinal_) ||
paulmeyer3ac612d2016-09-30 19:21:06876 pending_find_next_reply_) {
paulmeyerbbaacbe2016-08-30 18:04:13877 // All the find results for |request_id| are in and ready to report. Note
878 // that |final_update| will be set to false if there are still pending
879 // replies expected from the initial find request.
880 NotifyFindReply(request_id,
881 pending_initial_replies_.empty() /* final_update */);
paulmeyerc8cb7cb2016-06-07 01:14:19882 AdvanceQueue(request_id);
883 return;
884 }
885
886 // There are matches, but no active match was returned, so another find next
887 // request must be sent.
888
889 RenderFrameHost* target_rfh;
Rakina Zata Amni3f77dff2018-09-08 16:19:43890 if (request_id == current_request_.id &&
Russell Davis8a36226c2020-06-05 17:09:50891 !current_request_.options->new_session) {
paulmeyerc8cb7cb2016-06-07 01:14:19892 // If this was a find next operation, then the active match will be in the
893 // next frame with matches after this one.
Rakina Zata Amni3f77dff2018-09-08 16:19:43894 target_rfh = Traverse(rfh, current_request_.options->forward,
895 true /* matches_only */, true /* wrap */);
paulmeyerfeafc2d2017-04-25 21:46:40896 } else if ((target_rfh =
897 contents_->GetFocusedWebContents()->GetFocusedFrame()) !=
898 nullptr) {
paulmeyerc8cb7cb2016-06-07 01:14:19899 // Otherwise, if there is a focused frame, then the active match will be in
900 // the next frame with matches after that one.
Rakina Zata Amni3f77dff2018-09-08 16:19:43901 target_rfh = Traverse(target_rfh, current_request_.options->forward,
902 true /* matches_only */, true /* wrap */);
paulmeyerc8cb7cb2016-06-07 01:14:19903 } else {
904 // Otherwise, the first frame with matches will have the active match.
Rakina Zata Amni3f77dff2018-09-08 16:19:43905 target_rfh = GetInitialFrame(current_request_.options->forward);
Rakina Zata Amni724f3efe2018-07-23 03:19:46906 if (!CheckFrame(target_rfh) ||
907 !find_in_page_clients_[target_rfh]->number_of_matches()) {
Rakina Zata Amni3f77dff2018-09-08 16:19:43908 target_rfh = Traverse(target_rfh, current_request_.options->forward,
909 true /* matches_only */, false /* wrap */);
paulmeyerc8cb7cb2016-06-07 01:14:19910 }
911 }
Rakina Zata Amni16196122018-11-14 02:14:00912 if (!target_rfh) {
913 // Sometimes when the WebContents is deleted/navigated, we got into this
914 // situation. We don't care about this WebContents anyways so it's ok to
915 // just not ask for the active match and return immediately.
916 // TODO(rakina): Understand what leads to this situation.
917 // See: https://crbug.com/884679.
918 return;
919 }
paulmeyerc8cb7cb2016-06-07 01:14:19920
921 // Forward the find reply without |final_update| set because the active match
922 // has not yet been found.
923 NotifyFindReply(request_id, false /* final_update */);
924
Russell Davis8a36226c2020-06-05 17:09:50925 current_request_.options->new_session = false;
Rakina Zata Amni2ac57502018-08-16 01:44:45926 SendFindRequest(current_request_, target_rfh);
paulmeyerc8cb7cb2016-06-07 01:14:19927}
928
Miyoung Shinbf4c40c2021-10-21 11:00:23929std::unique_ptr<FindInPageClient> FindRequestManager::CreateFindInPageClient(
930 RenderFrameHostImpl* rfh) {
931 if (create_find_in_page_client_for_testing_)
932 return create_find_in_page_client_for_testing_(this, rfh);
933 return std::make_unique<FindInPageClient>(this, rfh);
934}
935
Xiaohan Wang1ecfd002022-01-19 22:33:10936#if BUILDFLAG(IS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19937void FindRequestManager::RemoveNearestFindResultPendingReply(
938 RenderFrameHost* rfh) {
939 auto it = activate_.pending_replies.find(rfh);
940 if (it == activate_.pending_replies.end())
941 return;
942
943 activate_.pending_replies.erase(it);
944 if (activate_.pending_replies.empty() &&
945 CheckFrame(activate_.nearest_frame)) {
Rakina Zata Amni724f3efe2018-07-23 03:19:46946 const auto client_it = find_in_page_clients_.find(activate_.nearest_frame);
947 if (client_it != find_in_page_clients_.end())
948 client_it->second->ActivateNearestFindResult(current_session_id_,
949 activate_.point);
paulmeyerc8cb7cb2016-06-07 01:14:19950 }
951}
952
953void FindRequestManager::RemoveFindMatchRectsPendingReply(
954 RenderFrameHost* rfh) {
955 auto it = match_rects_.pending_replies.find(rfh);
956 if (it == match_rects_.pending_replies.end())
957 return;
958
959 match_rects_.pending_replies.erase(it);
Lei Zhang4ea8c202019-10-16 02:27:59960 if (!match_rects_.pending_replies.empty())
961 return;
paulmeyerc8cb7cb2016-06-07 01:14:19962
Lei Zhang4ea8c202019-10-16 02:27:59963 // All replies are in.
964 std::vector<gfx::RectF> aggregate_rects;
965 if (match_rects_.request_version != match_rects_.known_version) {
966 // Request version is stale, so aggregate and report the newer find
967 // match rects. The rects should be aggregated in search order.
968 for (RenderFrameHost* frame = GetInitialFrame(true /* forward */); frame;
969 frame = Traverse(frame, true /* forward */, true /* matches_only */,
970 false /* wrap */)) {
971 auto frame_it = match_rects_.frame_rects.find(frame);
972 if (frame_it == match_rects_.frame_rects.end())
973 continue;
974
975 std::vector<gfx::RectF>& frame_rects = frame_it->second.rects;
976 aggregate_rects.insert(aggregate_rects.end(), frame_rects.begin(),
977 frame_rects.end());
paulmeyerc8cb7cb2016-06-07 01:14:19978 }
paulmeyerc8cb7cb2016-06-07 01:14:19979 }
Lei Zhang4ea8c202019-10-16 02:27:59980 contents_->NotifyFindMatchRectsReply(
981 match_rects_.known_version, aggregate_rects, match_rects_.active_rect);
paulmeyerc0b762b2016-04-13 11:55:17982}
Xiaohan Wang1ecfd002022-01-19 22:33:10983#endif // BUILDFLAG(IS_ANDROID)
paulmeyerc0b762b2016-04-13 11:55:17984
985} // namespace content