Index: content/browser/find_request_manager_browsertest.cc |
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc |
index c8ac858f4bdb5e3cd4a7a682b593841757777266..0ce93a6d6f02eb0a3726e9f356c9c14c4deb3b3a 100644 |
--- a/content/browser/find_request_manager_browsertest.cc |
+++ b/content/browser/find_request_manager_browsertest.cc |
@@ -12,6 +12,7 @@ |
#include "content/public/test/test_navigation_observer.h" |
#include "content/public/test/test_utils.h" |
#include "content/shell/browser/shell.h" |
+#include "content/test/content_browser_test_utils_internal.h" |
#include "net/dns/mock_host_resolver.h" |
#include "third_party/WebKit/public/web/WebFindOptions.h" |
@@ -52,6 +53,31 @@ class TestWebContentsDelegate : public WebContentsDelegate { |
return current_results_; |
} |
+#if defined(OS_ANDROID) |
+ // Waits for the next find reply. This is useful for waiting for a single |
+ // match to be activated, which results in a single find reply (without a |
+ // unique request ID). |
+ void WaitForNextReply() { |
+ waiting_request_id_ = 0; |
+ find_message_loop_runner_ = new content::MessageLoopRunner; |
+ find_message_loop_runner_->Run(); |
+ } |
+ |
+ // Waits for all of the find match rects to be received. |
+ void WaitForMatchRects() { |
+ match_rects_message_loop_runner_ = new content::MessageLoopRunner; |
+ match_rects_message_loop_runner_->Run(); |
+ } |
+ |
+ const std::vector<gfx::RectF>& find_match_rects() const { |
+ return find_match_rects_; |
+ } |
+ |
+ const gfx::RectF& active_match_rect() const { |
+ return active_match_rect_; |
+ } |
+#endif |
+ |
private: |
// WebContentsDelegate override. |
void FindReply(WebContents* web_contents, |
@@ -78,6 +104,25 @@ class TestWebContentsDelegate : public WebContentsDelegate { |
} |
} |
+#if defined(OS_ANDROID) |
+ void FindMatchRectsReply(WebContents* web_contents, |
+ int version, |
+ const std::vector<gfx::RectF>& rects, |
+ const gfx::RectF& active_rect) override { |
+ // Update the current rects. |
+ find_match_rects_ = rects; |
+ active_match_rect_ = active_rect; |
+ |
+ // If we are waiting for match rects, stop waiting. |
+ if (match_rects_message_loop_runner_.get()) |
+ match_rects_message_loop_runner_->Quit(); |
+ } |
+ |
+ std::vector<gfx::RectF> find_match_rects_; |
+ |
+ gfx::RectF active_match_rect_; |
+#endif |
+ |
// The latest known results from the current find request. |
FindResults current_results_; |
@@ -89,11 +134,13 @@ class TestWebContentsDelegate : public WebContentsDelegate { |
int waiting_request_id_; |
scoped_refptr<content::MessageLoopRunner> find_message_loop_runner_; |
+ scoped_refptr<content::MessageLoopRunner> match_rects_message_loop_runner_; |
DISALLOW_COPY_AND_ASSIGN(TestWebContentsDelegate); |
}; |
-class FindRequestManagerTest : public ContentBrowserTest { |
+class FindRequestManagerTest : public ContentBrowserTest, |
+ public testing::WithParamInterface<bool> { |
public: |
FindRequestManagerTest() |
: normal_delegate_(nullptr), |
@@ -114,18 +161,49 @@ class FindRequestManagerTest : public ContentBrowserTest { |
contents()->SetDelegate(normal_delegate_); |
} |
+#if !defined(OS_ANDROID) |
void SetUpCommandLine(base::CommandLine* command_line) override { |
IsolateAllSitesForTesting(command_line); |
} |
+#endif |
protected: |
- // Navigate to |url| and wait for it to finish loading. |
+ // Navigates to |url| and waits for it to finish loading. |
void LoadAndWait(const std::string& url) { |
TestNavigationObserver navigation_observer(contents()); |
NavigateToURL(shell(), embedded_test_server()->GetURL("a.com", url)); |
EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); |
} |
+ // Loads a multi-frame page. The page will have a full binary frame tree of |
+ // height |height|. If |cross_process| is true, child frames will be loaded |
+ // cross-process. |
+ void LoadMultiFramePage(int height, bool cross_process) { |
+ LoadAndWait("/find_in_page_multi_frame.html"); |
+ |
+ FrameTreeNode* root = |
+ static_cast<WebContentsImpl*>(shell()->web_contents())-> |
+ GetFrameTree()->root(); |
+ |
+ LoadMultiFramePageChildFrames(height, cross_process, root); |
+ } |
+ |
+ // Reloads the child frame cross-process. |
+ void MakeChildFrameCrossProcess() { |
+ FrameTreeNode* root = |
+ static_cast<WebContentsImpl*>(shell()->web_contents())-> |
+ GetFrameTree()->root(); |
+ |
+ TestNavigationObserver observer(shell()->web_contents()); |
+ |
+ FrameTreeNode* child = root->child_at(0); |
+ GURL url(embedded_test_server()->GetURL("b.com", |
+ child->current_url().path())); |
+ NavigateFrameToURL(child, url); |
+ EXPECT_EQ(url, observer.last_navigation_url()); |
+ EXPECT_TRUE(observer.last_navigation_succeeded()); |
+ } |
+ |
void Find(const std::string& search_text, |
const blink::WebFindOptions& options) { |
contents()->Find(++last_request_id_, |
@@ -150,6 +228,33 @@ class FindRequestManagerTest : public ContentBrowserTest { |
} |
private: |
+ // Helper function for LoadMultiFramePage. Loads child frames until the frame |
+ // tree rooted at |root| is a full binary tree of height |height|. |
+ void LoadMultiFramePageChildFrames(int height, |
+ bool cross_process, |
+ FrameTreeNode* root) { |
+ if (height == 0) |
+ return; |
+ |
+ std::string hostname = root->current_origin().host(); |
+ if (cross_process) |
+ hostname.insert(0, 1, 'a'); |
+ GURL url(embedded_test_server()->GetURL(hostname, |
+ "/find_in_page_multi_frame.html")); |
+ |
+ TestNavigationObserver observer(shell()->web_contents()); |
+ |
+ FrameTreeNode* child = root->child_at(0); |
+ NavigateFrameToURL(child, url); |
+ EXPECT_TRUE(observer.last_navigation_succeeded()); |
+ LoadMultiFramePageChildFrames(height - 1, cross_process, child); |
+ |
+ child = root->child_at(1); |
+ NavigateFrameToURL(child, url); |
+ EXPECT_TRUE(observer.last_navigation_succeeded()); |
+ LoadMultiFramePageChildFrames(height - 1, cross_process, child); |
+ } |
+ |
TestWebContentsDelegate test_delegate_; |
WebContentsDelegate* normal_delegate_; |
@@ -159,14 +264,43 @@ class FindRequestManagerTest : public ContentBrowserTest { |
DISALLOW_COPY_AND_ASSIGN(FindRequestManagerTest); |
}; |
-// TODO(paulmeyer): These tests currently fail on the linux_android_rel_ng |
-// trybot. Remove this guard once that problem is figured out. |
-#if !defined(OS_ANDROID) |
- |
-// Test basic find-in-page functionality (such as searching forward and |
+// Frames are made cross-process when the test param is set to |
+// true. Cross-process frames are not used on android. |
+#if defined(OS_ANDROID) |
+INSTANTIATE_TEST_CASE_P( |
+ FindRequestManagerTests, FindRequestManagerTest, testing::Values(false)); |
+#else |
+INSTANTIATE_TEST_CASE_P( |
+ FindRequestManagerTests, FindRequestManagerTest, testing::Bool()); |
+#endif |
+ |
+// TODO(crbug.com/615291): These tests sometimes fail on the |
+// linux_android_rel_ng trybot. |
+#if defined(OS_ANDROID) && defined(NDEBUG) |
+#define MAYBE_Basic DISABLED_Basic |
+#define MAYBE_CharacterByCharacter DISABLED_CharacterByCharacter |
+#define MAYBE_RapidFire DISABLED_RapidFire |
+#define MAYBE_RemoveFrame DISABLED_RemoveFrame |
+#define MAYBE_HiddenFrame DISABLED_HiddenFrame |
+#define MAYBE_FindMatchRects DISABLED_FindMatchRects |
+#define MAYBE_ActivateNearestFindMatch DISABLED_ActivateNearestFindMatch |
+#else |
+#define MAYBE_Basic Basic |
+#define MAYBE_CharacterByCharacter CharacterByCharacter |
+#define MAYBE_RapidFire RapidFire |
+#define MAYBE_RemoveFrame RemoveFrame |
+#define MAYBE_HiddenFrame HiddenFrame |
+#define MAYBE_FindMatchRects FindMatchRects |
+#define MAYBE_ActivateNearestFindMatch ActivateNearestFindMatch |
+#endif |
+ |
+ |
+// Tests basic find-in-page functionality (such as searching forward and |
// backward) and check for correct results at each step. |
-IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, Basic) { |
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_Basic) { |
LoadAndWait("/find_in_page.html"); |
+ if (GetParam()) |
+ MakeChildFrameCrossProcess(); |
blink::WebFindOptions options; |
Find("result", options); |
@@ -202,8 +336,10 @@ IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, Basic) { |
// Tests searching for a word character-by-character, as would typically be done |
// by a user typing into the find bar. |
-IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, CharacterByCharacter) { |
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_CharacterByCharacter) { |
LoadAndWait("/find_in_page.html"); |
+ if (GetParam()) |
+ MakeChildFrameCrossProcess(); |
blink::WebFindOptions default_options; |
Find("r", default_options); |
@@ -220,9 +356,11 @@ IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, CharacterByCharacter) { |
EXPECT_EQ(1, results.active_match_ordinal); |
} |
-// Test sending a large number of find requests subsequently. |
-IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, RapidFire) { |
+// Tests sending a large number of find requests subsequently. |
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_RapidFire) { |
LoadAndWait("/find_in_page.html"); |
+ if (GetParam()) |
+ MakeChildFrameCrossProcess(); |
blink::WebFindOptions options; |
Find("result", options); |
@@ -239,6 +377,165 @@ IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, RapidFire) { |
results.active_match_ordinal); |
} |
-#endif // OS_ANDROID |
+// Tests removing a frame during a find session. |
+IN_PROC_BROWSER_TEST_P(FindRequestManagerTest, MAYBE_RemoveFrame) { |
+ LoadMultiFramePage(2 /* height */, GetParam() /* cross_process */); |
+ |
+ blink::WebFindOptions options; |
+ Find("result", options); |
+ options.findNext = true; |
+ options.forward = false; |
+ Find("result", options); |
+ Find("result", options); |
+ Find("result", options); |
+ Find("result", options); |
+ Find("result", options); |
+ WaitForFinalReply(); |
+ |
+ FindResults results = delegate()->GetFindResults(); |
+ EXPECT_EQ(last_request_id(), results.request_id); |
+ EXPECT_EQ(21, results.number_of_matches); |
+ EXPECT_EQ(17, results.active_match_ordinal); |
+ |
+ // Remove a frame. |
+ FrameTreeNode* root = |
+ static_cast<WebContentsImpl*>(shell()->web_contents())-> |
+ GetFrameTree()->root(); |
+ root->RemoveChild(root->child_at(0)); |
+ |
+ // The number of matches and active match ordinal should update automatically |
+ // to exclude the matches from the removed frame. |
+ results = delegate()->GetFindResults(); |
+ EXPECT_EQ(last_request_id(), results.request_id); |
+ EXPECT_EQ(12, results.number_of_matches); |
+ EXPECT_EQ(8, results.active_match_ordinal); |
+ |
+ // TODO(paulemeyer): Once adding frames mid-session is handled, test that too. |
+} |
+ |
+// Tests Searching in a hidden frame. Matches in the hidden frame should be |
+// ignored. |
+IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE_HiddenFrame) { |
+ LoadAndWait("/find_in_hidden_frame.html"); |
+ |
+ blink::WebFindOptions default_options; |
+ Find("hello", default_options); |
+ WaitForFinalReply(); |
+ FindResults results = delegate()->GetFindResults(); |
+ |
+ EXPECT_EQ(last_request_id(), results.request_id); |
+ EXPECT_EQ(1, results.number_of_matches); |
+ EXPECT_EQ(1, results.active_match_ordinal); |
+} |
+ |
+#if defined(OS_ANDROID) |
+// Tests requesting find match rects. |
+IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE_FindMatchRects) { |
+ LoadAndWait("/find_in_page.html"); |
+ |
+ blink::WebFindOptions default_options; |
+ Find("result", default_options); |
+ WaitForFinalReply(); |
+ EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches); |
+ |
+ // Request the find match rects. |
+ contents()->RequestFindMatchRects(-1); |
+ delegate()->WaitForMatchRects(); |
+ const std::vector<gfx::RectF>& rects = delegate()->find_match_rects(); |
+ |
+ // The first match should be active. |
+ EXPECT_EQ(rects[0], delegate()->active_match_rect()); |
+ |
+ // All results after the first two should be between them in find-in-page |
+ // coordinates. This is because results 2 to 19 are inside an iframe located |
+ // between results 0 and 1. This applies to the fixed div too. |
+ EXPECT_LT(rects[0].y(), rects[1].y()); |
+ for (int i = 2; i < 19; ++i) { |
+ EXPECT_LT(rects[0].y(), rects[i].y()); |
+ EXPECT_GT(rects[1].y(), rects[i].y()); |
+ } |
+ |
+ // Result 3 should be below results 2 and 4. This is caused by the CSS |
+ // transform in the containing div. If the transform doesn't work then result |
+ // 3 will be between results 2 and 4. |
+ EXPECT_GT(rects[3].y(), rects[2].y()); |
+ EXPECT_GT(rects[3].y(), rects[4].y()); |
+ |
+ // Results 6, 7, 8 and 9 should be one below the other in that same order. If |
+ // overflow:scroll is not properly handled then result 8 would be below result |
+ // 9 or result 7 above result 6 depending on the scroll. |
+ EXPECT_LT(rects[6].y(), rects[7].y()); |
+ EXPECT_LT(rects[7].y(), rects[8].y()); |
+ EXPECT_LT(rects[8].y(), rects[9].y()); |
+ |
+ // Results 11, 12, 13 and 14 should be between results 10 and 15, as they are |
+ // inside the table. |
+ EXPECT_GT(rects[11].y(), rects[10].y()); |
+ EXPECT_GT(rects[12].y(), rects[10].y()); |
+ EXPECT_GT(rects[13].y(), rects[10].y()); |
+ EXPECT_GT(rects[14].y(), rects[10].y()); |
+ EXPECT_LT(rects[11].y(), rects[15].y()); |
+ EXPECT_LT(rects[12].y(), rects[15].y()); |
+ EXPECT_LT(rects[13].y(), rects[15].y()); |
+ EXPECT_LT(rects[14].y(), rects[15].y()); |
+ |
+ // Result 11 should be above results 12, 13 and 14 as it's in the table |
+ // header. |
+ EXPECT_LT(rects[11].y(), rects[12].y()); |
+ EXPECT_LT(rects[11].y(), rects[13].y()); |
+ EXPECT_LT(rects[11].y(), rects[14].y()); |
+ |
+ // Result 11 should also be right of results 12, 13 and 14 because of the |
+ // colspan. |
+ EXPECT_GT(rects[11].x(), rects[12].x()); |
+ EXPECT_GT(rects[11].x(), rects[13].x()); |
+ EXPECT_GT(rects[11].x(), rects[14].x()); |
+ |
+ // Result 12 should be left of results 11, 13 and 14 in the table layout. |
+ EXPECT_LT(rects[12].x(), rects[11].x()); |
+ EXPECT_LT(rects[12].x(), rects[13].x()); |
+ EXPECT_LT(rects[12].x(), rects[14].x()); |
+ |
+ // Results 13, 12 and 14 should be one above the other in that order because |
+ // of the rowspan and vertical-align: middle by default. |
+ EXPECT_LT(rects[13].y(), rects[12].y()); |
+ EXPECT_LT(rects[12].y(), rects[14].y()); |
+ |
+ // Result 16 should be below result 15. |
+ EXPECT_GT(rects[15].y(), rects[14].y()); |
+ |
+ // Result 18 should be normalized with respect to the position:relative div, |
+ // and not it's immediate containing div. Consequently, result 18 should be |
+ // above result 17. |
+ EXPECT_GT(rects[17].y(), rects[18].y()); |
+} |
+ |
+// Tests activating the find match nearest to a given point. |
+IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE_ActivateNearestFindMatch) { |
+ LoadAndWait("/find_in_page.html"); |
+ |
+ blink::WebFindOptions default_options; |
+ Find("result", default_options); |
+ WaitForFinalReply(); |
+ EXPECT_EQ(19, delegate()->GetFindResults().number_of_matches); |
+ |
+ // Get the find match rects. |
+ contents()->RequestFindMatchRects(-1); |
+ delegate()->WaitForMatchRects(); |
+ const std::vector<gfx::RectF>& rects = delegate()->find_match_rects(); |
+ |
+ // Activate matches via points inside each of the find match rects, in an |
+ // arbitrary order. Check that the correct match becomes active after each |
+ // activation. |
+ int order[19] = |
+ {11, 13, 2, 0, 16, 5, 7, 10, 6, 1, 15, 14, 9, 17, 18, 3, 8, 12, 4}; |
+ for (int i = 0; i < 19; ++i) { |
+ contents()->ActivateNearestFindResult( |
+ rects[order[i]].CenterPoint().x(), rects[order[i]].CenterPoint().y()); |
+ delegate()->WaitForNextReply(); |
+ EXPECT_EQ(order[i] + 1, delegate()->GetFindResults().active_match_ordinal); |
+ } |
+} |
+#endif // defined(OS_ANDROID) |
} // namespace content |