Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc

Issue 2382003002: Fix dictionary look-up for highlighted text in Mac. (Closed)
Patch Set: Added a test Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698