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

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: Rebased 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
« no previous file with comments | « no previous file | content/browser/renderer_host/render_widget_host_view_mac.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(),
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after
941 true, true, true, false)); 946 true, true, true, false));
942 ui::SetTextEditKeyBindingsDelegate(old_delegate); 947 ui::SetTextEditKeyBindingsDelegate(old_delegate);
943 948
944 // Verify that the input field in the subframe is erased. 949 // Verify that the input field in the subframe is erased.
945 EXPECT_TRUE(ExecuteScriptAndExtractString( 950 EXPECT_TRUE(ExecuteScriptAndExtractString(
946 child, "window.domAutomationController.send(getInputFieldText());", 951 child, "window.domAutomationController.send(getInputFieldText());",
947 &result)); 952 &result));
948 EXPECT_EQ("", result); 953 EXPECT_EQ("", result);
949 } 954 }
950 #endif 955 #endif
956
957 // Ideally, the following code + test should be live in
958 // 'site_per_process_mac_browsertest.mm'. However, the test
959 // 'LookUpStringForRangeRoutesToFocusedWidget' relies on an override in
960 // ContentBrowserClient to register its filters in time. In content shell, we
961 // cannot have two instances of ShellContentBrowserClient (due to a DCHECK in
962 // the ctor). Therefore, we put the test here to use ChromeContentBrowserClient
963 // which does not have the same singleton constraint.
EhsanK 2016/10/18 04:02:08 This comment explains why we are adding the test h
964 #if defined(OS_MACOSX)
965 // An observer of number of open windows.
966 class WindowCountObserver {
967 public:
968 explicit WindowCountObserver(size_t lower_limit) : limit_(lower_limit) {}
969 ~WindowCountObserver() {}
970
971 // Keep polling the count of open windows until the number exceeds |limit_|.
972 void WaitForLimitOrMore() {
973 size_t current_count = content::GetOpenNSWindowsCount();
974 if (current_count >= limit_)
975 return;
976
977 message_loop_runner_ = new content::MessageLoopRunner();
978 message_loop_runner_->Run();
979 content::BrowserThread::PostTask(
980 content::BrowserThread::UI, FROM_HERE,
981 base::Bind(&WindowCountObserver::CheckWindowCount,
982 base::Unretained(this)));
983 }
984
985 private:
986 void CheckWindowCount() {
987 size_t current_count = content::GetOpenNSWindowsCount();
988 if (current_count >= limit_) {
989 message_loop_runner_->Quit();
990 return;
991 }
992 content::BrowserThread::PostTask(
993 content::BrowserThread::UI, FROM_HERE,
994 base::Bind(&WindowCountObserver::CheckWindowCount,
995 base::Unretained(this)));
996 }
997
998 size_t limit_;
999 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
1000
1001 DISALLOW_COPY_AND_ASSIGN(WindowCountObserver);
1002 };
1003
1004 // The original TextInputClientMessageFilter is added during the initialization
1005 // phase of RenderProcessHost. The only chance we have to add the test filter
1006 // (so that it can receive the TextInputClientMac incoming IPC messages) is
1007 // during the call to RenderProcessWillLaunch() on ContentBrowserClient. This
1008 // class provides that for testing.
1009 class TestBrowserClient : public ChromeContentBrowserClient {
1010 public:
1011 TestBrowserClient() {}
1012 ~TestBrowserClient() override {}
1013
1014 // ContentBrowserClient overrides.
1015 void RenderProcessWillLaunch(
1016 content::RenderProcessHost* process_host) override {
1017 ChromeContentBrowserClient::RenderProcessWillLaunch(process_host);
1018 filters_.push_back(
1019 new content::TestTextInputClientMessageFilter(process_host));
1020 }
1021
1022 // Retrieves the registered filter for the given RenderProcessHost. It will
1023 // return false if the RenderProcessHost was initialized while a different
1024 // instance of ContentBrowserClient was in action.
1025 scoped_refptr<content::TestTextInputClientMessageFilter>
1026 GetTextInputClientMessageFilterForProcess(
1027 content::RenderProcessHost* process_host) const {
1028 for (auto filter : filters_) {
1029 if (filter->process() == process_host)
1030 return filter;
1031 }
1032 return nullptr;
1033 }
1034
1035 private:
1036 std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>>
1037 filters_;
1038
1039 DISALLOW_COPY_AND_ASSIGN(TestBrowserClient);
1040 };
1041
1042 // This test verifies that requests for dictionary lookup based on selection
1043 // range are routed to the focused RenderWidgetHost.
1044 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1045 LookUpStringForRangeRoutesToFocusedWidget) {
1046 // TestBrowserClient needs to replace the ChromeContenBrowserClient after most
1047 // things are initialized but before the WebContents is created. Here we make
1048 // that happen by creating a new WebContents in a new tab. But before the test
1049 // exits, we must destroy the contents and replace the old
1050 // ContentBrowserClient because the original WebContents and the new one have
1051 // been initialized with the original ContentBrowserClient and the new
1052 // TestBrowserClient, respectively.
1053 TestBrowserClient browser_client;
1054 content::ContentBrowserClient* old_browser_client =
1055 content::SetBrowserClientForTesting(&browser_client);
1056
1057 content::WebContents* new_contents =
1058 content::WebContents::Create(content::WebContents::CreateParams(
1059 active_contents()->GetBrowserContext(), nullptr));
1060 browser()->tab_strip_model()->InsertWebContentsAt(1, new_contents,
1061 TabStripModel::ADD_ACTIVE);
1062 EXPECT_EQ(active_contents(), new_contents);
1063
1064 // Starting the test body.
1065 CreateIframePage("a(b)");
1066 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1067 GetFrame(IndexVector{0})};
1068 std::vector<content::RenderWidgetHostView*> views;
1069 for (auto frame : frames)
1070 views.push_back(frame->GetView());
1071 std::vector<std::string> values{"main frame", "child frame"};
1072
1073 // Adding some field with text to each frame so that we can later query for
1074 // dictionary lookup.
1075 for (size_t i = 0; i < frames.size(); ++i)
1076 AddInputFieldToFrame(frames[i], "text", values[i], true);
1077
1078 std::string result;
1079 // Recording window count now so that our WindowCountObserver will detect the
1080 // popup window.
1081 size_t current_window_count = content::GetOpenNSWindowsCount();
1082
1083 // Ensure we have both focus and selected text inside the main frame.
1084 EXPECT_TRUE(
1085 ExecuteScript(frames[0], "document.querySelector('input').focus();"));
1086
1087 // Request for the dictionary lookup and intercept the word on its way back.
1088 // The request is always on the tab's view which is a RenderWidgetHostViewMac.
1089 content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 4));
1090
1091 // Wait until the result comes back.
1092 auto root_filter = browser_client.GetTextInputClientMessageFilterForProcess(
1093 frames[0]->GetProcess());
1094 EXPECT_TRUE(root_filter);
1095 root_filter->WaitForStringFromRange();
1096
1097 EXPECT_EQ("main", root_filter->string_from_range());
1098
1099 // Wait for the popup to appear to make sure TextInputClientMac has consumed
1100 // the reply handler for the previous request.
1101 WindowCountObserver(current_window_count).WaitForLimitOrMore();
1102
1103 // Ensure we have both focus and selected text inside the child frame.
1104 EXPECT_TRUE(
1105 ExecuteScript(frames[1], "document.querySelector('input').focus();"));
1106
1107 // Record window count again for the popup observer.
1108 current_window_count = content::GetOpenNSWindowsCount();
1109
1110 // Make another request.
1111 content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 5));
1112
1113 // Wait until the result comes back.
1114 auto child_filter = browser_client.GetTextInputClientMessageFilterForProcess(
1115 frames[1]->GetProcess());
1116 child_filter->WaitForStringFromRange();
1117
1118 EXPECT_EQ("child", child_filter->string_from_range());
1119
1120 // Wait for the popup to appear to make sure TextInputClientMac has consumed
1121 // the reply handler for the previous request.
1122 WindowCountObserver(current_window_count).WaitForLimitOrMore();
1123 // Test ends here. The rest is cleanup.
1124
1125 // Closing this WebContents while we still hold on to our TestBrowserClient.
1126 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
1127 1, TabStripModel::CLOSE_USER_GESTURE));
1128
1129 // For the cleanup of the original WebContents in tab index 0.
1130 content::SetBrowserClientForTesting(old_browser_client);
1131 }
1132 #endif
OLDNEW
« no previous file with comments | « no previous file | content/browser/renderer_host/render_widget_host_view_mac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698