Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 2 | // 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 Kasting | 1557e5f | 2025-01-28 01:14:08 | [diff] [blame] | 7 | #include <algorithm> |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 8 | #include <utility> |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 9 | |
Lei Zhang | d4f2c7ad | 2021-05-13 20:10:12 | [diff] [blame] | 10 | #include "base/containers/contains.h" |
Brett Wilson | cc8623d | 2017-09-12 03:28:10 | [diff] [blame] | 11 | #include "base/containers/queue.h" |
Avi Drissman | adac2199 | 2023-01-11 23:46:39 | [diff] [blame] | 12 | #include "base/functional/bind.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 13 | #include "base/memory/raw_ptr.h" |
Daniel Cheng | 4f1c533 | 2025-06-19 19:37:21 | [diff] [blame] | 14 | #include "base/notimplemented.h" |
Sean Maher | 5b9af51f | 2022-11-21 15:32:47 | [diff] [blame] | 15 | #include "base/task/single_thread_task_runner.h" |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 16 | #include "build/build_config.h" |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 17 | #include "content/browser/find_in_page_client.h" |
danakj | e94b7c84 | 2020-09-16 18:47:43 | [diff] [blame] | 18 | #include "content/browser/renderer_host/render_frame_host_impl.h" |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 19 | #include "content/browser/web_contents/web_contents_impl.h" |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 20 | #include "content/public/browser/content_browser_client.h" |
Christian Dullweber | 5e66727b | 2021-07-02 13:23:15 | [diff] [blame] | 21 | #include "content/public/browser/render_frame_host.h" |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 22 | #include "content/public/common/content_client.h" |
Kevin McNee | 2585e73 | 2024-10-28 22:11:19 | [diff] [blame] | 23 | #include "content/public/common/content_features.h" |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 24 | |
| 25 | namespace content { |
| 26 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 27 | namespace { |
| 28 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 29 | // The following functions allow traversal over all RenderFrameHosts, including |
Jeremy Roman | c0c69be | 2023-11-21 19:14:52 | [diff] [blame] | 30 | // those across WebContentses. |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 31 | // |
W. James MacLean | 2539adb3 | 2019-12-13 00:40:44 | [diff] [blame] | 32 | // An inner WebContents may be embedded in an outer WebContents via an inner |
| 33 | // WebContentsTreeNode of the outer WebContents's WebContentsTreeNode. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 34 | std::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 Amni | 3d10bb3c | 2019-04-19 03:12:07 | [diff] [blame] | 38 | if (auto* contents = static_cast<WebContentsImpl*>( |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 39 | WebContentsImpl::FromOuterFrameTreeNode(rfh->child_at(i)))) { |
Jeremy Roman | c0c69be | 2023-11-21 19:14:52 | [diff] [blame] | 40 | // 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 Amni | 3d10bb3c | 2019-04-19 03:12:07 | [diff] [blame] | 44 | } else { |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 45 | children.push_back(rfh->child_at(i)->current_frame_host()); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 46 | } |
| 47 | } |
| 48 | |
| 49 | return children; |
| 50 | } |
| 51 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 52 | // Returns the first child RenderFrameHostImpl under |rfh|, if |rfh| has a |
| 53 | // child, or nullptr otherwise. |
| 54 | RenderFrameHostImpl* GetFirstChild(RenderFrameHostImpl* rfh) { |
| 55 | auto children = GetChildren(rfh); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 56 | if (!children.empty()) |
| 57 | return children.front(); |
| 58 | return nullptr; |
| 59 | } |
| 60 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 61 | // Returns the last child RenderFrameHostImpl under |rfh|, if |rfh| has a |
| 62 | // child, or nullptr otherwise. |
| 63 | RenderFrameHostImpl* GetLastChild(RenderFrameHostImpl* rfh) { |
| 64 | auto children = GetChildren(rfh); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 65 | if (!children.empty()) |
| 66 | return children.back(); |
| 67 | return nullptr; |
| 68 | } |
| 69 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 70 | // Returns the deepest last child frame under |rfh| in the frame tree. |
| 71 | RenderFrameHostImpl* GetDeepestLastChild(RenderFrameHostImpl* rfh) { |
| 72 | while (RenderFrameHostImpl* last_child = GetLastChild(rfh)) |
| 73 | rfh = last_child; |
| 74 | return rfh; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 75 | } |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 76 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 77 | // Returns the parent RenderFrameHost of |rfh|, if |rfh| has a parent, or |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 78 | // nullptr otherwise. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 79 | RenderFrameHostImpl* GetAncestor(RenderFrameHostImpl* rfh) { |
| 80 | if (!rfh) |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 81 | return nullptr; |
Rakina Zata Amni | 3d10bb3c | 2019-04-19 03:12:07 | [diff] [blame] | 82 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 83 | return rfh->GetParentOrOuterDocumentOrEmbedder(); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 84 | } |
| 85 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 86 | // Returns the previous sibling RenderFrameHostImpl of |rfh|, if one exists, |
| 87 | // or nullptr otherwise. |
| 88 | RenderFrameHostImpl* GetPreviousSibling(RenderFrameHostImpl* rfh) { |
| 89 | if (rfh->PreviousSibling()) { |
| 90 | return rfh->PreviousSibling()->current_frame_host(); |
| 91 | } |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 92 | |
| 93 | // The previous sibling may be in another WebContents. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 94 | if (RenderFrameHostImpl* parent = GetAncestor(rfh)) { |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 95 | auto children = GetChildren(parent); |
Peter Kasting | 1557e5f | 2025-01-28 01:14:08 | [diff] [blame] | 96 | auto it = std::ranges::find(children, rfh); |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 97 | // It is odd that this rfh may not be a child of its parent, but this is |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 98 | // 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 Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 107 | // Returns the next sibling RenderFrameHostImpl of |rfh|, if one exists, or |
| 108 | // nullptr otherwise. |
| 109 | RenderFrameHostImpl* GetNextSibling(RenderFrameHostImpl* rfh) { |
| 110 | if (rfh->NextSibling()) |
| 111 | return rfh->NextSibling()->current_frame_host(); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 112 | |
| 113 | // The next sibling may be in another WebContents. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 114 | if (RenderFrameHostImpl* parent = GetAncestor(rfh)) { |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 115 | auto children = GetChildren(parent); |
Peter Kasting | 1557e5f | 2025-01-28 01:14:08 | [diff] [blame] | 116 | auto it = std::ranges::find(children, rfh); |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 117 | // 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()". |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 120 | if (it != children.end() && ++it != children.end()) |
| 121 | return *it; |
| 122 | } |
| 123 | |
| 124 | return nullptr; |
| 125 | } |
| 126 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 127 | // 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(). |
| 132 | RenderFrameHostImpl* TraverseNext(RenderFrameHostImpl* rfh, bool wrap) { |
| 133 | if (RenderFrameHostImpl* first_child = GetFirstChild(rfh)) |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 134 | return first_child; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 135 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 136 | RenderFrameHostImpl* sibling = GetNextSibling(rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 137 | while (!sibling) { |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 138 | RenderFrameHostImpl* parent = GetAncestor(rfh); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 139 | if (!parent) |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 140 | return wrap ? rfh : nullptr; |
| 141 | rfh = parent; |
| 142 | sibling = GetNextSibling(rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 143 | } |
| 144 | return sibling; |
| 145 | } |
| 146 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 147 | // 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(). |
| 152 | RenderFrameHostImpl* TraversePrevious(RenderFrameHostImpl* rfh, bool wrap) { |
| 153 | if (RenderFrameHostImpl* previous_sibling = GetPreviousSibling(rfh)) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 154 | return GetDeepestLastChild(previous_sibling); |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 155 | if (RenderFrameHostImpl* parent = GetAncestor(rfh)) |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 156 | return parent; |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 157 | return wrap ? GetDeepestLastChild(rfh) : nullptr; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 158 | } |
| 159 | |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 160 | // The same as either TraverseNext() or TraversePrevious(), depending on |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 161 | // |forward|. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 162 | RenderFrameHostImpl* TraverseFrame(RenderFrameHostImpl* rfh, |
| 163 | bool forward, |
| 164 | bool wrap) { |
| 165 | return forward ? TraverseNext(rfh, wrap) : TraversePrevious(rfh, wrap); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 166 | } |
| 167 | |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 168 | bool IsFindInPageDisabled(RenderFrameHost* rfh) { |
| 169 | return rfh && GetContentClient()->browser()->IsFindInPageDisabledForOrigin( |
| 170 | rfh->GetLastCommittedOrigin()); |
| 171 | } |
| 172 | |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 173 | bool IsUnattachedGuestView(RenderFrameHost* rfh) { |
Kevin McNee | 2585e73 | 2024-10-28 22:11:19 | [diff] [blame] | 174 | if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) { |
| 175 | NOTIMPLEMENTED(); |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 176 | return false; |
Kevin McNee | 2585e73 | 2024-10-28 22:11:19 | [diff] [blame] | 177 | } else { |
| 178 | WebContentsImpl* web_contents = |
| 179 | static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh)); |
| 180 | if (!web_contents->IsGuest()) { |
| 181 | return false; |
| 182 | } |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 183 | |
Kevin McNee | 2585e73 | 2024-10-28 22:11:19 | [diff] [blame] | 184 | return !web_contents->GetOuterWebContents(); |
| 185 | } |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 186 | } |
| 187 | |
Joey Arhar | cac45bf | 2022-01-07 21:59:31 | [diff] [blame] | 188 | // kMinKeystrokesWithoutDelay should be high enough that script in the page |
| 189 | // can't provide every possible search result at the same time. |
| 190 | constexpr 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. |
| 196 | constexpr int kDelayMs = 400; |
| 197 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 198 | } // namespace |
| 199 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 200 | // Observes searched WebContentses for RenderFrameHost state updates, including |
| 201 | // deletion and loads. |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 202 | class FindRequestManager::FrameObserver : public WebContentsObserver { |
| 203 | public: |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 204 | FrameObserver(WebContents* web_contents, FindRequestManager* manager) |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 205 | : WebContentsObserver(web_contents), manager_(manager) {} |
| 206 | |
Peter Boström | 828b902 | 2021-09-21 02:28:43 | [diff] [blame] | 207 | FrameObserver(const FrameObserver&) = delete; |
| 208 | FrameObserver& operator=(const FrameObserver&) = delete; |
| 209 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 210 | ~FrameObserver() override = default; |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 211 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 212 | 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 | |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 239 | void DidFinishLoad(RenderFrameHost* rfh, const GURL& validated_url) override { |
| 240 | if (manager_->current_session_id_ == kInvalidId) |
| 241 | return; |
| 242 | |
| 243 | manager_->RemoveFrame(rfh); |
Carlos Caballero | 301e175 | 2021-03-25 13:20:58 | [diff] [blame] | 244 | // Make sure RenderFrameDeleted will be called to clean up |
Nasko Oskov | 8b04f40 | 2022-05-26 23:29:56 | [diff] [blame] | 245 | DCHECK(rfh->IsRenderFrameLive()); |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 246 | |
| 247 | if (IsFindInPageDisabled(rfh)) |
| 248 | return; |
| 249 | |
| 250 | manager_->AddFrame(rfh, /*force=*/true); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 251 | } |
| 252 | |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 253 | private: |
| 254 | // The FindRequestManager that owns this FrameObserver. |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 255 | const raw_ptr<FindRequestManager> manager_; |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 256 | }; |
| 257 | |
Joey Arhar | 0574fb75 | 2022-03-29 00:45:16 | [diff] [blame] | 258 | bool FindRequestManager::RunDelayedFindTaskForTesting() { |
| 259 | if (!delayed_find_task_.IsCancelled()) { |
| 260 | delayed_find_task_.callback().Run(); |
| 261 | return true; |
| 262 | } |
| 263 | return false; |
| 264 | } |
| 265 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 266 | FindRequestManager::FindRequest::FindRequest() = default; |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 267 | |
| 268 | FindRequestManager::FindRequest::FindRequest( |
| 269 | int id, |
Jan Wilken Dörrie | aace0cfef | 2021-03-11 22:01:58 | [diff] [blame] | 270 | const std::u16string& search_text, |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 271 | blink::mojom::FindOptionsPtr options) |
| 272 | : id(id), search_text(search_text), options(std::move(options)) {} |
| 273 | |
| 274 | FindRequestManager::FindRequest::FindRequest(const FindRequest& request) |
| 275 | : id(request.id), |
| 276 | search_text(request.search_text), |
| 277 | options(request.options.Clone()) {} |
| 278 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 279 | FindRequestManager::FindRequest::~FindRequest() = default; |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 280 | |
| 281 | FindRequestManager::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 Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 289 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 290 | FindRequestManager::ActivateNearestFindResultState:: |
| 291 | ActivateNearestFindResultState() = default; |
| 292 | FindRequestManager::ActivateNearestFindResultState:: |
Rakina Zata Amni | 57bec36 | 2018-05-22 06:05:10 | [diff] [blame] | 293 | ActivateNearestFindResultState(float x, float y) |
| 294 | : current_request_id(GetNextID()), point(x, y) {} |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 295 | FindRequestManager::ActivateNearestFindResultState:: |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 296 | ~ActivateNearestFindResultState() = default; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 297 | |
Devon Loehr | 11aff82 | 2025-05-30 16:25:28 | [diff] [blame] | 298 | int FindRequestManager::ActivateNearestFindResultState::GetNextID() { |
| 299 | static int next_id = 0; |
| 300 | return next_id++; |
| 301 | } |
| 302 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 303 | FindRequestManager::FrameRects::FrameRects() = default; |
| 304 | FindRequestManager::FrameRects::FrameRects(const std::vector<gfx::RectF>& rects, |
| 305 | int version) |
| 306 | : rects(rects), version(version) {} |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 307 | FindRequestManager::FrameRects::~FrameRects() = default; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 308 | |
| 309 | FindRequestManager::FindMatchRectsState::FindMatchRectsState() = default; |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 310 | FindRequestManager::FindMatchRectsState::~FindMatchRectsState() = default; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 311 | #endif |
| 312 | |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 313 | // static |
| 314 | const int FindRequestManager::kInvalidId = -1; |
| 315 | |
| 316 | FindRequestManager::FindRequestManager(WebContentsImpl* web_contents) |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 317 | : contents_(web_contents) {} |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 318 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 319 | FindRequestManager::~FindRequestManager() = default; |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 320 | |
| 321 | void FindRequestManager::Find(int request_id, |
Jan Wilken Dörrie | aace0cfef | 2021-03-11 22:01:58 | [diff] [blame] | 322 | const std::u16string& search_text, |
Joey Arhar | cac45bf | 2022-01-07 21:59:31 | [diff] [blame] | 323 | blink::mojom::FindOptionsPtr options, |
| 324 | bool skip_delay) { |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 325 | // 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 Arhar | cac45bf | 2022-01-07 21:59:31 | [diff] [blame] | 331 | 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 Maher | 5b9af51f | 2022-11-21 15:32:47 | [diff] [blame] | 352 | base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
Joey Arhar | cac45bf | 2022-01-07 21:59:31 | [diff] [blame] | 353 | 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 | |
| 363 | void FindRequestManager::EmitFindRequest(int request_id, |
| 364 | const std::u16string& search_text, |
| 365 | blink::mojom::FindOptionsPtr options) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 366 | // If this is a new find session, clear any queued requests from last session. |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 367 | if (options->new_session) |
Brett Wilson | cc8623d | 2017-09-12 03:28:10 | [diff] [blame] | 368 | find_request_queue_ = base::queue<FindRequest>(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 369 | |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 370 | find_request_queue_.emplace(request_id, search_text, std::move(options)); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 371 | if (find_request_queue_.size() == 1) |
| 372 | FindInternal(find_request_queue_.front()); |
Artur | 3bb150aa | 2022-10-18 02:36:05 | [diff] [blame] | 373 | if (request_id == current_session_id_) |
| 374 | find_request_queue_.pop(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 375 | } |
| 376 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 377 | void FindRequestManager::ForEachAddedFindInPageRenderFrameHost( |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 378 | base::FunctionRef<void(RenderFrameHostImpl*)> func_ref) { |
Peter Kasting | d2876a2 | 2024-12-03 01:12:55 | [diff] [blame] | 379 | contents_->GetPrimaryMainFrame()->ForEachRenderFrameHostImpl( |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 380 | [this, func_ref](RenderFrameHostImpl* rfh) { |
| 381 | if (!CheckFrame(rfh)) |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 382 | return; |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 383 | DCHECK(rfh->IsRenderFrameLive()); |
| 384 | DCHECK(rfh->IsActive()); |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 385 | func_ref(rfh); |
| 386 | }); |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 387 | } |
| 388 | |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 389 | void FindRequestManager::StopFinding(StopFindAction action) { |
Joey Arhar | 0574fb75 | 2022-03-29 00:45:16 | [diff] [blame] | 390 | // Cancel any delayed find-in-page requests |
| 391 | delayed_find_task_.Cancel(); |
| 392 | |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 393 | 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 | }); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 398 | |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 399 | current_session_id_ = kInvalidId; |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 400 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 401 | // 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 |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 406 | } |
| 407 | |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 408 | bool FindRequestManager::ShouldIgnoreReply(RenderFrameHostImpl* rfh, |
| 409 | int request_id) { |
Makoto Shimazu | fc0e1ea | 2018-08-14 08:12:52 | [diff] [blame] | 410 | // Ignore stale replies from abandoned find sessions or dead frames. |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 411 | return current_session_id_ == kInvalidId || |
| 412 | request_id < current_session_id_ || !CheckFrame(rfh); |
Makoto Shimazu | fc0e1ea | 2018-08-14 08:12:52 | [diff] [blame] | 413 | } |
| 414 | |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 415 | void FindRequestManager::HandleFinalUpdateForFrame(RenderFrameHostImpl* rfh, |
| 416 | int request_id) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 417 | // This is the final update for this frame for the current find operation. |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 418 | pending_initial_replies_.erase(rfh); |
| 419 | if (request_id == current_session_id_ && !pending_initial_replies_.empty()) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 420 | NotifyFindReply(request_id, false /* final_update */); |
| 421 | return; |
| 422 | } |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 423 | |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 424 | // This is the final update for all frames for the current find operation. |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 425 | if (request_id == current_request_.id && request_id != current_session_id_) { |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 426 | DCHECK(!current_request_.options->new_session); |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 427 | DCHECK_EQ(pending_find_next_reply_, rfh); |
| 428 | pending_find_next_reply_ = nullptr; |
| 429 | } |
| 430 | |
| 431 | FinalUpdateReceived(request_id, rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 432 | } |
| 433 | |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 434 | void 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 | |
| 453 | void FindRequestManager::SetActiveMatchRect( |
| 454 | const gfx::Rect& active_match_rect) { |
| 455 | selection_rect_ = active_match_rect; |
| 456 | } |
| 457 | |
| 458 | void FindRequestManager::SetActiveMatchOrdinal(RenderFrameHostImpl* rfh, |
| 459 | int request_id, |
| 460 | int active_match_ordinal) { |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 461 | 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 Amni | cca4889e | 2021-09-28 23:25:55 | [diff] [blame] | 468 | // Do not focus inactive RenderFrameHost. |
| 469 | if (!rfh->IsActive()) |
| 470 | return; |
Rakina Zata Amni | 77b7a4f0 | 2018-08-28 16:40:55 | [diff] [blame] | 471 | web_contents->SetFocusedFrame(rfh->frame_tree_node(), |
Sharon Yang | efe5263 | 2022-03-08 23:06:06 | [diff] [blame] | 472 | rfh->GetSiteInstance()->group()); |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 473 | } |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 474 | 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 Amni | 57bec36 | 2018-05-22 06:05:10 | [diff] [blame] | 492 | } |
| 493 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 494 | void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 495 | // If matches are counted for the frame that is being removed, decrement the |
| 496 | // match total before erasing that entry. |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 497 | 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 Amni | 2a6bc4b | 2023-11-16 00:04:18 | [diff] [blame] | 501 | } else { |
| 502 | // If there's no FindInPageClient for `rfh`, the state related to it must |
| 503 | // have been cleared already. |
| 504 | return; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 505 | } |
| 506 | |
Gyuyoung Kim | a855ff5d | 2021-11-19 07:24:35 | [diff] [blame] | 507 | // 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 Levin | 34a865b | 2020-01-08 21:54:29 | [diff] [blame] | 513 | find_request_queue_ = base::queue<FindRequest>(); |
| 514 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 515 | // 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; |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 519 | selection_rect_ = gfx::Rect(); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 520 | } |
| 521 | UpdateActiveMatchOrdinal(); |
| 522 | |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 523 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 524 | // 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 Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 531 | if (match_rects_.frame_rects.erase(rfh) != 0) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 532 | ++match_rects_.known_version; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 533 | |
| 534 | // A reply should not be expected from the removed frame. |
| 535 | RemoveNearestFindResultPendingReply(rfh); |
| 536 | RemoveFindMatchRectsPendingReply(rfh); |
| 537 | #endif |
| 538 | |
Rakina Zata Amni | 2a6bc4b | 2023-11-16 00:04:18 | [diff] [blame] | 539 | 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 | |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 554 | // If no pending find replies are expected for the removed frame, then just |
| 555 | // report the updated results. |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 556 | if (!base::Contains(pending_initial_replies_, rfh) && |
| 557 | pending_find_next_reply_ != rfh) { |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 558 | bool final_update = |
| 559 | pending_initial_replies_.empty() && !pending_find_next_reply_; |
| 560 | NotifyFindReply(current_session_id_, final_update); |
| 561 | return; |
| 562 | } |
| 563 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 564 | if (pending_initial_replies_.erase(rfh) != 0) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 565 | // A reply should not be expected from the removed frame. |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 566 | if (pending_initial_replies_.empty()) { |
| 567 | FinalUpdateReceived(current_session_id_, rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 568 | } |
| 569 | } |
| 570 | |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 571 | 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 | } |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 576 | } |
| 577 | |
Rakina Zata Amni | acf4049 | 2018-05-08 22:59:33 | [diff] [blame] | 578 | void FindRequestManager::ClearActiveFindMatch() { |
Rakina Zata Amni | 174ef71 | 2018-05-14 12:45:20 | [diff] [blame] | 579 | active_frame_->GetFindInPage()->ClearActiveFindMatch(); |
Rakina Zata Amni | acf4049 | 2018-05-08 22:59:33 | [diff] [blame] | 580 | } |
| 581 | |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 582 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 583 | void FindRequestManager::ActivateNearestFindResult(float x, float y) { |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 584 | if (current_session_id_ == kInvalidId) |
| 585 | return; |
| 586 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 587 | 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 Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 591 | 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 | }); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 601 | } |
| 602 | |
Rakina Zata Amni | 57bec36 | 2018-05-22 06:05:10 | [diff] [blame] | 603 | void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHostImpl* rfh, |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 604 | int request_id, |
| 605 | float distance) { |
| 606 | if (request_id != activate_.current_request_id || |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 607 | !base::Contains(activate_.pending_replies, rfh)) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 608 | 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); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 618 | } |
| 619 | |
| 620 | void FindRequestManager::RequestFindMatchRects(int current_version) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 621 | match_rects_.pending_replies.clear(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 622 | match_rects_.request_version = current_version; |
WangHui | 66701ae | 2020-11-09 20:01:29 | [diff] [blame] | 623 | match_rects_.active_rect = gfx::RectF(); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 624 | |
| 625 | // Request the latest find match rects from each frame. |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 626 | 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 | }); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 637 | } |
| 638 | |
| 639 | void FindRequestManager::OnFindMatchRectsReply( |
| 640 | RenderFrameHost* rfh, |
| 641 | int version, |
| 642 | const std::vector<gfx::RectF>& rects, |
| 643 | const gfx::RectF& active_rect) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 644 | 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); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 653 | } |
| 654 | #endif |
| 655 | |
| 656 | void FindRequestManager::Reset(const FindRequest& initial_request) { |
| 657 | current_session_id_ = initial_request.id; |
| 658 | current_request_ = initial_request; |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 659 | pending_initial_replies_.clear(); |
| 660 | pending_find_next_reply_ = nullptr; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 661 | pending_active_match_ordinal_ = true; |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 662 | find_in_page_clients_.clear(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 663 | number_of_matches_ = 0; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 664 | active_frame_ = nullptr; |
| 665 | relative_active_match_ordinal_ = 0; |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 666 | active_match_ordinal_ = 0; |
| 667 | selection_rect_ = gfx::Rect(); |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 668 | last_reported_id_ = kInvalidId; |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 669 | frame_observers_.clear(); |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 670 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 671 | activate_ = ActivateNearestFindResultState(); |
| 672 | match_rects_.pending_replies.clear(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 673 | #endif |
| 674 | } |
| 675 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 676 | void FindRequestManager::FindInternal(const FindRequest& request) { |
| 677 | DCHECK_GT(request.id, current_request_.id); |
| 678 | DCHECK_GT(request.id, current_session_id_); |
| 679 | |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 680 | if (!request.options->new_session) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 681 | // 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. |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 690 | RenderFrameHost* target_rfh = |
| 691 | contents_->GetFocusedWebContents()->GetFocusedFrame(); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 692 | if (!target_rfh || !CheckFrame(target_rfh)) |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 693 | target_rfh = GetInitialFrame(request.options->forward); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 694 | |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 695 | SendFindRequest(request, target_rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 696 | current_request_ = request; |
| 697 | pending_active_match_ordinal_ = true; |
| 698 | return; |
| 699 | } |
| 700 | |
| 701 | // This is an initial find operation. |
| 702 | Reset(request); |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 703 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 704 | // Add and observe eligible RFHs in the WebContents. And, use |
Peter Kasting | d2876a2 | 2024-12-03 01:12:55 | [diff] [blame] | 705 | // ForEachRenderFrameHostImpl instead of ForEachAddedFindInPageRenderFrameHost |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 706 | // because that calls CheckFrame() which will only be true if we've called |
| 707 | // AddFrame() for the frame. |
Peter Kasting | d2876a2 | 2024-12-03 01:12:55 | [diff] [blame] | 708 | contents_->GetPrimaryMainFrame()->ForEachRenderFrameHostImpl( |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 709 | [this](RenderFrameHostImpl* rfh) { |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 710 | auto* wc = WebContents::FromRenderFrameHost(rfh); |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 711 | // Make sure each WebContents is only added once. |
| 712 | if (rfh->IsInPrimaryMainFrame()) { |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 713 | frame_observers_.push_back(std::make_unique<FrameObserver>(wc, this)); |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 714 | } |
| 715 | if (IsFindInPageDisabled(rfh)) |
| 716 | return; |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 717 | AddFrame(rfh, false /* force */); |
| 718 | }); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 719 | } |
| 720 | |
| 721 | void 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 Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 732 | void FindRequestManager::SendFindRequest(const FindRequest& request, |
| 733 | RenderFrameHost* rfh) { |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 734 | DCHECK(CheckFrame(rfh)); |
| 735 | DCHECK(rfh->IsRenderFrameLive()); |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 736 | DCHECK(rfh->IsActive()); |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 737 | |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 738 | if (request.options->new_session) |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 739 | pending_initial_replies_.insert(rfh); |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 740 | else |
| 741 | pending_find_next_reply_ = rfh; |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 742 | |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 743 | static_cast<RenderFrameHostImpl*>(rfh)->GetFindInPage()->Find( |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 744 | request.id, base::UTF16ToUTF8(request.search_text), |
| 745 | request.options.Clone()); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 746 | } |
| 747 | |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 748 | void FindRequestManager::NotifyFindReply(int request_id, bool final_update) { |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 749 | if (request_id == kInvalidId) { |
Peter Boström | fc7ddc18 | 2024-10-31 19:37:21 | [diff] [blame] | 750 | NOTREACHED(); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 751 | } |
| 752 | |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 753 | // 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 | |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 760 | contents_->NotifyFindReply(request_id, number_of_matches_, selection_rect_, |
| 761 | active_match_ordinal_, final_update); |
| 762 | } |
| 763 | |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 764 | RenderFrameHost* FindRequestManager::GetInitialFrame(bool forward) const { |
Carlos Caballero | 6a99dac | 2021-11-03 10:41:17 | [diff] [blame] | 765 | RenderFrameHost* rfh = |
| 766 | contents_->GetPrimaryFrameTree().root()->current_frame_host(); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 767 | if (!forward) |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 768 | rfh = GetDeepestLastChild(static_cast<RenderFrameHostImpl*>(rfh)); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 769 | |
| 770 | return rfh; |
| 771 | } |
| 772 | |
| 773 | RenderFrameHost* FindRequestManager::Traverse(RenderFrameHost* from_rfh, |
| 774 | bool forward, |
| 775 | bool matches_only, |
| 776 | bool wrap) const { |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 777 | DCHECK(from_rfh); |
Rakina Zata Amni | e6da498 | 2019-02-07 05:40:04 | [diff] [blame] | 778 | // 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 Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 780 | // 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 Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 782 | auto* from_rfh_impl = static_cast<RenderFrameHostImpl*>(from_rfh); |
Sreeja Kamishetty | f352e30 | 2020-06-10 13:21:24 | [diff] [blame] | 783 | if (from_rfh_impl->IsPendingDeletion() || |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 784 | from_rfh_impl->IsInBackForwardCache() || |
| 785 | from_rfh_impl->lifecycle_state() == |
| 786 | RenderFrameHostImpl::LifecycleStateImpl::kPrerendering) { |
Rakina Zata Amni | e6da498 | 2019-02-07 05:40:04 | [diff] [blame] | 787 | return nullptr; |
Sreeja Kamishetty | f352e30 | 2020-06-10 13:21:24 | [diff] [blame] | 788 | } |
| 789 | |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 790 | RenderFrameHostImpl* rfh = from_rfh_impl; |
| 791 | RenderFrameHostImpl* last_frame = rfh; |
| 792 | while ((rfh = TraverseFrame(rfh, forward, wrap)) != nullptr) { |
| 793 | if (!CheckFrame(rfh)) { |
Rakina Zata Amni | e6da498 | 2019-02-07 05:40:04 | [diff] [blame] | 794 | // If we're in the same frame as before, we might got into an infinite |
| 795 | // loop. |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 796 | if (last_frame == rfh) |
Rakina Zata Amni | e6da498 | 2019-02-07 05:40:04 | [diff] [blame] | 797 | break; |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 798 | last_frame = rfh; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 799 | continue; |
Rakina Zata Amni | e6da498 | 2019-02-07 05:40:04 | [diff] [blame] | 800 | } |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 801 | RenderFrameHost* current_rfh = rfh; |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 802 | if (!matches_only || |
| 803 | find_in_page_clients_.find(current_rfh)->second->number_of_matches() || |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 804 | base::Contains(pending_initial_replies_, current_rfh)) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 805 | // 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 Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 808 | return rfh; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 809 | } |
Harkiran Bolaria | 37e21268 | 2021-10-14 19:44:37 | [diff] [blame] | 810 | if (wrap && rfh == from_rfh) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 811 | return nullptr; |
| 812 | } |
| 813 | |
| 814 | return nullptr; |
| 815 | } |
| 816 | |
paulmeyer | 3ac612d | 2016-09-30 19:21:06 | [diff] [blame] | 817 | void FindRequestManager::AddFrame(RenderFrameHost* rfh, bool force) { |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 818 | if (!rfh || !rfh->IsRenderFrameLive() || !rfh->IsActive() || |
| 819 | IsUnattachedGuestView(rfh)) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 820 | return; |
Miyoung Shin | 0e9b16b | 2022-10-03 11:05:17 | [diff] [blame] | 821 | } |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 822 | |
paulmeyer | 3ac612d | 2016-09-30 19:21:06 | [diff] [blame] | 823 | // A frame that is already being searched should not normally be added again. |
| 824 | DCHECK(force || !CheckFrame(rfh)); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 825 | |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 826 | DCHECK(!IsFindInPageDisabled(rfh)); |
| 827 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 828 | find_in_page_clients_[rfh] = |
| 829 | CreateFindInPageClient(static_cast<RenderFrameHostImpl*>(rfh)); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 830 | |
| 831 | FindRequest request = current_request_; |
| 832 | request.id = current_session_id_; |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 833 | request.options->new_session = true; |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 834 | request.options->force = force; |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 835 | SendFindRequest(request, rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 836 | } |
| 837 | |
| 838 | bool FindRequestManager::CheckFrame(RenderFrameHost* rfh) const { |
Alison Gale | 81f4f2c7 | 2024-04-22 19:33:31 | [diff] [blame] | 839 | // TODO(crbug.com/40196212): Convert IsFindInPageDisabled to a DCHECK when we |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 840 | // replace DidFinishLoad with DidFinishNavigation in FrameObserver. |
| 841 | if (!rfh || !base::Contains(find_in_page_clients_, rfh) || |
| 842 | IsFindInPageDisabled(rfh)) { |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 843 | return false; |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 844 | } |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 845 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 846 | DCHECK(rfh->IsActive()); |
Lei Zhang | 7c46fde | 2021-10-18 19:09:22 | [diff] [blame] | 847 | return true; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 848 | } |
| 849 | |
| 850 | void 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 Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 866 | active_match_ordinal_ += find_in_page_clients_[frame]->number_of_matches(); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 867 | } |
| 868 | active_match_ordinal_ += relative_active_match_ordinal_; |
| 869 | } |
| 870 | |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 871 | void FindRequestManager::FinalUpdateReceived(int request_id, |
| 872 | RenderFrameHost* rfh) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 873 | if (!number_of_matches_ || |
Russell Davis | dd2b678 | 2020-09-21 23:55:56 | [diff] [blame] | 874 | !current_request_.options->find_match || |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 875 | (active_match_ordinal_ && !pending_active_match_ordinal_) || |
paulmeyer | 3ac612d | 2016-09-30 19:21:06 | [diff] [blame] | 876 | pending_find_next_reply_) { |
paulmeyer | bbaacbe | 2016-08-30 18:04:13 | [diff] [blame] | 877 | // 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 */); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 882 | 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 Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 890 | if (request_id == current_request_.id && |
Russell Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 891 | !current_request_.options->new_session) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 892 | // 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 Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 894 | target_rfh = Traverse(rfh, current_request_.options->forward, |
| 895 | true /* matches_only */, true /* wrap */); |
paulmeyer | feafc2d | 2017-04-25 21:46:40 | [diff] [blame] | 896 | } else if ((target_rfh = |
| 897 | contents_->GetFocusedWebContents()->GetFocusedFrame()) != |
| 898 | nullptr) { |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 899 | // 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 Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 901 | target_rfh = Traverse(target_rfh, current_request_.options->forward, |
| 902 | true /* matches_only */, true /* wrap */); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 903 | } else { |
| 904 | // Otherwise, the first frame with matches will have the active match. |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 905 | target_rfh = GetInitialFrame(current_request_.options->forward); |
Rakina Zata Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 906 | if (!CheckFrame(target_rfh) || |
| 907 | !find_in_page_clients_[target_rfh]->number_of_matches()) { |
Rakina Zata Amni | 3f77dff | 2018-09-08 16:19:43 | [diff] [blame] | 908 | target_rfh = Traverse(target_rfh, current_request_.options->forward, |
| 909 | true /* matches_only */, false /* wrap */); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 910 | } |
| 911 | } |
Rakina Zata Amni | 1619612 | 2018-11-14 02:14:00 | [diff] [blame] | 912 | 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 | } |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 920 | |
| 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 Davis | 8a36226c | 2020-06-05 17:09:50 | [diff] [blame] | 925 | current_request_.options->new_session = false; |
Rakina Zata Amni | 2ac5750 | 2018-08-16 01:44:45 | [diff] [blame] | 926 | SendFindRequest(current_request_, target_rfh); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 927 | } |
| 928 | |
Miyoung Shin | bf4c40c | 2021-10-21 11:00:23 | [diff] [blame] | 929 | std::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 Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 936 | #if BUILDFLAG(IS_ANDROID) |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 937 | void 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 Amni | 724f3efe | 2018-07-23 03:19:46 | [diff] [blame] | 946 | 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); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 950 | } |
| 951 | } |
| 952 | |
| 953 | void 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 Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 960 | if (!match_rects_.pending_replies.empty()) |
| 961 | return; |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 962 | |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 963 | // 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()); |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 978 | } |
paulmeyer | c8cb7cb | 2016-06-07 01:14:19 | [diff] [blame] | 979 | } |
Lei Zhang | 4ea8c20 | 2019-10-16 02:27:59 | [diff] [blame] | 980 | contents_->NotifyFindMatchRectsReply( |
| 981 | match_rects_.known_version, aggregate_rects, match_rects_.active_rect); |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 982 | } |
Xiaohan Wang | 1ecfd00 | 2022-01-19 22:33:10 | [diff] [blame] | 983 | #endif // BUILDFLAG(IS_ANDROID) |
paulmeyer | c0b762b | 2016-04-13 11:55:17 | [diff] [blame] | 984 | |
| 985 | } // namespace content |