blob: 4e6ecb2fbe24d343d0702065a09c79e8f5a770b1 [file] [log] [blame]
paulmeyera41de3b2016-05-05 16:30:181// Copyright 2016 The Chromium Authors. All rights reserved.
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 "base/command_line.h"
6#include "base/strings/utf_string_conversions.h"
7#include "content/browser/web_contents/web_contents_impl.h"
8#include "content/public/browser/notification_types.h"
9#include "content/public/common/content_switches.h"
paulmeyer3ac612d2016-09-30 19:21:0610#include "content/public/test/browser_test_utils.h"
paulmeyera41de3b2016-05-05 16:30:1811#include "content/public/test/content_browser_test.h"
12#include "content/public/test/content_browser_test_utils.h"
13#include "content/public/test/test_navigation_observer.h"
14#include "content/public/test/test_utils.h"
15#include "content/shell/browser/shell.h"
paulmeyerc8cb7cb2016-06-07 01:14:1916#include "content/test/content_browser_test_utils_internal.h"
paulmeyera41de3b2016-05-05 16:30:1817#include "net/dns/mock_host_resolver.h"
18#include "third_party/WebKit/public/web/WebFindOptions.h"
19
20namespace content {
21
22namespace {
23
24const int kInvalidId = -1;
25
26// The results of a find request.
27struct FindResults {
paulmeyerdc6a6442016-08-19 16:48:2828 FindResults(int request_id, int number_of_matches, int active_match_ordinal)
29 : request_id(request_id),
30 number_of_matches(number_of_matches),
31 active_match_ordinal(active_match_ordinal) {}
32 FindResults() : FindResults(kInvalidId, 0, 0) {}
33
34 int request_id;
35 int number_of_matches;
36 int active_match_ordinal;
paulmeyera41de3b2016-05-05 16:30:1837};
38
39} // namespace
40
41class TestWebContentsDelegate : public WebContentsDelegate {
42 public:
43 TestWebContentsDelegate()
paulmeyer2f06e612016-08-10 17:39:0244 : last_request_id_(kInvalidId),
45 last_finished_request_id_(kInvalidId),
46 next_reply_received_(false),
paulmeyerdc6a6442016-08-19 16:48:2847 record_replies_(false),
paulmeyer2f06e612016-08-10 17:39:0248 waiting_for_(NOTHING) {}
paulmeyera41de3b2016-05-05 16:30:1849 ~TestWebContentsDelegate() override {}
50
paulmeyera41de3b2016-05-05 16:30:1851 // Returns the current find results.
paulmeyerdc6a6442016-08-19 16:48:2852 const FindResults& GetFindResults() const {
paulmeyera41de3b2016-05-05 16:30:1853 return current_results_;
54 }
55
paulmeyer2f06e612016-08-10 17:39:0256 // Waits for all pending replies to be received.
57 void WaitForFinalReply() {
58 if (last_finished_request_id_ >= last_request_id_)
59 return;
60
61 WaitFor(FINAL_REPLY);
paulmeyerc8cb7cb2016-06-07 01:14:1962 }
63
paulmeyer2f06e612016-08-10 17:39:0264 // Waits for the next find reply. This is useful for waiting for a single
65 // match to be activated, or for a new frame to be searched.
66 void WaitForNextReply() {
67 if (next_reply_received_)
68 return;
69
70 WaitFor(NEXT_REPLY);
71 }
72
73 // Indicates that the next find reply from this point will be the one to wait
74 // for when WaitForNextReply() is called. It may be the case that the reply
75 // comes before the call to WaitForNextReply(), in which case it will return
76 // immediately.
77 void MarkNextReply() {
78 next_reply_received_ = false;
79 }
80
81 // Called when a new find request is issued, so the delegate knows the last
82 // request ID.
83 void UpdateLastRequest(int request_id) {
84 last_request_id_ = request_id;
85 }
86
paulmeyerdc6a6442016-08-19 16:48:2887 // From when this function is called, all replies coming in via FindReply()
88 // will be recorded. These replies can be retrieved via GetReplyRecord().
89 void StartReplyRecord() {
90 reply_record_.clear();
91 record_replies_ = true;
92 }
93
94 // Retreives the results from the find replies recorded since the last call to
95 // StartReplyRecord(). Calling this function also stops the recording new find
96 // replies.
97 const std::vector<FindResults>& GetReplyRecord() {
98 record_replies_ = false;
99 return reply_record_;
100 }
101
paulmeyer2f06e612016-08-10 17:39:02102#if defined(OS_ANDROID)
paulmeyerc8cb7cb2016-06-07 01:14:19103 // Waits for all of the find match rects to be received.
104 void WaitForMatchRects() {
paulmeyer2f06e612016-08-10 17:39:02105 WaitFor(MATCH_RECTS);
paulmeyerc8cb7cb2016-06-07 01:14:19106 }
107
108 const std::vector<gfx::RectF>& find_match_rects() const {
109 return find_match_rects_;
110 }
111
112 const gfx::RectF& active_match_rect() const {
113 return active_match_rect_;
114 }
115#endif
116
paulmeyera41de3b2016-05-05 16:30:18117 private:
paulmeyer2f06e612016-08-10 17:39:02118 enum WaitingFor {
119 NOTHING,
120 FINAL_REPLY,
121 NEXT_REPLY,
122#if defined(OS_ANDROID)
123 MATCH_RECTS
124#endif
125 };
126
paulmeyera41de3b2016-05-05 16:30:18127 // WebContentsDelegate override.
128 void FindReply(WebContents* web_contents,
129 int request_id,
130 int number_of_matches,
131 const gfx::Rect& selection_rect,
132 int active_match_ordinal,
133 bool final_update) override {
paulmeyerdc6a6442016-08-19 16:48:28134 if (record_replies_) {
135 reply_record_.emplace_back(
136 request_id, number_of_matches, active_match_ordinal);
137 }
138
paulmeyera41de3b2016-05-05 16:30:18139 // Update the current results.
140 if (request_id > current_results_.request_id)
141 current_results_.request_id = request_id;
142 if (number_of_matches != -1)
143 current_results_.number_of_matches = number_of_matches;
144 if (active_match_ordinal != -1)
145 current_results_.active_match_ordinal = active_match_ordinal;
146
paulmeyer2f06e612016-08-10 17:39:02147 if (!final_update)
148 return;
paulmeyera41de3b2016-05-05 16:30:18149
paulmeyer2f06e612016-08-10 17:39:02150 if (request_id > last_finished_request_id_)
151 last_finished_request_id_ = request_id;
152 next_reply_received_ = true;
153
154 // If we are waiting for this find reply, stop waiting.
155 if (waiting_for_ == NEXT_REPLY ||
156 (waiting_for_ == FINAL_REPLY &&
157 last_finished_request_id_ >= last_request_id_)) {
158 StopWaiting();
paulmeyera41de3b2016-05-05 16:30:18159 }
160 }
161
paulmeyer2f06e612016-08-10 17:39:02162 // Uses |message_loop_runner_| to wait for various things.
163 void WaitFor(WaitingFor wait_for) {
164 ASSERT_EQ(NOTHING, waiting_for_);
165 ASSERT_NE(NOTHING, wait_for);
166
167 // Wait for |wait_for|.
168 waiting_for_ = wait_for;
169 message_loop_runner_ = new content::MessageLoopRunner;
170 message_loop_runner_->Run();
171
172 // Done waiting.
173 waiting_for_ = NOTHING;
174 message_loop_runner_ = nullptr;
175 }
176
177 // Stop waiting for |waiting_for_|.
178 void StopWaiting() {
179 if (!message_loop_runner_.get())
180 return;
181
182 ASSERT_NE(NOTHING, waiting_for_);
183 message_loop_runner_->Quit();
184 }
185
paulmeyerc8cb7cb2016-06-07 01:14:19186#if defined(OS_ANDROID)
paulmeyer2f06e612016-08-10 17:39:02187 // WebContentsDelegate override.
paulmeyerc8cb7cb2016-06-07 01:14:19188 void FindMatchRectsReply(WebContents* web_contents,
189 int version,
190 const std::vector<gfx::RectF>& rects,
191 const gfx::RectF& active_rect) override {
192 // Update the current rects.
193 find_match_rects_ = rects;
194 active_match_rect_ = active_rect;
195
196 // If we are waiting for match rects, stop waiting.
paulmeyer2f06e612016-08-10 17:39:02197 if (waiting_for_ == MATCH_RECTS)
198 StopWaiting();
paulmeyerc8cb7cb2016-06-07 01:14:19199 }
200
201 std::vector<gfx::RectF> find_match_rects_;
202
203 gfx::RectF active_match_rect_;
204#endif
205
paulmeyera41de3b2016-05-05 16:30:18206 // The latest known results from the current find request.
207 FindResults current_results_;
208
paulmeyer2f06e612016-08-10 17:39:02209 // The ID of the last find request issued.
210 int last_request_id_;
211
paulmeyera41de3b2016-05-05 16:30:18212 // The ID of the last find request to finish (all replies received).
213 int last_finished_request_id_;
214
paulmeyer2f06e612016-08-10 17:39:02215 // Indicates whether the next reply after MarkNextReply() has been received.
216 bool next_reply_received_;
paulmeyera41de3b2016-05-05 16:30:18217
paulmeyerdc6a6442016-08-19 16:48:28218 // Indicates whether the find results from incoming find replies are currently
219 // being recorded.
220 bool record_replies_;
221
222 // A record of all find replies that have come in via FindReply() since
223 // StartReplyRecor() was last called.
224 std::vector<FindResults> reply_record_;
225
paulmeyer2f06e612016-08-10 17:39:02226 // Indicates what |message_loop_runner_| is waiting for, if anything.
227 WaitingFor waiting_for_;
228
229 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
paulmeyera41de3b2016-05-05 16:30:18230
231 DISALLOW_COPY_AND_ASSIGN(TestWebContentsDelegate);
232};
233
paulmeyerc8cb7cb2016-06-07 01:14:19234class FindRequestManagerTest : public ContentBrowserTest,
235 public testing::WithParamInterface<bool> {
paulmeyera41de3b2016-05-05 16:30:18236 public:
237 FindRequestManagerTest()
238 : normal_delegate_(nullptr),
239 last_request_id_(0) {}
240 ~FindRequestManagerTest() override {}
241
242 void SetUpOnMainThread() override {
243 host_resolver()->AddRule("*", "127.0.0.1");
244 ASSERT_TRUE(embedded_test_server()->Start());
245
246 // Swap the WebContents's delegate for our test delegate.
247 normal_delegate_ = contents()->GetDelegate();
248 contents()->SetDelegate(&test_delegate_);
249 }
250
251 void TearDownOnMainThread() override {
252 // Swap the WebContents's delegate back to its usual delegate.
253 contents()->SetDelegate(normal_delegate_);
254 }
255
paulmeyerc8cb7cb2016-06-07 01:14:19256#if !defined(OS_ANDROID)
paulmeyera41de3b2016-05-05 16:30:18257 void SetUpCommandLine(base::CommandLine* command_line) override {
258 IsolateAllSitesForTesting(command_line);
259 }
paulmeyerc8cb7cb2016-06-07 01:14:19260#endif
paulmeyera41de3b2016-05-05 16:30:18261
262 protected:
paulmeyerc8cb7cb2016-06-07 01:14:19263 // Navigates to |url| and waits for it to finish loading.
paulmeyera41de3b2016-05-05 16:30:18264 void LoadAndWait(const std::string& url) {
265 TestNavigationObserver navigation_observer(contents());
266 NavigateToURL(shell(), embedded_test_server()->GetURL("a.com", url));
267 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
268 }
269
paulmeyerc8cb7cb2016-06-07 01:14:19270 // Loads a multi-frame page. The page will have a full binary frame tree of
271 // height |height|. If |cross_process| is true, child frames will be loaded
272 // cross-process.
273 void LoadMultiFramePage(int height, bool cross_process) {
274 LoadAndWait("/find_in_page_multi_frame.html");
paulmeyer74f68c92016-08-12 22:34:13275 FrameTreeNode* root = contents()->GetFrameTree()->root();
paulmeyerc8cb7cb2016-06-07 01:14:19276 LoadMultiFramePageChildFrames(height, cross_process, root);
277 }
278
279 // Reloads the child frame cross-process.
280 void MakeChildFrameCrossProcess() {
paulmeyer74f68c92016-08-12 22:34:13281 FrameTreeNode* root = contents()->GetFrameTree()->root();
282 FrameTreeNode* child = root->child_at(0);
283 GURL url(embedded_test_server()->GetURL(
284 "b.com", child->current_url().path()));
paulmeyerc8cb7cb2016-06-07 01:14:19285
286 TestNavigationObserver observer(shell()->web_contents());
paulmeyerc8cb7cb2016-06-07 01:14:19287 NavigateFrameToURL(child, url);
288 EXPECT_EQ(url, observer.last_navigation_url());
289 EXPECT_TRUE(observer.last_navigation_succeeded());
290 }
291
paulmeyera41de3b2016-05-05 16:30:18292 void Find(const std::string& search_text,
293 const blink::WebFindOptions& options) {
paulmeyer2f06e612016-08-10 17:39:02294 delegate()->UpdateLastRequest(++last_request_id_);
295 contents()->Find(last_request_id_,
paulmeyera41de3b2016-05-05 16:30:18296 base::UTF8ToUTF16(search_text),
297 options);
298 }
299
paulmeyer74f68c92016-08-12 22:34:13300 WebContentsImpl* contents() const {
301 return static_cast<WebContentsImpl*>(shell()->web_contents());
paulmeyera41de3b2016-05-05 16:30:18302 }
303
304 TestWebContentsDelegate* delegate() const {
305 return static_cast<TestWebContentsDelegate*>(contents()->GetDelegate());
306 }
307
308 int last_request_id() const {
309 return last_request_id_;
310 }
311
312 private:
paulmeyerc8cb7cb2016-06-07 01:14:19313 // Helper function for LoadMultiFramePage. Loads child frames until the frame
314 // tree rooted at |root| is a full binary tree of height |height|.
315 void LoadMultiFramePageChildFrames(int height,
316 bool cross_process,
317 FrameTreeNode* root) {
318 if (height == 0)
319 return;
320
321 std::string hostname = root->current_origin().host();
322 if (cross_process)
323 hostname.insert(0, 1, 'a');
324 GURL url(embedded_test_server()->GetURL(hostname,
325 "/find_in_page_multi_frame.html"));
326
327 TestNavigationObserver observer(shell()->web_contents());
328
329 FrameTreeNode* child = root->child_at(0);
330 NavigateFrameToURL(child, url);
331 EXPECT_TRUE(observer.last_navigation_succeeded());
332 LoadMultiFramePageChildFrames(height - 1, cross_process, child);
333
334 child = root->child_at(1);
335 NavigateFrameToURL(child, url);
336 EXPECT_TRUE(observer.last_navigation_succeeded());
337 LoadMultiFramePageChildFrames(height - 1, cross_process, child);
338 }
339
paulmeyera41de3b2016-05-05 16:30:18340 TestWebContentsDelegate test_delegate_;
341 WebContentsDelegate* normal_delegate_;
342
343 // The ID of the last find request requested.
344 int last_request_id_;
345
346 DISALLOW_COPY_AND_ASSIGN(FindRequestManagerTest);
347};
348
paulmeyerc8cb7cb2016-06-07 01:14:19349// Frames are made cross-process when the test param is set to
350// true. Cross-process frames are not used on android.
351#if defined(OS_ANDROID)
352INSTANTIATE_TEST_CASE_P(
353 FindRequestManagerTests, FindRequestManagerTest, testing::Values(false));
354#else
355INSTANTIATE_TEST_CASE_P(
356 FindRequestManagerTests, FindRequestManagerTest, testing::Bool());
357#endif
paulmeyera41de3b2016-05-05 16:30:18358
torne65fee8192016-10-25 12:09:03359// TODO(crbug.com/615291): These tests frequently fail on Android.
360#if defined(OS_ANDROID)
paulmeyercc9a00f92016-07-06 13:44:27361#define MAYBE(x) DISABLED_##x
paulmeyerc8cb7cb2016-06-07 01:14:19362#else
paulmeyercc9a00f92016-07-06 13:44:27363#define MAYBE(x) x
paulmeyerc8cb7cb2016-06-07 01:14:19364#endif
365
366
367// Tests basic find-in-page functionality (such as searching forward and
paulmeyera41de3b2016-05-05 16:30:18368// backward) and check for correct results at each step.
paulmeyercc9a00f92016-07-06 13:44:27369IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(Basic)) {
paulmeyera41de3b2016-05-05 16:30:18370 LoadAndWait("/find_in_page.html");
paulmeyerc8cb7cb2016-06-07 01:14:19371 if (GetParam())
372 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18373
374 blink::WebFindOptions options;
375 Find("result", options);
paulmeyer2f06e612016-08-10 17:39:02376 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18377
378 FindResults results = delegate()->GetFindResults();
379 EXPECT_EQ(last_request_id(), results.request_id);
380 EXPECT_EQ(19, results.number_of_matches);
381 EXPECT_EQ(1, results.active_match_ordinal);
382
383 options.findNext = true;
384 for (int i = 2; i <= 10; ++i) {
385 Find("result", options);
paulmeyer2f06e612016-08-10 17:39:02386 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18387
388 results = delegate()->GetFindResults();
389 EXPECT_EQ(last_request_id(), results.request_id);
390 EXPECT_EQ(19, results.number_of_matches);
391 EXPECT_EQ(i, results.active_match_ordinal);
392 }
393
394 options.forward = false;
395 for (int i = 9; i >= 5; --i) {
396 Find("result", options);
paulmeyer2f06e612016-08-10 17:39:02397 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18398
399 results = delegate()->GetFindResults();
400 EXPECT_EQ(last_request_id(), results.request_id);
401 EXPECT_EQ(19, results.number_of_matches);
402 EXPECT_EQ(i, results.active_match_ordinal);
403 }
404}
405
406// Tests searching for a word character-by-character, as would typically be done
407// by a user typing into the find bar.
paulmeyercc9a00f92016-07-06 13:44:27408IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(CharacterByCharacter)) {
paulmeyera41de3b2016-05-05 16:30:18409 LoadAndWait("/find_in_page.html");
paulmeyerc8cb7cb2016-06-07 01:14:19410 if (GetParam())
411 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18412
413 blink::WebFindOptions default_options;
414 Find("r", default_options);
415 Find("re", default_options);
416 Find("res", default_options);
417 Find("resu", default_options);
418 Find("resul", default_options);
419 Find("result", default_options);
paulmeyer2f06e612016-08-10 17:39:02420 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18421
422 FindResults results = delegate()->GetFindResults();
423 EXPECT_EQ(last_request_id(), results.request_id);
424 EXPECT_EQ(19, results.number_of_matches);
425 EXPECT_EQ(1, results.active_match_ordinal);
426}
427
paulmeyerc8cb7cb2016-06-07 01:14:19428// Tests sending a large number of find requests subsequently.
paulmeyercc9a00f92016-07-06 13:44:27429IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(RapidFire)) {
paulmeyera41de3b2016-05-05 16:30:18430 LoadAndWait("/find_in_page.html");
paulmeyerc8cb7cb2016-06-07 01:14:19431 if (GetParam())
432 MakeChildFrameCrossProcess();
paulmeyera41de3b2016-05-05 16:30:18433
434 blink::WebFindOptions options;
435 Find("result", options);
436
437 options.findNext = true;
438 for (int i = 2; i <= 1000; ++i)
439 Find("result", options);
paulmeyer2f06e612016-08-10 17:39:02440 delegate()->WaitForFinalReply();
paulmeyera41de3b2016-05-05 16:30:18441
442 FindResults results = delegate()->GetFindResults();
443 EXPECT_EQ(last_request_id(), results.request_id);
444 EXPECT_EQ(19, results.number_of_matches);
445 EXPECT_EQ(last_request_id() % results.number_of_matches,
446 results.active_match_ordinal);
447}
448
paulmeyerc8cb7cb2016-06-07 01:14:19449// Tests removing a frame during a find session.
mek98430952016-11-29 01:34:46450// TODO(crbug.com/657331): Test is flaky on all platforms.
451IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_RemoveFrame) {
paulmeyerc8cb7cb2016-06-07 01:14:19452 LoadMultiFramePage(2 /* height */, GetParam() /* cross_process */);
453
454 blink::WebFindOptions options;
455 Find("result", options);
paulmeyerdc6a6442016-08-19 16:48:28456 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19457 options.findNext = true;
458 options.forward = false;
459 Find("result", options);
460 Find("result", options);
461 Find("result", options);
462 Find("result", options);
463 Find("result", options);
paulmeyer2f06e612016-08-10 17:39:02464 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19465
466 FindResults results = delegate()->GetFindResults();
467 EXPECT_EQ(last_request_id(), results.request_id);
468 EXPECT_EQ(21, results.number_of_matches);
469 EXPECT_EQ(17, results.active_match_ordinal);
470
471 // Remove a frame.
paulmeyer74f68c92016-08-12 22:34:13472 FrameTreeNode* root = contents()->GetFrameTree()->root();
paulmeyerc8cb7cb2016-06-07 01:14:19473 root->RemoveChild(root->child_at(0));
474
475 // The number of matches and active match ordinal should update automatically
476 // to exclude the matches from the removed frame.
477 results = delegate()->GetFindResults();
paulmeyerc8cb7cb2016-06-07 01:14:19478 EXPECT_EQ(12, results.number_of_matches);
479 EXPECT_EQ(8, results.active_match_ordinal);
paulmeyer3ac612d2016-09-30 19:21:06480}
paulmeyerc8cb7cb2016-06-07 01:14:19481
paulmeyer3ac612d2016-09-30 19:21:06482// Tests adding a frame during a find session.
mek98430952016-11-29 01:34:46483// TODO(crbug.com/657331): Test is flaky on all platforms.
484IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, DISABLED_AddFrame) {
paulmeyer3ac612d2016-09-30 19:21:06485 LoadMultiFramePage(2 /* height */, GetParam() /* cross_process */);
486
487 blink::WebFindOptions options;
488 Find("result", options);
489 options.findNext = true;
490 Find("result", options);
491 Find("result", options);
492 Find("result", options);
493 Find("result", options);
494 delegate()->WaitForFinalReply();
495
496 FindResults results = delegate()->GetFindResults();
497 EXPECT_EQ(last_request_id(), results.request_id);
498 EXPECT_EQ(21, results.number_of_matches);
499 EXPECT_EQ(5, results.active_match_ordinal);
500
501 // Add a frame. It contains 5 new matches.
502 std::string url = embedded_test_server()->GetURL(
503 GetParam() ? "b.com" : "a.com", "/find_in_simple_page.html").spec();
504 std::string script = std::string() +
505 "var frame = document.createElement('iframe');" +
506 "frame.src = '" + url + "';" +
507 "document.body.appendChild(frame);";
508 delegate()->MarkNextReply();
509 ASSERT_TRUE(ExecuteScript(shell(), script));
510 delegate()->WaitForNextReply();
511
512 // The number of matches should update automatically to include the matches
513 // from the newly added frame.
514 results = delegate()->GetFindResults();
515 EXPECT_EQ(26, results.number_of_matches);
516 EXPECT_EQ(5, results.active_match_ordinal);
517}
518
519// Tests adding a frame during a find session where there were previously no
520// matches.
521IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(AddFrameAfterNoMatches)) {
522 TestNavigationObserver navigation_observer(contents());
523 NavigateToURL(shell(), GURL("about:blank"));
524 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
525
526 blink::WebFindOptions default_options;
527 Find("result", default_options);
528 delegate()->WaitForFinalReply();
529
530 // Initially, there are no matches on the page.
531 FindResults results = delegate()->GetFindResults();
532 EXPECT_EQ(last_request_id(), results.request_id);
533 EXPECT_EQ(0, results.number_of_matches);
534 EXPECT_EQ(0, results.active_match_ordinal);
535
536 // Add a frame. It contains 5 new matches.
537 std::string url =
538 embedded_test_server()->GetURL("/find_in_simple_page.html").spec();
539 std::string script = std::string() +
540 "var frame = document.createElement('iframe');" +
541 "frame.src = '" + url + "';" +
542 "document.body.appendChild(frame);";
543 delegate()->MarkNextReply();
544 ASSERT_TRUE(ExecuteScript(shell(), script));
545 delegate()->WaitForNextReply();
546
547 // The matches from the new frame should be found automatically, and the first
548 // match in the frame should be activated.
549 results = delegate()->GetFindResults();
550 EXPECT_EQ(5, results.number_of_matches);
551 EXPECT_EQ(1, results.active_match_ordinal);
552}
553
554// Tests a frame navigating to a different page during a find session.
555IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(NavigateFrame)) {
556 LoadMultiFramePage(2 /* height */, GetParam() /* cross_process */);
557
558 blink::WebFindOptions options;
559 Find("result", options);
560 options.findNext = true;
561 options.forward = false;
562 Find("result", options);
563 Find("result", options);
564 Find("result", options);
565 delegate()->WaitForFinalReply();
566
567 FindResults results = delegate()->GetFindResults();
568 EXPECT_EQ(last_request_id(), results.request_id);
569 EXPECT_EQ(21, results.number_of_matches);
570 EXPECT_EQ(19, results.active_match_ordinal);
571
572 // Navigate one of the empty frames to a page with 5 matches.
573 FrameTreeNode* root =
574 static_cast<WebContentsImpl*>(shell()->web_contents())->
575 GetFrameTree()->root();
576 GURL url(embedded_test_server()->GetURL(
577 GetParam() ? "b.com" : "a.com", "/find_in_simple_page.html"));
578 delegate()->MarkNextReply();
579 TestNavigationObserver navigation_observer(contents());
580 NavigateFrameToURL(root->child_at(0)->child_at(1)->child_at(0), url);
581 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
582 delegate()->WaitForNextReply();
583
584 // The navigation results in an extra reply before the one we care about. This
585 // extra reply happens because the RenderFrameHost changes before it navigates
586 // (because the navigation is cross-origin). The first reply will not change
587 // the number of matches because the frame that is navigating was empty
588 // before.
589 if (delegate()->GetFindResults().number_of_matches == 21) {
590 delegate()->MarkNextReply();
591 delegate()->WaitForNextReply();
592 }
593
594 // The number of matches and the active match ordinal should update
595 // automatically to include the new matches.
596 results = delegate()->GetFindResults();
597 EXPECT_EQ(26, results.number_of_matches);
598 EXPECT_EQ(24, results.active_match_ordinal);
paulmeyerc8cb7cb2016-06-07 01:14:19599}
600
601// Tests Searching in a hidden frame. Matches in the hidden frame should be
602// ignored.
paulmeyercc9a00f92016-07-06 13:44:27603IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(HiddenFrame)) {
paulmeyerc8cb7cb2016-06-07 01:14:19604 LoadAndWait("/find_in_hidden_frame.html");
605
606 blink::WebFindOptions default_options;
607 Find("hello", default_options);
paulmeyer2f06e612016-08-10 17:39:02608 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19609 FindResults results = delegate()->GetFindResults();
610
611 EXPECT_EQ(last_request_id(), results.request_id);
612 EXPECT_EQ(1, results.number_of_matches);
613 EXPECT_EQ(1, results.active_match_ordinal);
614}
615
paulmeyer74f68c92016-08-12 22:34:13616// Tests that new matches can be found in dynamically added text.
617IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE(FindNewMatches)) {
618 LoadAndWait("/find_in_dynamic_page.html");
619
620 blink::WebFindOptions options;
621 Find("result", options);
622 options.findNext = true;
623 Find("result", options);
624 Find("result", options);
625 delegate()->WaitForFinalReply();
626
627 FindResults results = delegate()->GetFindResults();
628 EXPECT_EQ(last_request_id(), results.request_id);
629 EXPECT_EQ(3, results.number_of_matches);
630 EXPECT_EQ(3, results.active_match_ordinal);
631
632 // Dynamically add new text to the page. This text contains 5 new matches for
633 // "result".
634 ASSERT_TRUE(ExecuteScript(contents()->GetMainFrame(), "addNewText()"));
635
636 Find("result", options);
637 delegate()->WaitForFinalReply();
638
639 results = delegate()->GetFindResults();
640 EXPECT_EQ(last_request_id(), results.request_id);
641 EXPECT_EQ(8, results.number_of_matches);
642 EXPECT_EQ(4, results.active_match_ordinal);
643}
644
paulmeyerdc6a6442016-08-19 16:48:28645IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindInPage_Issue627799)) {
646 LoadAndWait("/find_in_long_page.html");
647
648 blink::WebFindOptions options;
649 Find("42", options);
650 delegate()->WaitForFinalReply();
651
652 FindResults results = delegate()->GetFindResults();
653 EXPECT_EQ(last_request_id(), results.request_id);
654 EXPECT_EQ(970, results.number_of_matches);
655 EXPECT_EQ(1, results.active_match_ordinal);
656
657 delegate()->StartReplyRecord();
658 options.findNext = true;
659 options.forward = false;
660 Find("42", options);
661 delegate()->WaitForFinalReply();
662
663 // This is the crux of the issue that this test guards against. Searching
664 // across the frame boundary should not cause the frame to be re-scoped. If
665 // the re-scope occurs, then we will see the number of matches change in one
666 // of the recorded find replies.
667 for (auto& reply : delegate()->GetReplyRecord()) {
668 EXPECT_EQ(last_request_id(), reply.request_id);
669 EXPECT_TRUE(reply.number_of_matches == kInvalidId ||
670 reply.number_of_matches == results.number_of_matches);
671 }
672}
673
paulmeyerd3b32d52016-09-07 22:24:55674IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindInPage_Issue644448)) {
675 TestNavigationObserver navigation_observer(contents());
676 NavigateToURL(shell(), GURL("about:blank"));
677 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
678
679 blink::WebFindOptions default_options;
680 Find("result", default_options);
681 delegate()->WaitForFinalReply();
682
683 // Initially, there are no matches on the page.
684 FindResults results = delegate()->GetFindResults();
685 EXPECT_EQ(last_request_id(), results.request_id);
686 EXPECT_EQ(0, results.number_of_matches);
687 EXPECT_EQ(0, results.active_match_ordinal);
688
689 // Load a page with matches.
690 LoadAndWait("/find_in_simple_page.html");
691
692 Find("result", default_options);
693 delegate()->WaitForFinalReply();
694
695 // There should now be matches found. When the bug was present, there were
696 // still no matches found.
697 results = delegate()->GetFindResults();
698 EXPECT_EQ(last_request_id(), results.request_id);
699 EXPECT_EQ(5, results.number_of_matches);
paulmeyerd3b32d52016-09-07 22:24:55700}
701
paulmeyerc8cb7cb2016-06-07 01:14:19702#if defined(OS_ANDROID)
703// Tests requesting find match rects.
paulmeyercc9a00f92016-07-06 13:44:27704IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindMatchRects)) {
paulmeyerc8cb7cb2016-06-07 01:14:19705 LoadAndWait("/find_in_page.html");
706
707 blink::WebFindOptions default_options;
708 Find("result", default_options);
paulmeyer2f06e612016-08-10 17:39:02709 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19710 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
711
712 // Request the find match rects.
713 contents()->RequestFindMatchRects(-1);
714 delegate()->WaitForMatchRects();
715 const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
716
717 // The first match should be active.
718 EXPECT_EQ(rects[0], delegate()->active_match_rect());
719
720 // All results after the first two should be between them in find-in-page
721 // coordinates. This is because results 2 to 19 are inside an iframe located
722 // between results 0 and 1. This applies to the fixed div too.
723 EXPECT_LT(rects[0].y(), rects[1].y());
724 for (int i = 2; i < 19; ++i) {
725 EXPECT_LT(rects[0].y(), rects[i].y());
726 EXPECT_GT(rects[1].y(), rects[i].y());
727 }
728
729 // Result 3 should be below results 2 and 4. This is caused by the CSS
730 // transform in the containing div. If the transform doesn't work then result
731 // 3 will be between results 2 and 4.
732 EXPECT_GT(rects[3].y(), rects[2].y());
733 EXPECT_GT(rects[3].y(), rects[4].y());
734
735 // Results 6, 7, 8 and 9 should be one below the other in that same order. If
736 // overflow:scroll is not properly handled then result 8 would be below result
737 // 9 or result 7 above result 6 depending on the scroll.
738 EXPECT_LT(rects[6].y(), rects[7].y());
739 EXPECT_LT(rects[7].y(), rects[8].y());
740 EXPECT_LT(rects[8].y(), rects[9].y());
741
742 // Results 11, 12, 13 and 14 should be between results 10 and 15, as they are
743 // inside the table.
744 EXPECT_GT(rects[11].y(), rects[10].y());
745 EXPECT_GT(rects[12].y(), rects[10].y());
746 EXPECT_GT(rects[13].y(), rects[10].y());
747 EXPECT_GT(rects[14].y(), rects[10].y());
748 EXPECT_LT(rects[11].y(), rects[15].y());
749 EXPECT_LT(rects[12].y(), rects[15].y());
750 EXPECT_LT(rects[13].y(), rects[15].y());
751 EXPECT_LT(rects[14].y(), rects[15].y());
752
753 // Result 11 should be above results 12, 13 and 14 as it's in the table
754 // header.
755 EXPECT_LT(rects[11].y(), rects[12].y());
756 EXPECT_LT(rects[11].y(), rects[13].y());
757 EXPECT_LT(rects[11].y(), rects[14].y());
758
759 // Result 11 should also be right of results 12, 13 and 14 because of the
760 // colspan.
761 EXPECT_GT(rects[11].x(), rects[12].x());
762 EXPECT_GT(rects[11].x(), rects[13].x());
763 EXPECT_GT(rects[11].x(), rects[14].x());
764
765 // Result 12 should be left of results 11, 13 and 14 in the table layout.
766 EXPECT_LT(rects[12].x(), rects[11].x());
767 EXPECT_LT(rects[12].x(), rects[13].x());
768 EXPECT_LT(rects[12].x(), rects[14].x());
769
770 // Results 13, 12 and 14 should be one above the other in that order because
771 // of the rowspan and vertical-align: middle by default.
772 EXPECT_LT(rects[13].y(), rects[12].y());
773 EXPECT_LT(rects[12].y(), rects[14].y());
774
775 // Result 16 should be below result 15.
776 EXPECT_GT(rects[15].y(), rects[14].y());
777
778 // Result 18 should be normalized with respect to the position:relative div,
779 // and not it's immediate containing div. Consequently, result 18 should be
780 // above result 17.
781 EXPECT_GT(rects[17].y(), rects[18].y());
782}
783
784// Tests activating the find match nearest to a given point.
paulmeyercc9a00f92016-07-06 13:44:27785IN_PROC_BROWSER_TEST_F(FindRequestManagerTest,
786 MAYBE(ActivateNearestFindMatch)) {
paulmeyerc8cb7cb2016-06-07 01:14:19787 LoadAndWait("/find_in_page.html");
788
789 blink::WebFindOptions default_options;
790 Find("result", default_options);
paulmeyer2f06e612016-08-10 17:39:02791 delegate()->WaitForFinalReply();
paulmeyerc8cb7cb2016-06-07 01:14:19792 EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches);
793
794 // Get the find match rects.
795 contents()->RequestFindMatchRects(-1);
796 delegate()->WaitForMatchRects();
797 const std::vector<gfx::RectF>& rects = delegate()->find_match_rects();
798
799 // Activate matches via points inside each of the find match rects, in an
800 // arbitrary order. Check that the correct match becomes active after each
801 // activation.
802 int order[19] =
803 {11, 13, 2, 0, 16, 5, 7, 10, 6, 1, 15, 14, 9, 17, 18, 3, 8, 12, 4};
804 for (int i = 0; i < 19; ++i) {
paulmeyer2f06e612016-08-10 17:39:02805 delegate()->MarkNextReply();
paulmeyerc8cb7cb2016-06-07 01:14:19806 contents()->ActivateNearestFindResult(
807 rects[order[i]].CenterPoint().x(), rects[order[i]].CenterPoint().y());
808 delegate()->WaitForNextReply();
809 EXPECT_EQ(order[i] + 1, delegate()->GetFindResults().active_match_ordinal);
810 }
811}
812#endif // defined(OS_ANDROID)
paulmeyera41de3b2016-05-05 16:30:18813
814} // namespace content