Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <vector> | 5 #include <vector> |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "chrome/browser/chrome_content_browser_client.h" | |
| 9 #include "chrome/browser/ui/browser.h" | 10 #include "chrome/browser/ui/browser.h" |
| 10 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 11 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 11 #include "chrome/test/base/in_process_browser_test.h" | 12 #include "chrome/test/base/in_process_browser_test.h" |
| 12 #include "chrome/test/base/interactive_test_utils.h" | 13 #include "chrome/test/base/interactive_test_utils.h" |
| 13 #include "chrome/test/base/ui_test_utils.h" | 14 #include "chrome/test/base/ui_test_utils.h" |
| 15 #include "content/public/browser/browser_message_filter.h" | |
| 16 #include "content/public/browser/content_browser_client.h" | |
| 14 #include "content/public/browser/render_frame_host.h" | 17 #include "content/public/browser/render_frame_host.h" |
| 15 #include "content/public/browser/render_process_host.h" | 18 #include "content/public/browser/render_process_host.h" |
| 16 #include "content/public/browser/render_widget_host_view.h" | 19 #include "content/public/browser/render_widget_host_view.h" |
| 17 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
| 21 #include "content/public/common/content_client.h" | |
| 18 #include "content/public/test/browser_test_utils.h" | 22 #include "content/public/test/browser_test_utils.h" |
| 19 #include "content/public/test/content_browser_test_utils.h" | 23 #include "content/public/test/content_browser_test_utils.h" |
| 20 #include "content/public/test/test_utils.h" | 24 #include "content/public/test/test_utils.h" |
| 21 #include "content/public/test/text_input_test_utils.h" | 25 #include "content/public/test/text_input_test_utils.h" |
| 22 #include "net/dns/mock_host_resolver.h" | 26 #include "net/dns/mock_host_resolver.h" |
| 23 #include "net/test/embedded_test_server/embedded_test_server.h" | 27 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 24 #include "ui/base/ime/composition_underline.h" | 28 #include "ui/base/ime/composition_underline.h" |
| 25 #include "ui/base/ime/text_edit_commands.h" | 29 #include "ui/base/ime/text_edit_commands.h" |
| 26 #include "ui/base/ime/text_input_client.h" | 30 #include "ui/base/ime/text_input_client.h" |
| 27 #include "ui/base/ime/text_input_mode.h" | 31 #include "ui/base/ime/text_input_mode.h" |
| (...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 } | 327 } |
| 324 | 328 |
| 325 protected: | 329 protected: |
| 326 content::WebContents* active_contents() { | 330 content::WebContents* active_contents() { |
| 327 return browser()->tab_strip_model()->GetActiveWebContents(); | 331 return browser()->tab_strip_model()->GetActiveWebContents(); |
| 328 } | 332 } |
| 329 | 333 |
| 330 // static | 334 // static |
| 331 // Adds an <input> field to a given frame by executing javascript code. | 335 // Adds an <input> field to a given frame by executing javascript code. |
| 332 // The input can be added as the first element or the last element of | 336 // The input can be added as the first element or the last element of |
| 333 // |document.body|. | 337 // |document.body|. The text range defined by |selection_range| will be |
| 338 // marked. | |
| 334 static void AddInputFieldToFrame(content::RenderFrameHost* rfh, | 339 static void AddInputFieldToFrame(content::RenderFrameHost* rfh, |
| 335 const std::string& type, | 340 const std::string& type, |
| 336 const std::string& value, | 341 const std::string& value, |
| 337 bool append_as_first_child) { | 342 bool append_as_first_child) { |
| 338 std::string script = base::StringPrintf( | 343 std::string script = base::StringPrintf( |
| 339 "var input = document.createElement('input');" | 344 "var input = document.createElement('input');" |
| 340 "input.setAttribute('type', '%s');" | 345 "input.setAttribute('type', '%s');" |
| 341 "input.setAttribute('value', '%s');" | 346 "input.setAttribute('value', '%s');" |
| 342 "document.body.%s;", | 347 "document.body.%s;", |
| 343 type.c_str(), value.c_str(), | 348 type.c_str(), value.c_str(), |
| 344 append_as_first_child ? "insertBefore(input, document.body.firstChild)" | 349 append_as_first_child ? "insertBefore(input, document.body.firstChild)" |
| 345 : "appendChild(input)"); | 350 : "appendChild(input)"); |
| 346 EXPECT_TRUE(ExecuteScript(rfh, script)); | 351 EXPECT_TRUE(ExecuteScript(rfh, script)); |
| 347 } | 352 } |
| 348 | 353 |
| 354 // Returns a point representing the top-left corner of the first <input> | |
| 355 // inside |rfh|. | |
|
EhsanK
2016/10/11 19:31:56
Apologies. This method should have not been in the
| |
| 356 static gfx::Point GetTopLeftOfFirstInput(content::RenderFrameHost* rfh) { | |
| 357 int x = 0; | |
| 358 int y = 0; | |
| 359 // Get the client X value. | |
| 360 EXPECT_TRUE(content::ExecuteScriptAndExtractInt( | |
| 361 rfh, | |
| 362 "var input = document.querySelector('input');" | |
| 363 "domAutomationController.send(input.getBoundingClientRect().left);", | |
| 364 &x)); | |
| 365 EXPECT_TRUE(content::ExecuteScriptAndExtractInt( | |
| 366 rfh, | |
| 367 "var input = document.querySelector('input');" | |
| 368 "domAutomationController.send(input.getBoundingClientRect().top);", | |
| 369 &y)); | |
| 370 return gfx::Point(x, y); | |
| 371 } | |
| 372 | |
| 349 // Uses 'cross_site_iframe_factory.html'. The main frame's domain is | 373 // Uses 'cross_site_iframe_factory.html'. The main frame's domain is |
| 350 // 'a.com'. | 374 // 'a.com'. |
| 351 void CreateIframePage(const std::string& structure) { | 375 void CreateIframePage(const std::string& structure) { |
| 352 std::string path = base::StringPrintf("/cross_site_iframe_factory.html?%s", | 376 std::string path = base::StringPrintf("/cross_site_iframe_factory.html?%s", |
| 353 structure.c_str()); | 377 structure.c_str()); |
| 354 GURL main_url(embedded_test_server()->GetURL("a.com", path)); | 378 GURL main_url(embedded_test_server()->GetURL("a.com", path)); |
| 355 ui_test_utils::NavigateToURL(browser(), main_url); | 379 ui_test_utils::NavigateToURL(browser(), main_url); |
| 356 } | 380 } |
| 357 | 381 |
| 358 // Iteratively uses ChildFrameAt(frame, i) to get the i-th child frame | 382 // Iteratively uses ChildFrameAt(frame, i) to get the i-th child frame |
| (...skipping 582 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 941 true, true, true, false)); | 965 true, true, true, false)); |
| 942 ui::SetTextEditKeyBindingsDelegate(old_delegate); | 966 ui::SetTextEditKeyBindingsDelegate(old_delegate); |
| 943 | 967 |
| 944 // Verify that the input field in the subframe is erased. | 968 // Verify that the input field in the subframe is erased. |
| 945 EXPECT_TRUE(ExecuteScriptAndExtractString( | 969 EXPECT_TRUE(ExecuteScriptAndExtractString( |
| 946 child, "window.domAutomationController.send(getInputFieldText());", | 970 child, "window.domAutomationController.send(getInputFieldText());", |
| 947 &result)); | 971 &result)); |
| 948 EXPECT_EQ("", result); | 972 EXPECT_EQ("", result); |
| 949 } | 973 } |
| 950 #endif | 974 #endif |
| 975 | |
| 976 #if defined(OS_MACOSX) | |
|
Charlie Reis
2016/10/07 19:07:21
We added a site_per_process_mac_browsertest.mm rec
EhsanK
2016/10/11 19:31:56
Yes, ideally, we should stay within that file. Spe
Charlie Reis
2016/10/17 21:35:03
Ok, sounds like ShellContentBrowserClient was non-
| |
| 977 // An observer of number of open windows. | |
| 978 class WindowCountObserver { | |
| 979 public: | |
| 980 explicit WindowCountObserver(size_t lower_limit) : limit_(lower_limit) {} | |
| 981 ~WindowCountObserver() {} | |
| 982 | |
| 983 // Keep polling the count of open windows until the number exceeds |limit_|. | |
| 984 void WaitForLimitOrMore() { | |
| 985 size_t current_count = content::GetOpenNSWindowsCount(); | |
| 986 if (current_count >= limit_) | |
| 987 return; | |
| 988 | |
| 989 message_loop_runner_ = new content::MessageLoopRunner(); | |
| 990 message_loop_runner_->Run(); | |
| 991 content::BrowserThread::PostTask( | |
| 992 content::BrowserThread::UI, FROM_HERE, | |
| 993 base::Bind(&WindowCountObserver::CheckWindowCount, | |
| 994 base::Unretained(this))); | |
| 995 } | |
| 996 | |
| 997 private: | |
| 998 void CheckWindowCount() { | |
| 999 size_t current_count = content::GetOpenNSWindowsCount(); | |
| 1000 if (current_count >= limit_) { | |
| 1001 message_loop_runner_->Quit(); | |
| 1002 return; | |
| 1003 } | |
| 1004 content::BrowserThread::PostTask( | |
| 1005 content::BrowserThread::UI, FROM_HERE, | |
| 1006 base::Bind(&WindowCountObserver::CheckWindowCount, | |
| 1007 base::Unretained(this))); | |
| 1008 } | |
| 1009 | |
| 1010 size_t limit_; | |
| 1011 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; | |
| 1012 | |
| 1013 DISALLOW_COPY_AND_ASSIGN(WindowCountObserver); | |
| 1014 }; | |
| 1015 | |
| 1016 // The original TextInputClientMessageFilter is added during the initialization | |
| 1017 // phase of RenderProcessHost. The only chance we have to add the test filter | |
| 1018 // (so that it can receive the TextInputClientMac incoming IPC messages) is | |
| 1019 // during the call to RenderProcessWillLaunch() on ContentBrowserClient. This | |
| 1020 // class provides that for testing. | |
| 1021 class TestBrowserClient : public ChromeContentBrowserClient { | |
| 1022 public: | |
| 1023 TestBrowserClient() {} | |
| 1024 ~TestBrowserClient() override {} | |
| 1025 | |
| 1026 // ContentBrowserClient overrides. | |
| 1027 void RenderProcessWillLaunch( | |
| 1028 content::RenderProcessHost* process_host) override { | |
| 1029 ChromeContentBrowserClient::RenderProcessWillLaunch(process_host); | |
| 1030 filters_.push_back( | |
| 1031 new content::TestTextInputClientMessageFilter(process_host)); | |
| 1032 } | |
| 1033 | |
| 1034 // Retrieves the registered filter for the given RenderProcessHost. It will | |
| 1035 // return false if the RenderProcessHost was initialized while a different | |
| 1036 // instance of ContentBrowserClient was in action. | |
| 1037 scoped_refptr<content::TestTextInputClientMessageFilter> | |
| 1038 GetTextInputClientMessageFilterForProcess( | |
| 1039 content::RenderProcessHost* process_host) const { | |
| 1040 for (auto filter : filters_) { | |
| 1041 if (filter->process() == process_host) | |
| 1042 return filter; | |
| 1043 } | |
| 1044 return nullptr; | |
| 1045 } | |
| 1046 | |
| 1047 private: | |
| 1048 std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>> | |
| 1049 filters_; | |
| 1050 | |
| 1051 DISALLOW_COPY_AND_ASSIGN(TestBrowserClient); | |
| 1052 }; | |
| 1053 | |
| 1054 // This test verifies that requests for dictionary lookup based on selection | |
| 1055 // range are routed to the focused RenderWidgetHost. | |
| 1056 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, | |
| 1057 LookUpStringForRangeRoutesToFocusedWidget) { | |
| 1058 // TestBrowserClient needs to replace the ChromeContenBrowserClient after most | |
| 1059 // things are initialized but before the WebContents is created. Here we make | |
| 1060 // that happen by creating a new WebContents in a new tab. But before the test | |
| 1061 // exits, we must destroy the contents and replace the old | |
| 1062 // ContentBrowserClient because the original WebContents and the new one have | |
| 1063 // been initialized with the original ContentBrowserClient and the new | |
| 1064 // TestBrowserClient, respectively. | |
| 1065 TestBrowserClient browser_client; | |
| 1066 content::ContentBrowserClient* old_browser_client = | |
| 1067 content::SetBrowserClientForTesting(&browser_client); | |
| 1068 | |
| 1069 content::WebContents* new_contents = | |
| 1070 content::WebContents::Create(content::WebContents::CreateParams( | |
| 1071 active_contents()->GetBrowserContext(), nullptr)); | |
| 1072 browser()->tab_strip_model()->InsertWebContentsAt(1, new_contents, | |
| 1073 TabStripModel::ADD_ACTIVE); | |
| 1074 EXPECT_EQ(active_contents(), new_contents); | |
| 1075 | |
| 1076 // Starting the test body. | |
| 1077 CreateIframePage("a(b)"); | |
| 1078 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}), | |
| 1079 GetFrame(IndexVector{0})}; | |
| 1080 std::vector<content::RenderWidgetHostView*> views; | |
| 1081 for (auto frame : frames) | |
| 1082 views.push_back(frame->GetView()); | |
| 1083 std::vector<std::string> values{"main frame", "child frame"}; | |
| 1084 | |
| 1085 // Adding some field with text to each frame so that we can later query for | |
| 1086 // dictionary lookup. | |
| 1087 for (size_t i = 0; i < frames.size(); ++i) | |
| 1088 AddInputFieldToFrame(frames[i], "text", values[i], true); | |
| 1089 | |
| 1090 std::string result; | |
| 1091 // Recording window count now so that our WindowCountObserver will detect the | |
| 1092 // popup window. | |
| 1093 size_t current_window_count = content::GetOpenNSWindowsCount(); | |
| 1094 | |
| 1095 // Ensure we have both focus and selected text inside the main frame. | |
| 1096 EXPECT_TRUE( | |
| 1097 ExecuteScript(frames[0], "document.querySelector('input').focus();")); | |
| 1098 | |
| 1099 // Request for the dictionary lookup and intercept the word on its way back. | |
| 1100 // The request is always on the tab's view which is a RenderWidgetHostViewMac. | |
| 1101 content::AskForLookUpDictoinaryForRange(views[0], gfx::Range(0, 4)); | |
| 1102 | |
| 1103 // Wait until the result comes back. | |
| 1104 auto root_filter = browser_client.GetTextInputClientMessageFilterForProcess( | |
| 1105 frames[0]->GetProcess()); | |
| 1106 EXPECT_TRUE(root_filter); | |
| 1107 root_filter->WaitForStringFromRange(); | |
| 1108 | |
| 1109 EXPECT_EQ("main", root_filter->string_from_range()); | |
| 1110 | |
| 1111 // Wait for the popup to appear to make sure TextInputClientMac has consumed | |
| 1112 // the reply handler for the previous request. | |
| 1113 WindowCountObserver(current_window_count).WaitForLimitOrMore(); | |
| 1114 | |
| 1115 // Ensure we have both focus and selected text inside the child frame. | |
| 1116 EXPECT_TRUE( | |
| 1117 ExecuteScript(frames[1], "document.querySelector('input').focus();")); | |
| 1118 | |
| 1119 // Record window count again for the popup observer. | |
| 1120 current_window_count = content::GetOpenNSWindowsCount(); | |
| 1121 | |
| 1122 // Make another request. | |
| 1123 content::AskForLookUpDictoinaryForRange(views[0], gfx::Range(0, 5)); | |
| 1124 | |
| 1125 // Wait until the result comes back. | |
| 1126 auto child_filter = browser_client.GetTextInputClientMessageFilterForProcess( | |
| 1127 frames[1]->GetProcess()); | |
| 1128 child_filter->WaitForStringFromRange(); | |
| 1129 | |
| 1130 EXPECT_EQ("child", child_filter->string_from_range()); | |
| 1131 | |
| 1132 // Wait for the popup to appear to make sure TextInputClientMac has consumed | |
| 1133 // the reply handler for the previous request. | |
| 1134 WindowCountObserver(current_window_count).WaitForLimitOrMore(); | |
| 1135 // Test ends here. The rest is cleanup. | |
| 1136 | |
| 1137 // Closing this WebContents while we still hold on to our TestBrowserClient. | |
| 1138 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( | |
| 1139 1, TabStripModel::CLOSE_USER_GESTURE)); | |
| 1140 | |
| 1141 // For the cleanup of the original WebContents in tab index 0. | |
| 1142 content::SetBrowserClientForTesting(old_browser_client); | |
| 1143 } | |
| 1144 #endif | |
| OLD | NEW |