blob: b720e41106595e699a5ba6ce41b502788ac39a08 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2016 The Chromium Authors
paulmeyera41de3b2016-05-05 16:30:182// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Lei Zhang0a85e65a2025-05-23 19:22:065#include "content/browser/find_request_manager.h"
6
paulmeyera41de3b2016-05-05 16:30:187#include "base/command_line.h"
Keishi Hattori0e45c022021-11-27 09:25:528#include "base/memory/raw_ptr.h"
Lei Zhang0a85e65a2025-05-23 19:22:069#include "base/strings/string_number_conversions.h"
paulmeyera41de3b2016-05-05 16:30:1810#include "base/strings/utf_string_conversions.h"
Lucas Furukawa Gadania39a3162020-01-07 18:54:5311#include "base/test/scoped_feature_list.h"
Makoto Shimazu3266e2f52017-10-31 04:30:0712#include "build/build_config.h"
Miyoung Shinbf4c40c2021-10-21 11:00:2313#include "content/browser/find_in_page_client.h"
paulmeyera41de3b2016-05-05 16:30:1814#include "content/browser/web_contents/web_contents_impl.h"
Lei Zhang7c46fde2021-10-18 19:09:2215#include "content/public/browser/content_browser_client.h"
Lei Zhang7c46fde2021-10-18 19:09:2216#include "content/public/common/content_client.h"
paulmeyera41de3b2016-05-05 16:30:1817#include "content/public/common/content_switches.h"
Ming-Ying Chung72636182023-02-28 12:16:0418#include "content/public/test/back_forward_cache_util.h"
Peter Kasting919ce652020-05-07 10:22:3619#include "content/public/test/browser_test.h"
paulmeyer3ac612d2016-09-30 19:21:0620#include "content/public/test/browser_test_utils.h"
paulmeyera41de3b2016-05-05 16:30:1821#include "content/public/test/content_browser_test.h"
Scott Violet99861992023-02-08 01:20:1222#include "content/public/test/content_browser_test_content_browser_client.h"
paulmeyera41de3b2016-05-05 16:30:1823#include "content/public/test/content_browser_test_utils.h"
Gyuyoung Kima855ff5d2021-11-19 07:24:3524#include "content/public/test/fenced_frame_test_util.h"
paulmeyerda992f52017-01-27 17:11:2825#include "content/public/test/find_test_utils.h"
Takuto Ikutaa47d7852024-02-19 03:46:5826#include "content/public/test/no_renderer_crashes_assertion.h"
Miyoung Shinbf4c40c2021-10-21 11:00:2327#include "content/public/test/prerender_test_util.h"
paulmeyera41de3b2016-05-05 16:30:1828#include "content/public/test/test_navigation_observer.h"
29#include "content/public/test/test_utils.h"
30#include "content/shell/browser/shell.h"
paulmeyerc8cb7cb2016-06-07 01:14:1931#include "content/test/content_browser_test_utils_internal.h"
paulmeyera41de3b2016-05-05 16:30:1832#include "net/dns/mock_host_resolver.h"
Lucas Furukawa Gadania39a3162020-01-07 18:54:5333#include "third_party/blink/public/common/features.h"
Gyuyoung Kim1ac4ca782020-09-11 03:32:5134#include "third_party/blink/public/common/web_preferences/web_preferences.h"
Antonio Gomes014265dcd2020-04-24 04:22:4635#include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
Lei Zhang7c46fde2021-10-18 19:09:2236#include "url/origin.h"
paulmeyera41de3b2016-05-05 16:30:1837
Xiaohan Wang1ecfd002022-01-19 22:33:1038#if BUILDFLAG(IS_ANDROID)
WangHui9677e35e2021-01-16 02:52:2039#include "ui/android/view_android.h"
40#endif
41
paulmeyera41de3b2016-05-05 16:30:1842namespace content {
43
44namespace {
45
46const int kInvalidId = -1;
47
Lei Zhang7c46fde2021-10-18 19:09:2248const url::Origin& GetOriginForFrameTreeNode(FrameTreeNode* node) {
49 return node->current_frame_host()->GetLastCommittedOrigin();
50}
51
Xiaohan Wang1ecfd002022-01-19 22:33:1052#if BUILDFLAG(IS_ANDROID)
Vladimir Levin8e5ac5c2022-01-14 19:40:1553double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
54 return EvalJs(adapter, "window.devicePixelRatio;").ExtractDouble();
55}
Xiaohan Wang1ecfd002022-01-19 22:33:1056#endif // BUILDFLAG(IS_ANDROID)
Vladimir Levin8e5ac5c2022-01-14 19:40:1557
paulmeyera41de3b2016-05-05 16:30:1858} // namespace
59
Miyoung Shin03191752022-03-29 09:20:5160class FindRequestManagerTestBase : public ContentBrowserTest {
paulmeyera41de3b2016-05-05 16:30:1861 public:
Miyoung Shin03191752022-03-29 09:20:5162 FindRequestManagerTestBase()
63 : normal_delegate_(nullptr), last_request_id_(0) {}
Peter Boström828b9022021-09-21 02:28:4364
Miyoung Shin03191752022-03-29 09:20:5165 FindRequestManagerTestBase(const FindRequestManagerTestBase&) = delete;
66 FindRequestManagerTestBase& operator=(const FindRequestManagerTestBase&) =
67 delete;
Peter Boström828b9022021-09-21 02:28:4368
Miyoung Shin03191752022-03-29 09:20:5169 ~FindRequestManagerTestBase() override = default;
paulmeyera41de3b2016-05-05 16:30:1870
71 void SetUpOnMainThread() override {
72 host_resolver()->AddRule("*", "127.0.0.1");
73 ASSERT_TRUE(embedded_test_server()->Start());
74
75 // Swap the WebContents's delegate for our test delegate.
76 normal_delegate_ = contents()->GetDelegate();
77 contents()->SetDelegate(&test_delegate_);
78 }
79
80 void TearDownOnMainThread() override {
81 // Swap the WebContents's delegate back to its usual delegate.
82 contents()->SetDelegate(normal_delegate_);
punithnayake70ebc22023-10-20 12:09:2583 normal_delegate_ = nullptr;
paulmeyera41de3b2016-05-05 16:30:1884 }
85
86 void SetUpCommandLine(base::CommandLine* command_line) override {
87 IsolateAllSitesForTesting(command_line);
88 }
89
90 protected:
paulmeyerc8cb7cb2016-06-07 01:14:1991 // Navigates to |url| and waits for it to finish loading.
paulmeyera41de3b2016-05-05 16:30:1892 void LoadAndWait(const std::string& url) {
93 TestNavigationObserver navigation_observer(contents());
Alex Moshchukaeb20fe32019-09-25 17:40:0194 EXPECT_TRUE(
95 NavigateToURL(shell(), embedded_test_server()->GetURL("a.com", url)));
paulmeyerda992f52017-01-27 17:11:2896 ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
Scott Haseley1219a4a82024-03-19 01:14:5497
98 // crbug.com/330147459: Ensure a frame has been produced in the renderer so
99 // the active match is set correctly.
100 ASSERT_TRUE(
101 EvalJsAfterLifecycleUpdate(contents()->GetPrimaryMainFrame(), "", "")
Chris Fredricksonbc209522025-07-25 17:05:52102 .is_ok());
paulmeyera41de3b2016-05-05 16:30:18103 }
104
paulmeyerc8cb7cb2016-06-07 01:14:19105 // Loads a multi-frame page. The page will have a full binary frame tree of
106 // height |height|. If |cross_process| is true, child frames will be loaded
107 // cross-process.
108 void LoadMultiFramePage(int height, bool cross_process) {
109 LoadAndWait("/find_in_page_multi_frame.html");
Lei Zhang7c46fde2021-10-18 19:09:22110 LoadMultiFramePageChildFrames(height, cross_process, root());
paulmeyerc8cb7cb2016-06-07 01:14:19111 }
112
113 // Reloads the child frame cross-process.
114 void MakeChildFrameCrossProcess() {
Lei Zhang7c46fde2021-10-18 19:09:22115 FrameTreeNode* child = first_child();
116 GURL url =
117 embedded_test_server()->GetURL("b.com", child->current_url().path());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20118 EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
paulmeyerc8cb7cb2016-06-07 01:14:19119 }
120
paulmeyera41de3b2016-05-05 16:30:18121 void Find(const std::string& search_text,
Rakina Zata Amni3f77dff2018-09-08 16:19:43122 blink::mojom::FindOptionsPtr options) {
paulmeyer2f06e612016-08-10 17:39:02123 delegate()->UpdateLastRequest(++last_request_id_);
Rakina Zata Amni3f77dff2018-09-08 16:19:43124 contents()->Find(last_request_id_, base::UTF8ToUTF16(search_text),
Avi Drissman5710b062024-07-02 21:23:29125 std::move(options), /*skip_delay=*/false);
paulmeyera41de3b2016-05-05 16:30:18126 }
127
paulmeyer74f68c92016-08-12 22:34:13128 WebContentsImpl* contents() const {
129 return static_cast<WebContentsImpl*>(shell()->web_contents());
paulmeyera41de3b2016-05-05 16:30:18130 }
131
paulmeyerda992f52017-01-27 17:11:28132 FindTestWebContentsDelegate* delegate() const {
133 return static_cast<FindTestWebContentsDelegate*>(contents()->GetDelegate());
paulmeyera41de3b2016-05-05 16:30:18134 }
135
136 int last_request_id() const {
137 return last_request_id_;
138 }
139
Carlos Caballero15caeeb2021-10-27 09:57:55140 FrameTreeNode* root() { return contents()->GetPrimaryFrameTree().root(); }
Lei Zhang7c46fde2021-10-18 19:09:22141
142 FrameTreeNode* first_child() { return root()->child_at(0); }
143
paulmeyera41de3b2016-05-05 16:30:18144 private:
paulmeyerc8cb7cb2016-06-07 01:14:19145 // Helper function for LoadMultiFramePage. Loads child frames until the frame
146 // tree rooted at |root| is a full binary tree of height |height|.
147 void LoadMultiFramePageChildFrames(int height,
148 bool cross_process,
149 FrameTreeNode* root) {
150 if (height == 0)
151 return;
152
153 std::string hostname = root->current_origin().host();
154 if (cross_process)
155 hostname.insert(0, 1, 'a');
156 GURL url(embedded_test_server()->GetURL(hostname,
157 "/find_in_page_multi_frame.html"));
158
159 TestNavigationObserver observer(shell()->web_contents());
160
161 FrameTreeNode* child = root->child_at(0);
162 NavigateFrameToURL(child, url);
163 EXPECT_TRUE(observer.last_navigation_succeeded());
164 LoadMultiFramePageChildFrames(height - 1, cross_process, child);
165
166 child = root->child_at(1);
167 NavigateFrameToURL(child, url);
168 EXPECT_TRUE(observer.last_navigation_succeeded());
169 LoadMultiFramePageChildFrames(height - 1, cross_process, child);
170 }
171
paulmeyerda992f52017-01-27 17:11:28172 FindTestWebContentsDelegate test_delegate_;
punithnayake70ebc22023-10-20 12:09:25173 raw_ptr<WebContentsDelegate> normal_delegate_;
paulmeyera41de3b2016-05-05 16:30:18174
175 // The ID of the last find request requested.
176 int last_request_id_;
paulmeyera41de3b2016-05-05 16:30:18177};
178
Miyoung Shin03191752022-03-29 09:20:51179class FindRequestManagerTest : public FindRequestManagerTestBase,
180 public testing::WithParamInterface<bool> {
181 protected:
182 bool test_with_oopif() const { return GetParam(); }
183};
184
Victor Costan3fa94d22019-01-28 19:27:26185INSTANTIATE_TEST_SUITE_P(FindRequestManagerTests,
186 FindRequestManagerTest,
187 testing::Bool());
paulmeyera41de3b2016-05-05 16:30:18188
Alison Gale770f3fc2024-04-27 00:39:58189// TODO(crbug.com/40470937): These tests frequently fail on Android.
Xiaohan Wang1ecfd002022-01-19 22:33:10190#if BUILDFLAG(IS_ANDROID)
paulmeyercc9a00f92016-07-06 13:44:27191#define MAYBE(x) DISABLED_##x
paulmeyerc8cb7cb2016-06-07 01:14:19192#else
paulmeyercc9a00f92016-07-06 13:44:27193#define MAYBE(x) x
paulmeyerc8cb7cb2016-06-07 01:14:19194#endif
195
196
197// Tests basic find-in-page functionality (such as searching forward and
paulmeyera41de3b2016-05-05 16:30:18198// backward) and check for correct results at each step.
paulmeyercc9a00f92016-07-06 13:44:27199IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(Basic)) {
paulmeyera41de3b2016-05-05 16:30:18200 LoadAndWait("/find_in_page.html");
Joey Arhar0574fb752022-03-29 00:45:16201 if (test_with_oopif())
paulmeyerc8cb7cb2016-06-07 01:14:19202 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18203
Rakina Zata Amni3f77dff2018-09-08 16:19:43204 auto options = blink::mojom::FindOptions::New();
205 options->run_synchronously_for_testing = true;
206 Find("result", options->Clone());
paulmeyer2f06e612016-08-10 17:39:02207 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18208
209 FindResults results = delegate()->GetFindResults();
210 EXPECT_EQ(last_request_id(), results.request_id);
211 EXPECT_EQ(19, results.number_of_matches);
212 EXPECT_EQ(1, results.active_match_ordinal);
213
Russell Davis8a36226c2020-06-05 17:09:50214 options->new_session = false;
paulmeyera41de3b2016-05-05 16:30:18215 for (int i = 2; i <= 10; ++i) {
Rakina Zata Amni3f77dff2018-09-08 16:19:43216 Find("result", options->Clone());
paulmeyer2f06e612016-08-10 17:39:02217 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18218
219 results = delegate()->GetFindResults();
220 EXPECT_EQ(last_request_id(), results.request_id);
221 EXPECT_EQ(19, results.number_of_matches);
222 EXPECT_EQ(i, results.active_match_ordinal);
223 }
224
Rakina Zata Amni3f77dff2018-09-08 16:19:43225 options->forward = false;
paulmeyera41de3b2016-05-05 16:30:18226 for (int i = 9; i >= 5; --i) {
Rakina Zata Amni3f77dff2018-09-08 16:19:43227 Find("result", options->Clone());
paulmeyer2f06e612016-08-10 17:39:02228 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18229
230 results = delegate()->GetFindResults();
231 EXPECT_EQ(last_request_id(), results.request_id);
232 EXPECT_EQ(19, results.number_of_matches);
233 EXPECT_EQ(i, results.active_match_ordinal);
234 }
235}
236
Russell Davisdd2b6782020-09-21 23:55:56237IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, FindInPage_Issue615291) {
238 LoadAndWait("/find_in_simple_page.html");
239
240 auto options = blink::mojom::FindOptions::New();
241 options->run_synchronously_for_testing = true;
242 options->find_match = false;
243 Find("result", options->Clone());
244 delegate()->WaitForFinalReply();
245
246 FindResults results = delegate()->GetFindResults();
247 EXPECT_EQ(5, results.number_of_matches);
248 EXPECT_EQ(0, results.active_match_ordinal);
249
250 options->new_session = false;
251 Find("result", options->Clone());
252 // With the issue being tested, this would loop forever and cause the
253 // test to timeout.
254 delegate()->WaitForFinalReply();
255 results = delegate()->GetFindResults();
256 EXPECT_EQ(5, results.number_of_matches);
257 EXPECT_EQ(0, results.active_match_ordinal);
258}
259
David Bokan7ebbe90a2018-02-13 17:41:54260bool ExecuteScriptAndExtractRect(FrameTreeNode* frame,
261 const std::string& script,
262 gfx::Rect* out) {
David Bokan7ebbe90a2018-02-13 17:41:54263 std::string script_and_extract =
Avi Drissmanc91bd8e2021-04-19 23:58:44264 script + "rect.x + ',' + rect.y + ',' + rect.width + ',' + rect.height;";
265 std::string result = EvalJs(frame, script_and_extract).ExtractString();
David Bokan7ebbe90a2018-02-13 17:41:54266
267 std::vector<std::string> tokens = base::SplitString(
268 result, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
269 if (tokens.size() != 4U)
270 return false;
271
272 double x, y, width, height;
273 if (!base::StringToDouble(tokens[0], &x) ||
274 !base::StringToDouble(tokens[1], &y) ||
275 !base::StringToDouble(tokens[2], &width) ||
276 !base::StringToDouble(tokens[3], &height))
277 return false;
278
279 *out = gfx::Rect(static_cast<int>(x), static_cast<int>(y),
280 static_cast<int>(width), static_cast<int>(height));
281 return true;
282}
283
284// Basic test that a search result is actually brought into view.
285IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, ScrollAndZoomIntoView) {
Sandra Sunc79fc93a2018-04-10 15:22:23286 WebContentsImpl* web_contents =
287 static_cast<WebContentsImpl*>(shell()->web_contents());
Gyuyoung Kim1ac4ca782020-09-11 03:32:51288 blink::web_pref::WebPreferences prefs =
289 web_contents->GetOrCreateWebPreferences();
Sandra Sunc79fc93a2018-04-10 15:22:23290 prefs.smooth_scroll_for_find_enabled = false;
Rakina Zata Amni347b70902020-07-22 10:49:04291 web_contents->SetWebPreferences(prefs);
Sandra Sunc79fc93a2018-04-10 15:22:23292
David Bokan7ebbe90a2018-02-13 17:41:54293 LoadAndWait("/find_in_page_desktop.html");
W. James MacLean92a45192018-12-20 20:09:55294 // Note: for now, don't run this test on Android in OOPIF mode.
Joey Arhar0574fb752022-03-29 00:45:16295 if (test_with_oopif())
Xiaohan Wang1ecfd002022-01-19 22:33:10296#if BUILDFLAG(IS_ANDROID)
W. James MacLean92a45192018-12-20 20:09:55297 return;
298#else
Ehsan Karamad99176772018-03-09 15:45:48299 MakeChildFrameCrossProcess();
Xiaohan Wang1ecfd002022-01-19 22:33:10300#endif // BUILDFLAG(IS_ANDROID)
David Bokan7ebbe90a2018-02-13 17:41:54301
302 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55303 ->GetPrimaryFrameTree()
304 .root();
David Bokan7ebbe90a2018-02-13 17:41:54305 FrameTreeNode* child = root->child_at(0);
306
307 // Start off at a non-origin scroll offset to ensure coordinate conversisons
308 // work correctly.
Avi Drissmanc91bd8e2021-04-19 23:58:44309 ASSERT_TRUE(ExecJs(root, "window.scrollTo(3500, 1500);"));
David Bokan7ebbe90a2018-02-13 17:41:54310
311 // Search for a result further down in the iframe.
Rakina Zata Amni3f77dff2018-09-08 16:19:43312 auto options = blink::mojom::FindOptions::New();
313 options->run_synchronously_for_testing = true;
314 Find("result 17", options->Clone());
David Bokan7ebbe90a2018-02-13 17:41:54315 delegate()->WaitForFinalReply();
316
317 // gBCR of result box in iframe.
318 gfx::Rect target_in_iframe;
319
320 // gBCR of iframe in main document.
321 gfx::Rect iframe_rect;
322
323 // Window size with location at origin (for comparison with gBCR).
324 gfx::Rect root_rect;
325
326 // Visual viewport rect relative to root_rect.
327 gfx::Rect visual_rect;
328
329 ASSERT_TRUE(ExecuteScriptAndExtractRect(
330 child,
331 "var result = document.querySelector('.margin-overflow');"
332 "var rect = result.getBoundingClientRect();",
333 &target_in_iframe));
334 ASSERT_TRUE(ExecuteScriptAndExtractRect(
335 root,
336 "var rect = document.querySelector('#frame').getBoundingClientRect();",
337 &iframe_rect));
338 ASSERT_TRUE(ExecuteScriptAndExtractRect(
339 root,
340 "var rect = new DOMRect(0, 0, window.innerWidth, window.innerHeight);",
341 &root_rect));
342 ASSERT_TRUE(ExecuteScriptAndExtractRect(
343 root,
344 "var rect = new DOMRect(visualViewport.offsetLeft, "
345 " visualViewport.offsetTop,"
346 " visualViewport.width,"
347 " visualViewport.height);",
348 &visual_rect));
349
350 gfx::Rect result_in_root = target_in_iframe + iframe_rect.OffsetFromOrigin();
351
352 EXPECT_TRUE(gfx::Rect(iframe_rect.size()).Contains(target_in_iframe))
353 << "Result rect[ " << target_in_iframe.ToString()
354 << " ] not visible in iframe [ 0,0 " << iframe_rect.size().ToString()
355 << " ].";
356
357 EXPECT_TRUE(root_rect.Contains(result_in_root))
358 << "Result rect[ " << result_in_root.ToString()
359 << " ] not visible in root frame [ " << root_rect.ToString() << " ].";
360
361 EXPECT_TRUE(visual_rect.Contains(result_in_root))
362 << "Result rect[ " << result_in_root.ToString()
363 << " ] not visible in visual viewport [ " << visual_rect.ToString()
364 << " ].";
365}
366
paulmeyera41de3b2016-05-05 16:30:18367// Tests searching for a word character-by-character, as would typically be done
368// by a user typing into the find bar.
paulmeyercc9a00f92016-07-06 13:44:27369IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(CharacterByCharacter)) {
paulmeyera41de3b2016-05-05 16:30:18370 LoadAndWait("/find_in_page.html");
Joey Arhar0574fb752022-03-29 00:45:16371 if (test_with_oopif())
paulmeyerc8cb7cb2016-06-07 01:14:19372 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18373
Rakina Zata Amni3f77dff2018-09-08 16:19:43374 auto default_options = blink::mojom::FindOptions::New();
375 default_options->run_synchronously_for_testing = true;
376 Find("r", default_options->Clone());
377 Find("re", default_options->Clone());
378 Find("res", default_options->Clone());
379 Find("resu", default_options->Clone());
380 Find("resul", default_options->Clone());
381 Find("result", default_options->Clone());
paulmeyer2f06e612016-08-10 17:39:02382 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18383
384 FindResults results = delegate()->GetFindResults();
385 EXPECT_EQ(last_request_id(), results.request_id);
386 EXPECT_EQ(19, results.number_of_matches);
387 EXPECT_EQ(1, results.active_match_ordinal);
388}
389
Alison Gale770f3fc2024-04-27 00:39:58390// TODO(crbug.com/40470937): This test frequently fails on Android.
Alison Galed94ce4f2024-04-22 15:20:39391// TODO(crbug.com/41291496): This test is flaky on Win
392// TODO(crbug.com/41393143): Flaky on CrOS MSan
paulmeyerc8cb7cb2016-06-07 01:14:19393// Tests sending a large number of find requests subsequently.
Alexander Potapenko5faa659d2018-06-07 12:47:19394IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_RapidFire) {
paulmeyera41de3b2016-05-05 16:30:18395 LoadAndWait("/find_in_page.html");
Joey Arhar0574fb752022-03-29 00:45:16396 if (test_with_oopif())
paulmeyerc8cb7cb2016-06-07 01:14:19397 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18398
Rakina Zata Amni3f77dff2018-09-08 16:19:43399 auto options = blink::mojom::FindOptions::New();
400 options->run_synchronously_for_testing = true;
401 Find("result", options.Clone());
paulmeyera41de3b2016-05-05 16:30:18402
Russell Davis8a36226c2020-06-05 17:09:50403 options->new_session = false;
paulmeyera41de3b2016-05-05 16:30:18404 for (int i = 2; i <= 1000; ++i)
Rakina Zata Amni3f77dff2018-09-08 16:19:43405 Find("result", options.Clone());
paulmeyer2f06e612016-08-10 17:39:02406 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18407
408 FindResults results = delegate()->GetFindResults();
409 EXPECT_EQ(last_request_id(), results.request_id);
410 EXPECT_EQ(19, results.number_of_matches);
411 EXPECT_EQ(last_request_id() % results.number_of_matches,
412 results.active_match_ordinal);
413}
414
paulmeyerc8cb7cb2016-06-07 01:14:19415// Tests removing a frame during a find session.
Alison Gale770f3fc2024-04-27 00:39:58416// TODO(crbug.com/40489609): Test is flaky on all platforms.
mek98430952016-11-29 01:34:46417IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_RemoveFrame) {
Joey Arhar0574fb752022-03-29 00:45:16418 LoadMultiFramePage(2 /* height */, test_with_oopif() /* cross_process */);
paulmeyerc8cb7cb2016-06-07 01:14:19419
Rakina Zata Amni3f77dff2018-09-08 16:19:43420 auto options = blink::mojom::FindOptions::New();
421 options->run_synchronously_for_testing = true;
422 Find("result", options->Clone());
paulmeyerdc6a6442016-08-19 16:48:28423 delegate()->WaitForFinalReply();
Russell Davis8a36226c2020-06-05 17:09:50424 options->new_session = false;
Rakina Zata Amni3f77dff2018-09-08 16:19:43425 options->forward = false;
426 Find("result", options->Clone());
427 Find("result", options->Clone());
428 Find("result", options->Clone());
429 Find("result", options->Clone());
430 Find("result", options->Clone());
paulmeyer2f06e612016-08-10 17:39:02431 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19432
433 FindResults results = delegate()->GetFindResults();
434 EXPECT_EQ(last_request_id(), results.request_id);
435 EXPECT_EQ(21, results.number_of_matches);
436 EXPECT_EQ(17, results.active_match_ordinal);
437
438 // Remove a frame.
Lei Zhang7c46fde2021-10-18 19:09:22439 root()->current_frame_host()->RemoveChild(first_child());
paulmeyerc8cb7cb2016-06-07 01:14:19440
441 // The number of matches and active match ordinal should update automatically
442 // to exclude the matches from the removed frame.
443 results = delegate()->GetFindResults();
paulmeyerc8cb7cb2016-06-07 01:14:19444 EXPECT_EQ(12, results.number_of_matches);
445 EXPECT_EQ(8, results.active_match_ordinal);
paulmeyer3ac612d2016-09-30 19:21:06446}
paulmeyerc8cb7cb2016-06-07 01:14:19447
Vladimir Levin34a865b2020-01-08 21:54:29448IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, RemoveMainFrame) {
449 LoadAndWait("/find_in_page.html");
450
451 auto options = blink::mojom::FindOptions::New();
452 options->run_synchronously_for_testing = true;
453 Find("result", options->Clone());
454 delegate()->WaitForFinalReply();
Russell Davis8a36226c2020-06-05 17:09:50455 options->new_session = false;
Vladimir Levin34a865b2020-01-08 21:54:29456 options->forward = false;
457 Find("result", options->Clone());
458 Find("result", options->Clone());
459 Find("result", options->Clone());
460 Find("result", options->Clone());
461 Find("result", options->Clone());
462
463 // Don't wait for the reply, and end the test. This will remove the main
464 // frame, which should not crash.
465}
466
paulmeyer3ac612d2016-09-30 19:21:06467// Tests adding a frame during a find session.
Alison Gale770f3fc2024-04-27 00:39:58468// TODO(crbug.com/40489609): Test is flaky on all platforms.
mek98430952016-11-29 01:34:46469IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_AddFrame) {
Joey Arhar0574fb752022-03-29 00:45:16470 LoadMultiFramePage(2 /* height */, test_with_oopif() /* cross_process */);
paulmeyer3ac612d2016-09-30 19:21:06471
Rakina Zata Amni3f77dff2018-09-08 16:19:43472 auto options = blink::mojom::FindOptions::New();
473 options->run_synchronously_for_testing = true;
474 Find("result", options.Clone());
Russell Davis8a36226c2020-06-05 17:09:50475 options->new_session = false;
Rakina Zata Amni3f77dff2018-09-08 16:19:43476 Find("result", options.Clone());
477 Find("result", options.Clone());
478 Find("result", options.Clone());
479 Find("result", options.Clone());
paulmeyer3ac612d2016-09-30 19:21:06480 delegate()->WaitForFinalReply();
481
482 FindResults results = delegate()->GetFindResults();
483 EXPECT_EQ(last_request_id(), results.request_id);
484 EXPECT_EQ(21, results.number_of_matches);
485 EXPECT_EQ(5, results.active_match_ordinal);
486
487 // Add a frame. It contains 5 new matches.
Joey Arhar0574fb752022-03-29 00:45:16488 std::string url = embedded_test_server()
489 ->GetURL(test_with_oopif() ? "b.com" : "a.com",
490 "/find_in_simple_page.html")
491 .spec();
paulmeyer3ac612d2016-09-30 19:21:06492 std::string script = std::string() +
493 "var frame = document.createElement('iframe');" +
494 "frame.src = '" + url + "';" +
495 "document.body.appendChild(frame);";
496 delegate()->MarkNextReply();
Avi Drissmanc91bd8e2021-04-19 23:58:44497 ASSERT_TRUE(ExecJs(shell(), script));
paulmeyer3ac612d2016-09-30 19:21:06498 delegate()->WaitForNextReply();
499
500 // The number of matches should update automatically to include the matches
501 // from the newly added frame.
502 results = delegate()->GetFindResults();
503 EXPECT_EQ(26, results.number_of_matches);
504 EXPECT_EQ(5, results.active_match_ordinal);
505}
506
Vishal Lingamdb0d42a2023-03-20 06:06:34507// Tests adding an in-process hidden iframe during a find session.
508IN_PROC_BROWSER_TEST_P(FindRequestManagerTest,
509 AddInprocessHiddenFrameDuringFind) {
510 LoadAndWait("/find_in_page.html");
511
512 auto options = blink::mojom::FindOptions::New();
513 options->run_synchronously_for_testing = true;
514 Find("result", options.Clone());
515 delegate()->WaitForFinalReply();
516
517 FindResults results = delegate()->GetFindResults();
518 EXPECT_EQ(19, results.number_of_matches);
519
520 // Add a frame. It contains 5 new matches.
521 std::string url = embedded_test_server()
522 ->GetURL("a.com", "/find_in_simple_page.html")
523 .spec();
524 std::string script = JsReplace(R"JS(
525 var frame = document.createElement('iframe');
526 frame.src = '$1';
527 frame.style.visibility = 'hidden';
528 document.body.appendChild(frame);
529 )JS",
530 url);
531
532 delegate()->MarkNextReply();
533 ASSERT_TRUE(ExecJs(shell(), script));
534 delegate()->WaitForNextReply();
535
536 // The number of matches should not be effected by the
537 // the newly added hidden frame.
538 results = delegate()->GetFindResults();
539 EXPECT_EQ(19, results.number_of_matches);
540}
541
paulmeyer3ac612d2016-09-30 19:21:06542// Tests adding a frame during a find session where there were previously no
543// matches.
544IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(AddFrameAfterNoMatches)) {
545 TestNavigationObserver navigation_observer(contents());
Alex Moshchukaeb20fe32019-09-25 17:40:01546 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
paulmeyer3ac612d2016-09-30 19:21:06547 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
548
Rakina Zata Amni3f77dff2018-09-08 16:19:43549 auto default_options = blink::mojom::FindOptions::New();
550 default_options->run_synchronously_for_testing = true;
551 Find("result", default_options.Clone());
paulmeyer3ac612d2016-09-30 19:21:06552 delegate()->WaitForFinalReply();
553
554 // Initially, there are no matches on the page.
555 FindResults results = delegate()->GetFindResults();
556 EXPECT_EQ(last_request_id(), results.request_id);
557 EXPECT_EQ(0, results.number_of_matches);
558 EXPECT_EQ(0, results.active_match_ordinal);
559
560 // Add a frame. It contains 5 new matches.
561 std::string url =
562 embedded_test_server()->GetURL("/find_in_simple_page.html").spec();
563 std::string script = std::string() +
564 "var frame = document.createElement('iframe');" +
565 "frame.src = '" + url + "';" +
566 "document.body.appendChild(frame);";
567 delegate()->MarkNextReply();
Avi Drissmanc91bd8e2021-04-19 23:58:44568 ASSERT_TRUE(ExecJs(shell(), script));
paulmeyer3ac612d2016-09-30 19:21:06569 delegate()->WaitForNextReply();
570
571 // The matches from the new frame should be found automatically, and the first
572 // match in the frame should be activated.
573 results = delegate()->GetFindResults();
574 EXPECT_EQ(5, results.number_of_matches);
575 EXPECT_EQ(1, results.active_match_ordinal);
576}
577
578// Tests a frame navigating to a different page during a find session.
579IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(NavigateFrame)) {
Joey Arhar0574fb752022-03-29 00:45:16580 LoadMultiFramePage(2 /* height */, test_with_oopif() /* cross_process */);
paulmeyer3ac612d2016-09-30 19:21:06581
Rakina Zata Amni3f77dff2018-09-08 16:19:43582 auto options = blink::mojom::FindOptions::New();
583 options->run_synchronously_for_testing = true;
584 Find("result", options.Clone());
Russell Davis8a36226c2020-06-05 17:09:50585 options->new_session = false;
Rakina Zata Amni3f77dff2018-09-08 16:19:43586 options->forward = false;
587 Find("result", options.Clone());
588 Find("result", options.Clone());
589 Find("result", options.Clone());
paulmeyer3ac612d2016-09-30 19:21:06590 delegate()->WaitForFinalReply();
591
592 FindResults results = delegate()->GetFindResults();
593 EXPECT_EQ(last_request_id(), results.request_id);
594 EXPECT_EQ(21, results.number_of_matches);
595 EXPECT_EQ(19, results.active_match_ordinal);
596
597 // Navigate one of the empty frames to a page with 5 matches.
Carlos Caballero15caeeb2021-10-27 09:57:55598 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
599 ->GetPrimaryFrameTree()
600 .root();
Joey Arhar0574fb752022-03-29 00:45:16601 GURL url(embedded_test_server()->GetURL(test_with_oopif() ? "b.com" : "a.com",
602 "/find_in_simple_page.html"));
paulmeyer3ac612d2016-09-30 19:21:06603 delegate()->MarkNextReply();
604 TestNavigationObserver navigation_observer(contents());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20605 EXPECT_TRUE(NavigateToURLFromRenderer(
606 root->child_at(0)->child_at(1)->child_at(0), url));
paulmeyer3ac612d2016-09-30 19:21:06607 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
608 delegate()->WaitForNextReply();
609
610 // The navigation results in an extra reply before the one we care about. This
611 // extra reply happens because the RenderFrameHost changes before it navigates
612 // (because the navigation is cross-origin). The first reply will not change
613 // the number of matches because the frame that is navigating was empty
614 // before.
615 if (delegate()->GetFindResults().number_of_matches == 21) {
616 delegate()->MarkNextReply();
617 delegate()->WaitForNextReply();
618 }
619
620 // The number of matches and the active match ordinal should update
621 // automatically to include the new matches.
622 results = delegate()->GetFindResults();
623 EXPECT_EQ(26, results.number_of_matches);
624 EXPECT_EQ(24, results.active_match_ordinal);
paulmeyerc8cb7cb2016-06-07 01:14:19625}
626
627// Tests Searching in a hidden frame. Matches in the hidden frame should be
628// ignored.
paulmeyercc9a00f92016-07-06 13:44:27629IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(HiddenFrame)) {
paulmeyerc8cb7cb2016-06-07 01:14:19630 LoadAndWait("/find_in_hidden_frame.html");
631
Rakina Zata Amni3f77dff2018-09-08 16:19:43632 auto default_options = blink::mojom::FindOptions::New();
633 default_options->run_synchronously_for_testing = true;
634 Find("hello", default_options.Clone());
paulmeyer2f06e612016-08-10 17:39:02635 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19636 FindResults results = delegate()->GetFindResults();
637
638 EXPECT_EQ(last_request_id(), results.request_id);
639 EXPECT_EQ(1, results.number_of_matches);
640 EXPECT_EQ(1, results.active_match_ordinal);
641}
642
paulmeyer74f68c92016-08-12 22:34:13643// Tests that new matches can be found in dynamically added text.
Zachary Tanac4a483c2024-03-18 20:14:43644// TODO(crbug.com/330194342): Deflake and re-enable.
645#if BUILDFLAG(IS_ANDROID) || \
646 (BUILDFLAG(IS_LINUX) && !defined(UNDEFINED_SANITIZER))
647#define MAYBE_FindNewMatches DISABLED_FindNewMatches
648#else
649#define MAYBE_FindNewMatches FindNewMatches
650#endif
651IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_FindNewMatches) {
paulmeyer74f68c92016-08-12 22:34:13652 LoadAndWait("/find_in_dynamic_page.html");
653
Rakina Zata Amni3f77dff2018-09-08 16:19:43654 auto options = blink::mojom::FindOptions::New();
655 options->run_synchronously_for_testing = true;
656 Find("result", options.Clone());
Russell Davis8a36226c2020-06-05 17:09:50657 options->new_session = false;
Rakina Zata Amni3f77dff2018-09-08 16:19:43658 Find("result", options.Clone());
659 Find("result", options.Clone());
paulmeyer74f68c92016-08-12 22:34:13660 delegate()->WaitForFinalReply();
661
662 FindResults results = delegate()->GetFindResults();
663 EXPECT_EQ(last_request_id(), results.request_id);
664 EXPECT_EQ(3, results.number_of_matches);
665 EXPECT_EQ(3, results.active_match_ordinal);
666
667 // Dynamically add new text to the page. This text contains 5 new matches for
668 // "result".
Dave Tapuska327c06c92022-06-13 20:31:51669 ASSERT_TRUE(ExecJs(contents()->GetPrimaryMainFrame(), "addNewText()"));
paulmeyer74f68c92016-08-12 22:34:13670
Rakina Zata Amni3f77dff2018-09-08 16:19:43671 Find("result", options.Clone());
paulmeyer74f68c92016-08-12 22:34:13672 delegate()->WaitForFinalReply();
673
674 results = delegate()->GetFindResults();
675 EXPECT_EQ(last_request_id(), results.request_id);
676 EXPECT_EQ(8, results.number_of_matches);
677 EXPECT_EQ(4, results.active_match_ordinal);
678}
679
Alison Gale770f3fc2024-04-27 00:39:58680// TODO(crbug.com/40470937): These tests frequently fail on Android.
Alison Galed94ce4f2024-04-22 15:20:39681// TODO(crbug.com/41352658): Flaky timeout on Win7 (dbg).
682// TODO(crbug.com/41408666): Flaky on Win10.
Xiaohan Wang1ecfd002022-01-19 22:33:10683#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
Makoto Shimazu3266e2f52017-10-31 04:30:07684#define MAYBE_FindInPage_Issue627799 DISABLED_FindInPage_Issue627799
685#else
686#define MAYBE_FindInPage_Issue627799 FindInPage_Issue627799
687#endif
688
689IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE_FindInPage_Issue627799) {
paulmeyerdc6a6442016-08-19 16:48:28690 LoadAndWait("/find_in_long_page.html");
691
Rakina Zata Amni3f77dff2018-09-08 16:19:43692 auto options = blink::mojom::FindOptions::New();
693 options->run_synchronously_for_testing = true;
694 Find("42", options.Clone());
paulmeyerdc6a6442016-08-19 16:48:28695 delegate()->WaitForFinalReply();
696
697 FindResults results = delegate()->GetFindResults();
698 EXPECT_EQ(last_request_id(), results.request_id);
699 EXPECT_EQ(970, results.number_of_matches);
700 EXPECT_EQ(1, results.active_match_ordinal);
701
702 delegate()->StartReplyRecord();
Russell Davis8a36226c2020-06-05 17:09:50703 options->new_session = false;
Rakina Zata Amni3f77dff2018-09-08 16:19:43704 options->forward = false;
705 Find("42", options.Clone());
paulmeyerdc6a6442016-08-19 16:48:28706 delegate()->WaitForFinalReply();
707
708 // This is the crux of the issue that this test guards against. Searching
709 // across the frame boundary should not cause the frame to be re-scoped. If
710 // the re-scope occurs, then we will see the number of matches change in one
711 // of the recorded find replies.
712 for (auto& reply : delegate()->GetReplyRecord()) {
713 EXPECT_EQ(last_request_id(), reply.request_id);
714 EXPECT_TRUE(reply.number_of_matches == kInvalidId ||
715 reply.number_of_matches == results.number_of_matches);
716 }
717}
718
Rakina Zata Amnie6da4982019-02-07 05:40:04719IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, DetachFrameWithMatch) {
720 // Detaching an iframe with matches when the main document doesn't
721 // have matches should work and just remove the matches from the
722 // removed frame.
723 LoadAndWait("/find_in_page_two_frames.html");
724 auto options = blink::mojom::FindOptions::New();
725 options->run_synchronously_for_testing = true;
726
727 Find("result", options.Clone());
728 delegate()->WaitForFinalReply();
729 FindResults results = delegate()->GetFindResults();
730 EXPECT_EQ(last_request_id(), results.request_id);
731 EXPECT_EQ(6, results.number_of_matches);
732 EXPECT_EQ(1, results.active_match_ordinal);
Avi Drissmanc91bd8e2021-04-19 23:58:44733 EXPECT_TRUE(ExecJs(shell(),
734 "document.body.removeChild("
735 "document.querySelectorAll('iframe')[0])"));
Rakina Zata Amnie6da4982019-02-07 05:40:04736
737 Find("result", options.Clone());
738 delegate()->WaitForFinalReply();
739 results = delegate()->GetFindResults();
740 EXPECT_EQ(last_request_id(), results.request_id);
741 EXPECT_EQ(3, results.number_of_matches);
742 EXPECT_EQ(1, results.active_match_ordinal);
743}
744
paulmeyerd3b32d52016-09-07 22:24:55745IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindInPage_Issue644448)) {
746 TestNavigationObserver navigation_observer(contents());
Alex Moshchukaeb20fe32019-09-25 17:40:01747 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
paulmeyerd3b32d52016-09-07 22:24:55748 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
749
Rakina Zata Amni3f77dff2018-09-08 16:19:43750 auto default_options = blink::mojom::FindOptions::New();
751 default_options->run_synchronously_for_testing = true;
752 Find("result", default_options.Clone());
paulmeyerd3b32d52016-09-07 22:24:55753 delegate()->WaitForFinalReply();
754
755 // Initially, there are no matches on the page.
756 FindResults results = delegate()->GetFindResults();
757 EXPECT_EQ(last_request_id(), results.request_id);
758 EXPECT_EQ(0, results.number_of_matches);
759 EXPECT_EQ(0, results.active_match_ordinal);
760
761 // Load a page with matches.
762 LoadAndWait("/find_in_simple_page.html");
763
Rakina Zata Amni3f77dff2018-09-08 16:19:43764 Find("result", default_options.Clone());
paulmeyerd3b32d52016-09-07 22:24:55765 delegate()->WaitForFinalReply();
766
767 // There should now be matches found. When the bug was present, there were
768 // still no matches found.
769 results = delegate()->GetFindResults();
770 EXPECT_EQ(last_request_id(), results.request_id);
771 EXPECT_EQ(5, results.number_of_matches);
paulmeyerd3b32d52016-09-07 22:24:55772}
773
Xiaohan Wang1ecfd002022-01-19 22:33:10774#if BUILDFLAG(IS_ANDROID)
WangHui66701ae2020-11-09 20:01:29775// Tests empty active match rect when kWrapAround is false.
776IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, EmptyActiveMatchRect) {
777 LoadAndWait("/find_in_page.html");
778
779 // kWrapAround is false by default.
780 auto default_options = blink::mojom::FindOptions::New();
781 default_options->run_synchronously_for_testing = true;
782 Find("result 01", default_options.Clone());
783 delegate()->WaitForFinalReply();
784 EXPECT_EQ(1, delegate()->GetFindResults().number_of_matches);
785
786 // Request the find match rects.
787 contents()->RequestFindMatchRects(-1);
788 delegate()->WaitForMatchRects();
789 const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
790
791 // The first match should be active.
792 EXPECT_EQ(rects[0], delegate()->active_match_rect());
793
794 Find("result 00", default_options.Clone());
795 delegate()->WaitForFinalReply();
796 EXPECT_EQ(1, delegate()->GetFindResults().number_of_matches);
797
798 // Request the find match rects.
799 contents()->RequestFindMatchRects(-1);
800 delegate()->WaitForMatchRects();
801
802 // The active match rect should be empty.
803 EXPECT_EQ(gfx::RectF(), delegate()->active_match_rect());
804}
805
WangHui9677e35e2021-01-16 02:52:20806class MainFrameSizeChangedWaiter : public WebContentsObserver {
807 public:
808 MainFrameSizeChangedWaiter(WebContents* web_contents)
809 : WebContentsObserver(web_contents) {}
810 void Wait() { run_loop_.Run(); }
811
812 private:
813 void FrameSizeChanged(RenderFrameHost* render_frame_host,
814 const gfx::Size& frame_size) override {
Antia Puentesaa86ac82022-11-15 18:51:55815 if (render_frame_host->IsInPrimaryMainFrame())
WangHui9677e35e2021-01-16 02:52:20816 run_loop_.Quit();
817 }
818
819 base::RunLoop run_loop_;
820};
821
822// Tests match rects in the iframe are updated with the size of the main frame,
823// and the active match rect should be in it.
824IN_PROC_BROWSER_TEST_F(FindRequestManagerTest,
825 RectsUpdateWhenMainFrameSizeChanged) {
826 LoadAndWait("/find_in_page.html");
827
828 // Make a initial size for native view.
829 const int kWidth = 1080;
830 const int kHeight = 1286;
831 gfx::Size size(kWidth, kHeight);
832 contents()->GetNativeView()->OnSizeChanged(kWidth, kHeight);
833 contents()->GetNativeView()->OnPhysicalBackingSizeChanged(size);
834
835 // Make a FindRequest for "result".
836 auto options = blink::mojom::FindOptions::New();
837 options->run_synchronously_for_testing = true;
838 Find("result", options->Clone());
839 delegate()->WaitForFinalReply();
Peter Kastingd534d732022-09-01 18:14:37840 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
WangHui9677e35e2021-01-16 02:52:20841
842 contents()->RequestFindMatchRects(-1);
843 delegate()->WaitForMatchRects();
844
845 // Change the size of native view.
846 const int kNewHeight = 2121;
847 size = gfx::Size(kWidth, kNewHeight);
848 contents()->GetNativeView()->OnSizeChanged(kWidth, kNewHeight);
849 contents()->GetNativeView()->OnPhysicalBackingSizeChanged(size);
850
851 // Wait for the size of the mainframe to change, and then the position
852 // of match rects should change as expected.
853 MainFrameSizeChangedWaiter(contents()).Wait();
854
855 contents()->RequestFindMatchRects(-1);
856 delegate()->WaitForMatchRects();
857 std::vector<gfx::RectF> new_rects = delegate()->find_match_rects();
858
859 // The first match should be active.
860 EXPECT_EQ(new_rects[0], delegate()->active_match_rect());
861
862 // Check that all active rects (including iframe) matches with corresponding
863 // match rect.
864 for (int i = 1; i < 19; i++) {
865 options->new_session = false;
866 options->forward = true;
867 Find("result", options->Clone());
868 delegate()->WaitForFinalReply();
869
Peter Kastingd534d732022-09-01 18:14:37870 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
WangHui9677e35e2021-01-16 02:52:20871
872 // Request the find match rects.
873 contents()->RequestFindMatchRects(-1);
874 delegate()->WaitForMatchRects();
875 new_rects = delegate()->find_match_rects();
876
877 // The active rect should be equal to the corresponding match rect.
878 EXPECT_EQ(new_rects[i], delegate()->active_match_rect());
879 }
880}
881
W. James MacLean92a45192018-12-20 20:09:55882// TODO(wjmaclean): This test, if re-enabled, may require work to make it
883// OOPIF-compatible.
paulmeyerc8cb7cb2016-06-07 01:14:19884// Tests requesting find match rects.
paulmeyercc9a00f92016-07-06 13:44:27885IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindMatchRects)) {
paulmeyerc8cb7cb2016-06-07 01:14:19886 LoadAndWait("/find_in_page.html");
Joey Arhar0574fb752022-03-29 00:45:16887 if (test_with_oopif())
W. James MacLean92a45192018-12-20 20:09:55888 MakeChildFrameCrossProcess();
paulmeyerc8cb7cb2016-06-07 01:14:19889
Rakina Zata Amni3f77dff2018-09-08 16:19:43890 auto default_options = blink::mojom::FindOptions::New();
891 default_options->run_synchronously_for_testing = true;
892 Find("result", default_options.Clone());
paulmeyer2f06e612016-08-10 17:39:02893 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19894 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
895
896 // Request the find match rects.
897 contents()->RequestFindMatchRects(-1);
898 delegate()->WaitForMatchRects();
899 const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
900
901 // The first match should be active.
902 EXPECT_EQ(rects[0], delegate()->active_match_rect());
903
904 // All results after the first two should be between them in find-in-page
905 // coordinates. This is because results 2 to 19 are inside an iframe located
906 // between results 0 and 1. This applies to the fixed div too.
907 EXPECT_LT(rects[0].y(), rects[1].y());
908 for (int i = 2; i < 19; ++i) {
909 EXPECT_LT(rects[0].y(), rects[i].y());
910 EXPECT_GT(rects[1].y(), rects[i].y());
911 }
912
913 // Result 3 should be below results 2 and 4. This is caused by the CSS
914 // transform in the containing div. If the transform doesn't work then result
915 // 3 will be between results 2 and 4.
916 EXPECT_GT(rects[3].y(), rects[2].y());
917 EXPECT_GT(rects[3].y(), rects[4].y());
918
919 // Results 6, 7, 8 and 9 should be one below the other in that same order. If
920 // overflow:scroll is not properly handled then result 8 would be below result
921 // 9 or result 7 above result 6 depending on the scroll.
922 EXPECT_LT(rects[6].y(), rects[7].y());
923 EXPECT_LT(rects[7].y(), rects[8].y());
924 EXPECT_LT(rects[8].y(), rects[9].y());
925
926 // Results 11, 12, 13 and 14 should be between results 10 and 15, as they are
927 // inside the table.
928 EXPECT_GT(rects[11].y(), rects[10].y());
929 EXPECT_GT(rects[12].y(), rects[10].y());
930 EXPECT_GT(rects[13].y(), rects[10].y());
931 EXPECT_GT(rects[14].y(), rects[10].y());
932 EXPECT_LT(rects[11].y(), rects[15].y());
933 EXPECT_LT(rects[12].y(), rects[15].y());
934 EXPECT_LT(rects[13].y(), rects[15].y());
935 EXPECT_LT(rects[14].y(), rects[15].y());
936
937 // Result 11 should be above results 12, 13 and 14 as it's in the table
938 // header.
939 EXPECT_LT(rects[11].y(), rects[12].y());
940 EXPECT_LT(rects[11].y(), rects[13].y());
941 EXPECT_LT(rects[11].y(), rects[14].y());
942
943 // Result 11 should also be right of results 12, 13 and 14 because of the
944 // colspan.
945 EXPECT_GT(rects[11].x(), rects[12].x());
946 EXPECT_GT(rects[11].x(), rects[13].x());
947 EXPECT_GT(rects[11].x(), rects[14].x());
948
949 // Result 12 should be left of results 11, 13 and 14 in the table layout.
950 EXPECT_LT(rects[12].x(), rects[11].x());
951 EXPECT_LT(rects[12].x(), rects[13].x());
952 EXPECT_LT(rects[12].x(), rects[14].x());
953
954 // Results 13, 12 and 14 should be one above the other in that order because
955 // of the rowspan and vertical-align: middle by default.
956 EXPECT_LT(rects[13].y(), rects[12].y());
957 EXPECT_LT(rects[12].y(), rects[14].y());
958
959 // Result 16 should be below result 15.
960 EXPECT_GT(rects[15].y(), rects[14].y());
961
962 // Result 18 should be normalized with respect to the position:relative div,
963 // and not it's immediate containing div. Consequently, result 18 should be
964 // above result 17.
965 EXPECT_GT(rects[17].y(), rects[18].y());
966}
967
W. James MacLean92a45192018-12-20 20:09:55968namespace {
969
Antonio Gomes014265dcd2020-04-24 04:22:46970class ZoomToFindInPageRectMessageFilter
971 : public blink::mojom::FrameWidgetHostInterceptorForTesting {
W. James MacLean92a45192018-12-20 20:09:55972 public:
Antonio Gomes014265dcd2020-04-24 04:22:46973 ZoomToFindInPageRectMessageFilter(RenderWidgetHostImpl* rwhi)
974 : impl_(rwhi->frame_widget_host_receiver_for_testing().SwapImplForTesting(
975 this)),
W. James MacLean92a45192018-12-20 20:09:55976 widget_message_seen_(false) {}
Peter Boström828b9022021-09-21 02:28:43977
978 ZoomToFindInPageRectMessageFilter(const ZoomToFindInPageRectMessageFilter&) =
979 delete;
980 ZoomToFindInPageRectMessageFilter& operator=(
981 const ZoomToFindInPageRectMessageFilter&) = delete;
982
Antonio Gomes014265dcd2020-04-24 04:22:46983 ~ZoomToFindInPageRectMessageFilter() override {}
W. James MacLean92a45192018-12-20 20:09:55984
Antonio Gomes014265dcd2020-04-24 04:22:46985 blink::mojom::FrameWidgetHost* GetForwardingInterface() override {
986 return impl_;
W. James MacLean92a45192018-12-20 20:09:55987 }
988
989 void Reset() {
990 widget_rect_seen_ = gfx::Rect();
991 widget_message_seen_ = false;
992 }
993
994 void WaitForWidgetHostMessage() {
995 if (widget_message_seen_)
996 return;
997
998 base::RunLoop run_loop;
999 quit_closure_ = run_loop.QuitClosure();
1000 run_loop.Run();
1001 }
1002
1003 gfx::Rect& widget_message_rect() { return widget_rect_seen_; }
1004
1005 private:
Antonio Gomes014265dcd2020-04-24 04:22:461006 void ZoomToFindInPageRectInMainFrame(const gfx::Rect& rect_to_zoom) override {
W. James MacLean92a45192018-12-20 20:09:551007 widget_rect_seen_ = rect_to_zoom;
1008 widget_message_seen_ = true;
1009 if (!quit_closure_.is_null())
1010 std::move(quit_closure_).Run();
1011 }
1012
Keishi Hattori0e45c022021-11-27 09:25:521013 raw_ptr<blink::mojom::FrameWidgetHost> impl_;
W. James MacLean92a45192018-12-20 20:09:551014 gfx::Rect widget_rect_seen_;
1015 bool widget_message_seen_;
1016 base::OnceClosure quit_closure_;
W. James MacLean92a45192018-12-20 20:09:551017};
1018
1019} // namespace
1020
paulmeyerc8cb7cb2016-06-07 01:14:191021// Tests activating the find match nearest to a given point.
Alison Gale81f4f2c72024-04-22 19:33:311022// TODO(crbug.com/40864045): Fix flaky failures.
Andrew Grievea0c57c8882022-09-24 00:31:091023IN_PROC_BROWSER_TEST_P(FindRequestManagerTest,
1024 DISABLED_ActivateNearestFindMatch) {
paulmeyerc8cb7cb2016-06-07 01:14:191025 LoadAndWait("/find_in_page.html");
Joey Arhar0574fb752022-03-29 00:45:161026 if (test_with_oopif())
W. James MacLean92a45192018-12-20 20:09:551027 MakeChildFrameCrossProcess();
1028
Antonio Gomes014265dcd2020-04-24 04:22:461029 std::unique_ptr<ZoomToFindInPageRectMessageFilter> message_interceptor_child;
W. James MacLean92a45192018-12-20 20:09:551030
Joey Arhar0574fb752022-03-29 00:45:161031 if (test_with_oopif()) {
Antonio Gomes014265dcd2020-04-24 04:22:461032 message_interceptor_child =
1033 std::make_unique<ZoomToFindInPageRectMessageFilter>(
Lei Zhang7c46fde2021-10-18 19:09:221034 first_child()->current_frame_host()->GetRenderWidgetHost());
W. James MacLean92a45192018-12-20 20:09:551035 }
paulmeyerc8cb7cb2016-06-07 01:14:191036
Rakina Zata Amni3f77dff2018-09-08 16:19:431037 auto default_options = blink::mojom::FindOptions::New();
1038 default_options->run_synchronously_for_testing = true;
1039 Find("result", default_options.Clone());
paulmeyer2f06e612016-08-10 17:39:021040 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:191041 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
1042
W. James MacLean92a45192018-12-20 20:09:551043 auto* find_request_manager = contents()->GetFindRequestManagerForTesting();
1044
paulmeyerc8cb7cb2016-06-07 01:14:191045 // Get the find match rects.
1046 contents()->RequestFindMatchRects(-1);
1047 delegate()->WaitForMatchRects();
1048 const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
1049
Vladimir Levin8e5ac5c2022-01-14 19:40:151050 double device_scale_factor = GetFrameDeviceScaleFactor(contents());
1051
paulmeyerc8cb7cb2016-06-07 01:14:191052 // Activate matches via points inside each of the find match rects, in an
1053 // arbitrary order. Check that the correct match becomes active after each
1054 // activation.
Arthur Sonzognie007da1f2025-07-28 11:54:211055 const std::array<int, 19> order = {11, 13, 2, 0, 16, 5, 7, 10, 6, 1,
1056 15, 14, 9, 17, 18, 3, 8, 12, 4};
1057 for (const int rect_index : order) {
paulmeyer2f06e612016-08-10 17:39:021058 delegate()->MarkNextReply();
Arthur Sonzognie007da1f2025-07-28 11:54:211059 contents()->ActivateNearestFindResult(rects[rect_index].CenterPoint().x(),
1060 rects[rect_index].CenterPoint().y());
paulmeyerc8cb7cb2016-06-07 01:14:191061 delegate()->WaitForNextReply();
W. James MacLean92a45192018-12-20 20:09:551062
Arthur Sonzognie007da1f2025-07-28 11:54:211063 bool is_match_in_oopif = rect_index > 1 && test_with_oopif();
W. James MacLean92a45192018-12-20 20:09:551064 // Check widget message rect to make sure it matches.
1065 if (is_match_in_oopif) {
Antonio Gomes014265dcd2020-04-24 04:22:461066 message_interceptor_child->WaitForWidgetHostMessage();
Vladimir Levin8e5ac5c2022-01-14 19:40:151067 auto expected_rect = gfx::ScaleToEnclosingRect(
1068 message_interceptor_child->widget_message_rect(),
1069 1.f / device_scale_factor);
W. James MacLean92a45192018-12-20 20:09:551070 EXPECT_EQ(find_request_manager->GetSelectionRectForTesting(),
Vladimir Levin8e5ac5c2022-01-14 19:40:151071 expected_rect);
Antonio Gomes014265dcd2020-04-24 04:22:461072 message_interceptor_child->Reset();
W. James MacLean92a45192018-12-20 20:09:551073 }
1074
Arthur Sonzognie007da1f2025-07-28 11:54:211075 EXPECT_EQ(rect_index + 1,
1076 delegate()->GetFindResults().active_match_ordinal);
paulmeyerc8cb7cb2016-06-07 01:14:191077 }
1078}
Xiaohan Wang1ecfd002022-01-19 22:33:101079#endif // BUILDFLAG(IS_ANDROID)
paulmeyera41de3b2016-05-05 16:30:181080
arthursonzogniac627a32019-10-16 18:43:171081// Test basic find-in-page functionality after going back and forth to the same
1082// page. In particular, find-in-page should continue to work after going back to
1083// a page using the back-forward cache.
Elly Fong-Jones5ac18c12020-08-11 15:48:201084// Flaky everywhere: https://crbug.com/1115102
1085IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_HistoryBackAndForth) {
arthursonzogniac627a32019-10-16 18:43:171086 GURL url_a = embedded_test_server()->GetURL("a.com", "/find_in_page.html");
1087 GURL url_b = embedded_test_server()->GetURL("b.com", "/find_in_page.html");
1088
1089 auto test_page = [&] {
Joey Arhar0574fb752022-03-29 00:45:161090 if (test_with_oopif())
arthursonzogniac627a32019-10-16 18:43:171091 MakeChildFrameCrossProcess();
1092
1093 auto options = blink::mojom::FindOptions::New();
1094
1095 // The initial find-in-page request.
1096 Find("result", options->Clone());
1097 delegate()->WaitForFinalReply();
1098
1099 FindResults results = delegate()->GetFindResults();
1100 EXPECT_EQ(last_request_id(), results.request_id);
1101 EXPECT_EQ(19, results.number_of_matches);
1102
1103 // Iterate forward/backward over a few elements.
1104 int match_index = results.active_match_ordinal;
1105 for (int delta : {-1, -1, +1, +1, +1, +1, -1, +1, +1}) {
Russell Davis8a36226c2020-06-05 17:09:501106 options->new_session = false;
arthursonzogniac627a32019-10-16 18:43:171107 options->forward = delta > 0;
1108 // |active_match_ordinal| uses 1-based index. It belongs to [1, 19].
1109 match_index += delta;
1110 match_index = (match_index + 18) % 19 + 1;
1111
1112 Find("result", options->Clone());
1113 delegate()->WaitForFinalReply();
1114 results = delegate()->GetFindResults();
1115
1116 EXPECT_EQ(last_request_id(), results.request_id);
1117 EXPECT_EQ(19, results.number_of_matches);
1118 EXPECT_EQ(match_index, results.active_match_ordinal);
1119 }
1120 };
1121
1122 // 1) Navigate to A.
1123 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1124 test_page();
1125
1126 // 2) Navigate to B.
1127 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1128 test_page();
1129
1130 // 3) Go back to A.
1131 contents()->GetController().GoBack();
1132 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1133 test_page();
1134
1135 // 4) Go forward to B.
1136 contents()->GetController().GoForward();
1137 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1138 test_page();
1139}
1140
Scott Violet99861992023-02-08 01:20:121141class FindInPageDisabledForOriginBrowserClient
1142 : public ContentBrowserTestContentBrowserClient {
Lei Zhang7c46fde2021-10-18 19:09:221143 public:
Lei Zhang7c46fde2021-10-18 19:09:221144 // ContentBrowserClient:
1145 bool IsFindInPageDisabledForOrigin(const url::Origin& origin) override {
1146 return origin.host() == "b.com";
1147 }
1148};
1149
1150// Tests that find-in-page won't show results for origins that disabled
1151// find-in-page.
1152IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, FindInPageDisabledForOrigin) {
1153 FindInPageDisabledForOriginBrowserClient browser_client;
Lei Zhang7c46fde2021-10-18 19:09:221154
1155 // Start with a basic case to set a baseline.
1156 LoadAndWait("/find_in_page.html");
1157 url::Origin root_origin = GetOriginForFrameTreeNode(root());
1158 url::Origin child_origin = GetOriginForFrameTreeNode(first_child());
1159 EXPECT_EQ("a.com", root_origin.host());
1160 EXPECT_EQ("a.com", child_origin.host());
1161 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(root_origin));
1162 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(child_origin));
1163
1164 auto options = blink::mojom::FindOptions::New();
1165 options->run_synchronously_for_testing = true;
1166 Find("result", options->Clone());
1167 delegate()->WaitForFinalReply();
1168
1169 FindResults results = delegate()->GetFindResults();
1170 EXPECT_EQ(last_request_id(), results.request_id);
1171 EXPECT_EQ(19, results.number_of_matches);
1172
1173 // Navigate child frame to b.com.
1174 EXPECT_TRUE(NavigateToURLFromRenderer(
1175 first_child(), embedded_test_server()->GetURL(
1176 "b.com", first_child()->current_url().path())));
1177 root_origin = GetOriginForFrameTreeNode(root());
1178 child_origin = GetOriginForFrameTreeNode(first_child());
1179 EXPECT_EQ("a.com", root_origin.host());
1180 EXPECT_EQ("b.com", child_origin.host());
1181 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(root_origin));
1182 EXPECT_TRUE(browser_client.IsFindInPageDisabledForOrigin(child_origin));
1183
1184 Find("result", options->Clone());
1185 delegate()->WaitForFinalReply();
1186
1187 // Given the custom `browser_client` disabled find-in-page for b.com, only the
1188 // results from the root node should show up now.
1189 results = delegate()->GetFindResults();
1190 EXPECT_EQ(last_request_id(), results.request_id);
1191 EXPECT_EQ(2, results.number_of_matches);
1192
1193 // Navigate child frame, but remain on b.com.
1194 EXPECT_TRUE(NavigateToURLFromRenderer(
1195 first_child(),
1196 embedded_test_server()->GetURL("b.com", "/find_in_simple_page.html")));
1197 root_origin = GetOriginForFrameTreeNode(root());
1198 child_origin = GetOriginForFrameTreeNode(first_child());
1199 EXPECT_EQ("a.com", root_origin.host());
1200 EXPECT_EQ("b.com", child_origin.host());
1201 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(root_origin));
1202 EXPECT_TRUE(browser_client.IsFindInPageDisabledForOrigin(child_origin));
1203
1204 // Results from the child frame on b.com still do not show up.
1205 results = delegate()->GetFindResults();
1206 EXPECT_EQ(last_request_id(), results.request_id);
1207 EXPECT_EQ(2, results.number_of_matches);
1208
1209 // Navigate child frame to a.com again.
1210 EXPECT_TRUE(NavigateToURLFromRenderer(
1211 first_child(),
1212 embedded_test_server()->GetURL("a.com", "/find_in_simple_page.html")));
1213 root_origin = GetOriginForFrameTreeNode(root());
1214 child_origin = GetOriginForFrameTreeNode(first_child());
1215 EXPECT_EQ("a.com", root_origin.host());
1216 EXPECT_EQ("a.com", child_origin.host());
1217 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(root_origin));
1218 EXPECT_FALSE(browser_client.IsFindInPageDisabledForOrigin(child_origin));
1219
1220 Find("result", options->Clone());
1221 delegate()->WaitForFinalReply();
1222
1223 // Since the child frame is now on a.com, find-in-page is enabled, so its
1224 // results show up again.
1225 results = delegate()->GetFindResults();
1226 EXPECT_EQ(last_request_id(), results.request_id);
1227 EXPECT_EQ(7, results.number_of_matches);
Lei Zhang7c46fde2021-10-18 19:09:221228}
1229
Miyoung Shinbf4c40c2021-10-21 11:00:231230class FindTestWebContentsPrerenderingDelegate
1231 : public FindTestWebContentsDelegate {
1232 public:
Johanna0b3e9b2023-01-19 23:23:351233 PreloadingEligibility IsPrerender2Supported(
Hiroki Nakagawa662e7cc2024-12-16 05:15:361234 WebContents& web_contents,
1235 PreloadingTriggerType trigger_type) override {
Johanna0b3e9b2023-01-19 23:23:351236 return PreloadingEligibility::kEligible;
Hiroki Nakagawa4ae11b882022-02-03 03:24:111237 }
Miyoung Shinbf4c40c2021-10-21 11:00:231238};
1239
1240class FindRequestManagerPrerenderingTest : public FindRequestManagerTest {
1241 public:
1242 FindRequestManagerPrerenderingTest()
1243 : prerender_helper_(base::BindRepeating(
1244 &FindRequestManagerPrerenderingTest::web_contents,
1245 base::Unretained(this))) {}
1246 ~FindRequestManagerPrerenderingTest() override = default;
1247
1248 void SetUpOnMainThread() override {
1249 FindRequestManagerTest::SetUpOnMainThread();
1250 contents()->SetDelegate(&delegate_);
1251 }
1252
1253 content::test::PrerenderTestHelper* prerender_helper() {
1254 return &prerender_helper_;
1255 }
1256
1257 content::WebContents* web_contents() { return shell()->web_contents(); }
1258
1259 private:
1260 content::test::PrerenderTestHelper prerender_helper_;
1261 FindTestWebContentsPrerenderingDelegate delegate_;
1262};
1263
1264// Tests that find-in-page won't show results inside a prerendering page.
1265IN_PROC_BROWSER_TEST_F(FindRequestManagerPrerenderingTest, Basic) {
1266 EXPECT_TRUE(
1267 NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
1268 auto options = blink::mojom::FindOptions::New();
1269 options->run_synchronously_for_testing = true;
1270 Find("result", options->Clone());
1271 delegate()->WaitForFinalReply();
1272
1273 // Do a find-in-page on an empty page.
1274 FindResults results = delegate()->GetFindResults();
1275 EXPECT_EQ(last_request_id(), results.request_id);
1276 EXPECT_EQ(0, results.number_of_matches);
1277
1278 // Load a page that has 5 matches for "result" in the prerender.
1279 auto prerender_url =
1280 embedded_test_server()->GetURL("/find_in_simple_page.html?prerendering");
1281 prerender_helper()->AddPrerender(prerender_url);
1282
1283 Find("result", options->Clone());
1284 delegate()->WaitForFinalReply();
1285
1286 results = delegate()->GetFindResults();
1287 EXPECT_EQ(last_request_id(), results.request_id);
1288 // The prerendering page shouldn't affect the results of a find-in-page .
1289 EXPECT_EQ(0, results.number_of_matches);
1290
1291 // Activate the page from the prerendering.
1292 prerender_helper()->NavigatePrimaryPage(prerender_url);
1293 Find("result", options->Clone());
1294 delegate()->WaitForFinalReply();
1295
1296 results = delegate()->GetFindResults();
1297 // The results from the prerendered page getting activated should be 5 as the
1298 // mainframe(5 results) and no subframe.
1299 EXPECT_EQ(5, results.number_of_matches);
1300}
1301
1302class FindRequestManagerTestWithBFCache : public FindRequestManagerTest {
1303 public:
1304 FindRequestManagerTestWithBFCache() {
1305 scoped_feature_list_.InitWithFeaturesAndParameters(
Ming-Ying Chung72636182023-02-28 12:16:041306 GetDefaultEnabledBackForwardCacheFeaturesForTesting(
1307 /*ignore_outstanding_network_request=*/false),
1308 GetDefaultDisabledBackForwardCacheFeaturesForTesting());
Miyoung Shinbf4c40c2021-10-21 11:00:231309 }
1310 ~FindRequestManagerTestWithBFCache() override = default;
1311
1312 content::RenderFrameHost* render_frame_host() {
Dave Tapuska327c06c92022-06-13 20:31:511313 return contents()->GetPrimaryMainFrame();
Miyoung Shinbf4c40c2021-10-21 11:00:231314 }
1315
1316 private:
1317 base::test::ScopedFeatureList scoped_feature_list_;
1318};
1319
1320// Test basic find-in-page functionality when a page gets into and out of
1321// BFCache.
1322IN_PROC_BROWSER_TEST_F(FindRequestManagerTestWithBFCache, Basic) {
1323 GURL url_a = embedded_test_server()->GetURL("a.com", "/find_in_page.html");
1324 GURL url_b =
1325 embedded_test_server()->GetURL("b.com", "/find_in_simple_page.html");
1326
1327 auto options = blink::mojom::FindOptions::New();
1328 auto expect_match_results = [&](int expected_number_of_matches) {
1329 // The initial find-in-page request.
1330 Find("result", options->Clone());
1331 delegate()->WaitForFinalReply();
1332
1333 FindResults results = delegate()->GetFindResults();
1334 EXPECT_EQ(last_request_id(), results.request_id);
1335 EXPECT_EQ(expected_number_of_matches, results.number_of_matches);
1336 };
1337
1338 // 1) Navigate to A.
1339 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1340 content::RenderFrameHostWrapper rfh_a(render_frame_host());
1341 // The results from the page A should be 19 as the mainframe(2 results) and
1342 // the new subframe (17 results).
1343 expect_match_results(19);
1344
1345 // 2) Navigate to B.
1346 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1347 content::RenderFrameHostWrapper rfh_b(render_frame_host());
1348 // The results from the page B should be 5 as the mainframe(5 results) and no
1349 // subframe.
1350 expect_match_results(5);
1351
1352 // Ensure A is cached.
1353 EXPECT_EQ(rfh_a->GetLifecycleState(),
1354 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
1355
1356 // 3) Go back to A.
1357 contents()->GetController().GoBack();
1358 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1359 // |rfh_a| should become the active frame.
Antia Puentesaa86ac82022-11-15 18:51:551360 EXPECT_TRUE(rfh_a->IsInPrimaryMainFrame());
Miyoung Shinbf4c40c2021-10-21 11:00:231361 // The results from the page A should be 19 as the mainframe(2 results) and
1362 // the new subframe (17 results).
1363 expect_match_results(19);
1364
1365 // Ensure B is cached.
1366 EXPECT_EQ(rfh_b->GetLifecycleState(),
1367 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
1368
1369 // 4) Go forward to B.
1370 contents()->GetController().GoForward();
1371 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1372 // |rfh_b| should become the active frame.
Antia Puentesaa86ac82022-11-15 18:51:551373 EXPECT_TRUE(rfh_b->IsInPrimaryMainFrame());
Miyoung Shinbf4c40c2021-10-21 11:00:231374 // The results from the page B should be 5 as the mainframe(5 results) and no
1375 // subframe.
1376 expect_match_results(5);
1377}
1378
1379class WaitForFindTestWebContentsDelegate : public FindTestWebContentsDelegate {
1380 public:
1381 void WaitForFramesReply(int wait_count) {
1382 wait_count_ = wait_count;
1383 EXPECT_GT(wait_count_, 0);
1384 run_loop_ = std::make_unique<base::RunLoop>();
1385 run_loop_->Run();
1386 run_loop_.reset();
1387 }
1388
1389 void TryToStopWaiting() {
1390 if (run_loop_ && !--wait_count_)
1391 run_loop_->Quit();
1392 }
1393
1394 bool ShouldWait() { return wait_count_ > 0; }
1395
1396 private:
1397 int wait_count_ = 0;
1398 std::unique_ptr<base::RunLoop> run_loop_;
1399};
1400
Gyuyoung Kima855ff5d2021-11-19 07:24:351401class FindRequestManagerFencedFrameTest : public FindRequestManagerTest {
1402 public:
1403 FindRequestManagerFencedFrameTest() = default;
1404 ~FindRequestManagerFencedFrameTest() override = default;
1405 FindRequestManagerFencedFrameTest(const FindRequestManagerFencedFrameTest&) =
1406 delete;
1407
1408 FindRequestManagerFencedFrameTest& operator=(
1409 const FindRequestManagerFencedFrameTest&) = delete;
1410
1411 content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
1412 return fenced_frame_helper_;
1413 }
1414
1415 content::WebContents* GetWebContents() { return shell()->web_contents(); }
1416
1417 int find_request_queue_size() {
1418 return contents()
1419 ->GetFindRequestManagerForTesting()
1420 ->find_request_queue_.size();
1421 }
1422
1423 bool CheckFrame(RenderFrameHost* render_frame_host) const {
1424 return contents()->GetFindRequestManagerForTesting()->CheckFrame(
1425 render_frame_host);
1426 }
1427
1428 private:
1429 content::test::FencedFrameTestHelper fenced_frame_helper_;
1430};
1431
1432// This find-in-page client will make the find-request-queue not empty so that
1433// we can test a fenced frame doesn't clear the find-request-queue when it's
1434// deleted. To keep the find-request-queue not empty, this class
1435// intercepts the Mojo methods calls, and changes the FindMatchUpdateType to
1436// kMoreUpdatesComing (including those that were marked as kFinalUpdate), so
1437// that the find-request-queue won't get popped and will stay non-empty.
1438class NeverFinishFencedFrameFindInPageClient : public FindInPageClient {
1439 public:
1440 NeverFinishFencedFrameFindInPageClient(
1441 FindRequestManager* find_request_manager,
1442 RenderFrameHostImpl* rfh)
1443 : FindInPageClient(find_request_manager, rfh) {
1444 content::WebContents* web_contents =
1445 content::WebContents::FromRenderFrameHost(rfh);
1446 delegate_ = static_cast<WaitForFindTestWebContentsDelegate*>(
1447 web_contents->GetDelegate());
1448 }
1449 ~NeverFinishFencedFrameFindInPageClient() override = default;
1450
1451 // blink::mojom::FindInPageClient overrides
1452 void SetNumberOfMatches(
1453 int request_id,
1454 unsigned int current_number_of_matches,
1455 blink::mojom::FindMatchUpdateType update_type) override {
1456 update_type = blink::mojom::FindMatchUpdateType::kMoreUpdatesComing;
1457 FindInPageClient::SetNumberOfMatches(request_id, current_number_of_matches,
1458 update_type);
1459 }
1460
1461 // Do nothing on SetActiveMatch() calls, since this can potentially trigger
1462 // FindRequestManager::AdvanceQueue() and pop an item from the
1463 // find-request-queue.
1464 void SetActiveMatch(int request_id,
1465 const gfx::Rect& active_match_rect,
1466 int active_match_ordinal,
1467 blink::mojom::FindMatchUpdateType update_type) override {}
1468
1469 private:
Keishi Hattori0e45c022021-11-27 09:25:521470 raw_ptr<WaitForFindTestWebContentsDelegate> delegate_;
Gyuyoung Kima855ff5d2021-11-19 07:24:351471};
1472
1473static std::unique_ptr<FindInPageClient> CreateFencedFrameFindInPageClient(
1474 FindRequestManager* find_request_manager,
1475 RenderFrameHostImpl* rfh) {
1476 return std::make_unique<NeverFinishFencedFrameFindInPageClient>(
1477 find_request_manager, rfh);
1478}
1479
1480// Tests that a main frame, a sub frame, and a fenced frame clear the
1481// find-request-queue when the fenced frame is deleted.
1482IN_PROC_BROWSER_TEST_F(FindRequestManagerFencedFrameTest,
1483 OnlyPrimaryMainFrameClearsFindRequestQueue) {
1484 WaitForFindTestWebContentsDelegate delegate;
1485 contents()->SetDelegate(&delegate);
1486
1487 // Override the FindInPageClient class so that we can intercept the Mojo
1488 // methods calls to keep its find request queue non-empty.
1489 contents()
1490 ->GetFindRequestManagerForTesting()
1491 ->SetCreateFindInPageClientFunctionForTesting(
1492 &CreateFencedFrameFindInPageClient);
1493
1494 LoadAndWait("/find_in_page.html");
1495 auto options = blink::mojom::FindOptions::New();
1496 options->run_synchronously_for_testing = true;
1497 Find("result", options.Clone());
Artur3bb150aa2022-10-18 02:36:051498 // Initial find request is pop from the queue immediately so we make a second
1499 // find request.
1500 options->new_session = false;
1501 Find("result", options.Clone());
Gyuyoung Kima855ff5d2021-11-19 07:24:351502
1503 // Create a fenced frame.
1504 GURL find_test_url =
1505 embedded_test_server()->GetURL("/fenced_frames/find_in_page.html");
1506 content::RenderFrameHost* fenced_frame_host =
1507 fenced_frame_test_helper().CreateFencedFrame(
Dave Tapuska327c06c92022-06-13 20:31:511508 GetWebContents()->GetPrimaryMainFrame(), find_test_url);
Gyuyoung Kima855ff5d2021-11-19 07:24:351509 EXPECT_NE(nullptr, fenced_frame_host);
1510 EXPECT_TRUE(CheckFrame(fenced_frame_host));
1511 EXPECT_EQ(find_request_queue_size(), 1);
1512 EXPECT_EQ(last_request_id(), delegate.GetFindResults().request_id);
1513
1514 // Navigate the fenced frame, this won't cause the find request queue to be
1515 // cleared, since it's not a primary main frame.
Rakina Zata Amni364eb5d2023-03-22 13:19:001516 fenced_frame_host = fenced_frame_test_helper().NavigateFrameInFencedFrameTree(
1517 fenced_frame_host, find_test_url);
Gyuyoung Kima855ff5d2021-11-19 07:24:351518 EXPECT_TRUE(CheckFrame(fenced_frame_host));
1519 EXPECT_EQ(find_request_queue_size(), 1);
1520 EXPECT_EQ(last_request_id(), delegate.GetFindResults().request_id);
1521
1522 // Navigate the non-fenced frame subframe, this also won't cause the find
1523 // request queue to be cleared, since it's not a primary main frame.
1524 FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
1525 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), find_test_url));
1526 EXPECT_TRUE(CheckFrame(root->child_at(0)->current_frame_host()));
1527 EXPECT_EQ(find_request_queue_size(), 1);
1528 EXPECT_EQ(last_request_id(), delegate.GetFindResults().request_id);
1529
1530 // Navigate the main frame, this causes the find request queue to be cleared,
1531 // since it's the primary main frame.
1532 EXPECT_TRUE(NavigateToURL(shell(), find_test_url));
Dave Tapuska327c06c92022-06-13 20:31:511533 EXPECT_TRUE(CheckFrame(GetWebContents()->GetPrimaryMainFrame()));
Gyuyoung Kima855ff5d2021-11-19 07:24:351534 EXPECT_EQ(find_request_queue_size(), 0);
1535}
1536
Miyoung Shinbf4c40c2021-10-21 11:00:231537// This find-in-page client will make it so that we never stop listening for
1538// find-in-page updates only for subframes, through modifying final updates to
1539// be marked as non-final updates. It helps us to simulate various things that
1540// can happen before a find-in-page session finishes (e.g. navigation,
1541// lifecycle state change) without finishing the find session.
1542class NeverFinishSubframeFindInPageClient : public FindInPageClient {
1543 public:
1544 NeverFinishSubframeFindInPageClient(FindRequestManager* find_request_manager,
1545 RenderFrameHostImpl* rfh)
1546 : FindInPageClient(find_request_manager, rfh), rfh_(rfh) {
1547 content::WebContents* web_contents =
1548 content::WebContents::FromRenderFrameHost(rfh);
1549 delegate_ = static_cast<WaitForFindTestWebContentsDelegate*>(
1550 web_contents->GetDelegate());
1551 }
1552 ~NeverFinishSubframeFindInPageClient() override = default;
1553
1554 // blink::mojom::FindInPageClient overrides
1555 void SetNumberOfMatches(
1556 int request_id,
1557 unsigned int current_number_of_matches,
1558 blink::mojom::FindMatchUpdateType update_type) override {
1559 bool should_wait = delegate_->ShouldWait();
1560 if (update_type == blink::mojom::FindMatchUpdateType::kFinalUpdate)
1561 delegate_->TryToStopWaiting();
1562
1563 // Make sure subframe's reply is not marked as the final update.
1564 if (!rfh_->is_main_frame() && should_wait)
1565 update_type = blink::mojom::FindMatchUpdateType::kMoreUpdatesComing;
1566
1567 FindInPageClient::SetNumberOfMatches(request_id, current_number_of_matches,
1568 update_type);
1569 }
1570
1571 void SetActiveMatch(int request_id,
1572 const gfx::Rect& active_match_rect,
1573 int active_match_ordinal,
1574 blink::mojom::FindMatchUpdateType update_type) override {
1575 if (update_type == blink::mojom::FindMatchUpdateType::kFinalUpdate)
1576 delegate_->TryToStopWaiting();
1577
1578 // Make sure subframe's reply is not marked as the final update.
1579 if (!rfh_->is_main_frame())
1580 update_type = blink::mojom::FindMatchUpdateType::kMoreUpdatesComing;
1581
1582 FindInPageClient::SetActiveMatch(request_id, active_match_rect,
1583 active_match_ordinal, update_type);
1584 }
1585
1586 private:
Keishi Hattori0e45c022021-11-27 09:25:521587 raw_ptr<RenderFrameHostImpl> rfh_;
1588 raw_ptr<WaitForFindTestWebContentsDelegate> delegate_;
Miyoung Shinbf4c40c2021-10-21 11:00:231589};
1590
1591class FindRequestManagerTestObserver : public WebContentsObserver {
1592 public:
1593 explicit FindRequestManagerTestObserver(WebContents* web_contents)
1594 : WebContentsObserver(web_contents) {}
1595
1596 void DidFinishLoad(RenderFrameHost* render_frame_host,
1597 const GURL& url) override {
1598 auto* delegate = static_cast<FindTestWebContentsDelegate*>(
1599 web_contents()->GetDelegate());
1600 delegate->MarkNextReply();
1601 }
1602};
1603
1604static std::unique_ptr<FindInPageClient> CreateFindInPageClient(
1605 FindRequestManager* find_request_manager,
1606 RenderFrameHostImpl* rfh) {
1607 return std::make_unique<NeverFinishSubframeFindInPageClient>(
1608 find_request_manager, rfh);
1609}
1610
Miyoung Shin03191752022-03-29 09:20:511611enum class FrameSiteType {
1612 kSameOrigin,
1613 kCrossOrigin,
1614};
1615
1616enum class FrameTestType {
1617 kIFrame,
1618 kFencedFrame,
1619};
1620
1621class FindRequestManagerTestWithTestConfig
1622 : public FindRequestManagerTestBase,
1623 public testing::WithParamInterface<
1624 ::testing::tuple<FrameSiteType, FrameTestType>> {
1625 public:
1626 FrameSiteType GetFrameSiteType() const { return std::get<0>(GetParam()); }
1627
1628 FrameTestType GetFrameTestType() const { return std::get<1>(GetParam()); }
1629
1630 test::FencedFrameTestHelper& fenced_frame_test_helper() {
1631 return fenced_frame_test_helper_;
1632 }
1633
1634 private:
1635 test::FencedFrameTestHelper fenced_frame_test_helper_;
1636};
1637
1638INSTANTIATE_TEST_SUITE_P(
1639 FindRequestManagers,
1640 FindRequestManagerTestWithTestConfig,
1641 ::testing::Combine(::testing::Values(FrameSiteType::kSameOrigin,
1642 FrameSiteType::kCrossOrigin),
1643 ::testing::Values(FrameTestType::kIFrame,
1644 FrameTestType::kFencedFrame)));
1645
Miyoung Shinbf4c40c2021-10-21 11:00:231646// Tests that the previous results from old document are removed and we get the
1647// new results from the new document when we navigate the subframe that
1648// hasn't finished the find-in-page session to the new document.
Alison Gale81f4f2c72024-04-22 19:33:311649// TODO(crbug.com/40220234): Fix flakiness and reenable the test.
Ioana Pandele234e4bf2023-10-23 17:03:111650#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
Gyuyoung Kimdbfec692025-05-03 01:30:221651 BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
Hui Yingsta235c6e52022-03-30 00:41:141652#define MAYBE_NavigateFrameDuringFind DISABLED_NavigateFrameDuringFind
1653#else
1654#define MAYBE_NavigateFrameDuringFind NavigateFrameDuringFind
1655#endif
Miyoung Shin03191752022-03-29 09:20:511656IN_PROC_BROWSER_TEST_P(FindRequestManagerTestWithTestConfig,
Hui Yingsta235c6e52022-03-30 00:41:141657 MAYBE_NavigateFrameDuringFind) {
Miyoung Shinbf4c40c2021-10-21 11:00:231658 WaitForFindTestWebContentsDelegate delegate;
1659 contents()->SetDelegate(&delegate);
1660
Miyoung Shin03191752022-03-29 09:20:511661 // 1) Load a main frame with 5 matches.
1662 LoadAndWait("/find_in_simple_page.html");
1663
1664 GURL frame_url =
1665 embedded_test_server()->GetURL("a.com", "/find_in_page_frame.html");
1666 content::RenderFrameHost* fenced_frame_host = nullptr;
1667
1668 // 2) Load a subframe with 17 matches.
1669 if (GetFrameTestType() == FrameTestType::kIFrame) {
1670 EXPECT_TRUE(ExecJs(shell(), JsReplace(R"(
1671 var frame = document.createElement('iframe');
1672 frame.src = $1;
1673 document.body.appendChild(frame);
1674 )",
1675 frame_url)));
1676 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
1677 } else {
1678 fenced_frame_host = fenced_frame_test_helper().CreateFencedFrame(
Dave Tapuska327c06c92022-06-13 20:31:511679 shell()->web_contents()->GetPrimaryMainFrame(), frame_url);
Miyoung Shin03191752022-03-29 09:20:511680 EXPECT_NE(nullptr, fenced_frame_host);
1681 }
Miyoung Shinbf4c40c2021-10-21 11:00:231682
1683 auto options = blink::mojom::FindOptions::New();
1684 options->run_synchronously_for_testing = true;
1685
1686 // 2) First try a normal find-in-page session that finishes completely.
1687 Find("result", options.Clone());
1688 delegate.WaitForFinalReply();
1689
1690 FindResults results = delegate.GetFindResults();
1691 EXPECT_EQ(last_request_id(), results.request_id);
Miyoung Shin03191752022-03-29 09:20:511692 EXPECT_EQ(22, results.number_of_matches);
Miyoung Shinbf4c40c2021-10-21 11:00:231693 EXPECT_EQ(1, results.active_match_ordinal);
1694
1695 // 3) Override the FindInPageClient class so that we can simulate a subframe
1696 // change that happens in the middle of a find-in-page session.
1697 contents()
1698 ->GetFindRequestManagerForTesting()
1699 ->SetCreateFindInPageClientFunctionForTesting(&CreateFindInPageClient);
1700
1701 // 4) Try to find-in-page again, but this time the subframe won't be marked as
1702 // finished before it got navigated.
1703 Find("result", options.Clone());
1704
1705 // 5) Wait for the find request of the main frame's reply.
1706 delegate.WaitForFramesReply(2);
1707 results = delegate.GetFindResults();
1708 EXPECT_EQ(last_request_id(), results.request_id);
Miyoung Shin03191752022-03-29 09:20:511709 EXPECT_EQ(22, results.number_of_matches);
Miyoung Shinbf4c40c2021-10-21 11:00:231710 EXPECT_EQ(2, results.active_match_ordinal);
1711
1712 // 6) Navigate the subframe that hasn't finished the find-in-page session to a
1713 // document with 5 matches. This will trigger a find-in-page request on the
1714 // new document on the unfinished subframe, and removes the result from the
1715 // old document.
1716 FindRequestManagerTestObserver observer(contents());
Miyoung Shin03191752022-03-29 09:20:511717 GURL url(embedded_test_server()->GetURL(
1718 GetFrameSiteType() == FrameSiteType::kSameOrigin ? "a.com" : "b.com",
1719 "/find_in_simple_page.html"));
1720 if (GetFrameTestType() == FrameTestType::kIFrame) {
1721 FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
1722 TestNavigationObserver navigation_observer(contents());
1723 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
1724 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
1725 } else {
1726 fenced_frame_test_helper().NavigateFrameInFencedFrameTree(fenced_frame_host,
1727 url);
1728 }
Miyoung Shinbf4c40c2021-10-21 11:00:231729
1730 delegate.WaitForNextReply();
1731
1732 results = delegate.GetFindResults();
1733 EXPECT_EQ(last_request_id(), results.request_id);
1734 // The results from the old subframe (17 results) is removed entirely even
1735 // when it hasn't finished, and we added the next reply from the new subframe
Miyoung Shin03191752022-03-29 09:20:511736 // (5 results). So, the final results should be 10 as the mainframe(5 results)
1737 // and the new subframe (5 results).
1738 EXPECT_EQ(10, results.number_of_matches);
Miyoung Shinbf4c40c2021-10-21 11:00:231739 EXPECT_EQ(2, results.active_match_ordinal);
1740}
1741
1742// Tests that the previous results from the old documents are removed and we
1743// get the new results from the new document when we go back to the page in
1744// BFCache from the page that hasn't finished the find-in-page session.
1745// This TC does not intentionally check the |active_match_ordinal| value,
1746// because the main frame is not focused on Android, so it has a different
1747// result on Android.
1748IN_PROC_BROWSER_TEST_F(FindRequestManagerTestWithBFCache,
1749 NavigateFrameDuringFind) {
1750 WaitForFindTestWebContentsDelegate delegate;
1751 contents()->SetDelegate(&delegate);
1752
1753 GURL url_a = embedded_test_server()->GetURL("a.com", "/find_in_page.html");
1754 GURL url_b =
1755 embedded_test_server()->GetURL("b.com", "/find_in_page_two_frames.html");
1756
1757 // 1) Load A that is a main frame with 2 matches and a subframe with 17
1758 // matches.
1759 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1760 content::RenderFrameHostWrapper rfh_a(render_frame_host());
1761
1762 // 2) Load B that is a main frame with no match and two subframes with each 3
1763 // matches.
1764 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1765 // Ensure A is cached.
1766 EXPECT_EQ(rfh_a->GetLifecycleState(),
1767 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
1768 content::RenderFrameHostWrapper rfh_b(render_frame_host());
1769
1770 // 3) Override the FindInPageClient class so that we can simulate a subframe
1771 // change that happens in the middle of a find-in-page session.
1772 contents()
1773 ->GetFindRequestManagerForTesting()
1774 ->SetCreateFindInPageClientFunctionForTesting(&CreateFindInPageClient);
1775
1776 auto options = blink::mojom::FindOptions::New();
1777 options->run_synchronously_for_testing = true;
1778
1779 // 4) Try to find-in-page again, but this time the subframe won't be marked as
1780 // finished before it goes back in the BF cache.
1781 Find("result", options.Clone());
1782
1783 // 5) Wait for replies from the main frame and the subframes.
1784 delegate.WaitForFramesReply(3);
1785 FindResults results = delegate.GetFindResults();
1786 EXPECT_EQ(last_request_id(), results.request_id);
1787 EXPECT_EQ(6, results.number_of_matches);
1788
1789 // 6) Go back to A which has a main frame with 2 matches and the subframe with
1790 // 17 matches.
1791 FindRequestManagerTestObserver observer1(contents());
1792 contents()->GetController().GoBack();
1793 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1794 // |rfh_a| should become the active frame.
Antia Puentesaa86ac82022-11-15 18:51:551795 EXPECT_TRUE(rfh_a->IsInPrimaryMainFrame());
Miyoung Shinbf4c40c2021-10-21 11:00:231796 // Ensure B is cached.
1797 EXPECT_EQ(rfh_b->GetLifecycleState(),
1798 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
1799
1800 // 7) Wait for replies from the main frame and the subframes.
1801 delegate.WaitForFramesReply(2);
1802 results = delegate.GetFindResults();
1803 EXPECT_EQ(last_request_id(), results.request_id);
1804 // The results from the old page (6 results) is removed entirely even when
1805 // it hasn't finished, and we added the next reply from the new page (19
1806 // results). So, the final results should be 19.
1807 EXPECT_EQ(19, results.number_of_matches);
1808
1809 // 8) Go forward to B which has a main frame with no match and two subframes
1810 // with each 3 matches.
1811 contents()->GetController().GoForward();
1812 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1813 // |rfh_b| should become the active frame.
Antia Puentesaa86ac82022-11-15 18:51:551814 EXPECT_TRUE(rfh_b->IsInPrimaryMainFrame());
Miyoung Shinbf4c40c2021-10-21 11:00:231815
1816 // 9) Wait for replies from the main frame and the subframes.
1817 delegate.WaitForFinalReply();
1818 results = delegate.GetFindResults();
1819 EXPECT_EQ(last_request_id(), results.request_id);
1820 // The results from the old page (19 results) is removed entirely even when
1821 // it hasn't finished, and we added the next reply from the new page (6
1822 // results). So, the final results should be 6.
1823 EXPECT_EQ(6, results.number_of_matches);
1824}
1825
1826IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, CrashDuringFind) {
1827 WaitForFindTestWebContentsDelegate delegate;
1828 contents()->SetDelegate(&delegate);
1829
1830 // 1) Load a main frame with 2 matches and a subframe with 17 matches.
1831 LoadAndWait("/find_in_page.html");
1832 MakeChildFrameCrossProcess();
1833
1834 // 2) Override the FindInPageClient class so that we can simulate a subframe
1835 // change that happens in the middle of a find-in-page session.
1836 contents()
1837 ->GetFindRequestManagerForTesting()
1838 ->SetCreateFindInPageClientFunctionForTesting(&CreateFindInPageClient);
1839
1840 auto options = blink::mojom::FindOptions::New();
1841 options->run_synchronously_for_testing = true;
1842
1843 // 3) Try to find-in-page again, but this time the subframe won't be marked as
1844 // finished before it crashed.
1845 Find("result", options.Clone());
1846
1847 // 4) Wait for the find request of the main frame's reply.
1848 delegate.WaitForFramesReply(2);
1849 FindResults results = delegate.GetFindResults();
1850 EXPECT_EQ(last_request_id(), results.request_id);
1851 EXPECT_EQ(19, results.number_of_matches);
1852 EXPECT_EQ(1, results.active_match_ordinal);
1853
1854 // 5) Crash the subframe that hasn't finished the find-in-page
1855 // session. This will remove the result from the crashed document.
1856 {
Carlos Caballero1a261f82021-11-04 15:54:031857 FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
Miyoung Shinbf4c40c2021-10-21 11:00:231858 content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
1859 content::RenderFrameDeletedObserver crash_observer(
1860 root->child_at(0)->current_frame_host());
1861 root->child_at(0)->current_frame_host()->GetProcess()->Shutdown(1);
1862 crash_observer.WaitUntilDeleted();
1863 }
1864
1865 // 6) Wait for the crashed frame to be deleted.
1866 delegate.WaitForFinalReply();
1867 results = delegate.GetFindResults();
1868 EXPECT_EQ(last_request_id(), results.request_id);
1869 // The results from the crashed subframe (17 results) is removed entirely and
1870 // only have 2 results from the main frame.
1871 EXPECT_EQ(2, results.number_of_matches);
1872 EXPECT_EQ(1, results.active_match_ordinal);
1873}
1874
Joey Arhar0574fb752022-03-29 00:45:161875IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DelayThenStop) {
1876 LoadAndWait("/find_in_page.html");
1877 if (test_with_oopif())
1878 MakeChildFrameCrossProcess();
1879
1880 auto default_options = blink::mojom::FindOptions::New();
1881 Find("r", default_options->Clone());
1882 contents()->StopFinding(STOP_FIND_ACTION_CLEAR_SELECTION);
1883
1884 FindResults results = delegate()->GetFindResults();
1885 EXPECT_EQ(0, results.number_of_matches);
1886
1887 EXPECT_FALSE(contents()
1888 ->GetFindRequestManagerForTesting()
1889 ->RunDelayedFindTaskForTesting());
1890}
1891
paulmeyera41de3b2016-05-05 16:30:181892} // namespace content