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

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

Issue 2903833002: Reland: Update TextSelection for non-user initiated events
Patch Set: Suppress superfluous non-user initiated text selection events Created 3 years, 5 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/chrome_content_browser_client.h"
10 #include "chrome/browser/ui/browser.h" 10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 11 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/test/base/in_process_browser_test.h" 12 #include "chrome/test/base/in_process_browser_test.h"
13 #include "chrome/test/base/interactive_test_utils.h" 13 #include "chrome/test/base/interactive_test_utils.h"
14 #include "chrome/test/base/ui_test_utils.h" 14 #include "chrome/test/base/ui_test_utils.h"
15 #include "content/public/browser/content_browser_client.h" 15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/browser/render_frame_host.h" 16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h" 17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_widget_host_view.h" 18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_client.h" 20 #include "content/public/common/content_client.h"
21 #include "content/public/test/browser_test_utils.h" 21 #include "content/public/test/browser_test_utils.h"
22 #include "content/public/test/content_browser_test_utils.h" 22 #include "content/public/test/content_browser_test_utils.h"
23 #include "content/public/test/test_utils.h" 23 #include "content/public/test/test_utils.h"
24 #include "content/public/test/text_input_test_utils.h" 24 #include "content/public/test/text_input_test_utils.h"
25 #include "net/dns/mock_host_resolver.h" 25 #include "net/dns/mock_host_resolver.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h" 26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 #include "ui/base/clipboard/clipboard.h"
27 #include "ui/base/ime/composition_underline.h" 28 #include "ui/base/ime/composition_underline.h"
28 #include "ui/base/ime/text_edit_commands.h" 29 #include "ui/base/ime/text_edit_commands.h"
29 #include "ui/base/ime/text_input_client.h" 30 #include "ui/base/ime/text_input_client.h"
30 #include "ui/base/ime/text_input_mode.h" 31 #include "ui/base/ime/text_input_mode.h"
31 #include "ui/base/ime/text_input_type.h" 32 #include "ui/base/ime/text_input_type.h"
32 #include "url/gurl.h" 33 #include "url/gurl.h"
33 34
34 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 35 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
35 #include "ui/base/ime/linux/text_edit_command_auralinux.h" 36 #include "ui/base/ime/linux/text_edit_command_auralinux.h"
36 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h" 37 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 tester()->GetTextInputType(&type) ? type : ui::TEXT_INPUT_TYPE_NONE; 136 tester()->GetTextInputType(&type) ? type : ui::TEXT_INPUT_TYPE_NONE;
136 if (expected_type_ == type) 137 if (expected_type_ == type)
137 OnSuccess(); 138 OnSuccess();
138 } 139 }
139 140
140 const ui::TextInputType expected_type_; 141 const ui::TextInputType expected_type_;
141 142
142 DISALLOW_COPY_AND_ASSIGN(TextInputManagerTypeObserver); 143 DISALLOW_COPY_AND_ASSIGN(TextInputManagerTypeObserver);
143 }; 144 };
144 145
145 // This class observes TextInputManager for the first change in TextInputState.
146 class TextInputManagerChangeObserver : public TextInputManagerObserverBase {
147 public:
148 explicit TextInputManagerChangeObserver(content::WebContents* web_contents)
149 : TextInputManagerObserverBase(web_contents) {
150 tester()->SetUpdateTextInputStateCalledCallback(base::Bind(
151 &TextInputManagerChangeObserver::VerifyChange, base::Unretained(this)));
152 }
153
154 private:
155 void VerifyChange() {
156 if (tester()->IsTextInputStateChanged())
157 OnSuccess();
158 }
159
160 DISALLOW_COPY_AND_ASSIGN(TextInputManagerChangeObserver);
161 };
162
163 // This class observes |TextInputState.type| for a specific RWHV. 146 // This class observes |TextInputState.type| for a specific RWHV.
164 class ViewTextInputTypeObserver : public TextInputManagerObserverBase { 147 class ViewTextInputTypeObserver : public TextInputManagerObserverBase {
165 public: 148 public:
166 explicit ViewTextInputTypeObserver(content::WebContents* web_contents, 149 explicit ViewTextInputTypeObserver(content::WebContents* web_contents,
167 content::RenderWidgetHostView* rwhv, 150 content::RenderWidgetHostView* rwhv,
168 ui::TextInputType expected_type) 151 ui::TextInputType expected_type)
169 : TextInputManagerObserverBase(web_contents), 152 : TextInputManagerObserverBase(web_contents),
170 web_contents_(web_contents), 153 web_contents_(web_contents),
171 view_(rwhv), 154 view_(rwhv),
172 expected_type_(expected_type) { 155 expected_type_(expected_type) {
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 const content::RenderWidgetHostView* const expected_view_; 250 const content::RenderWidgetHostView* const expected_view_;
268 const size_t expected_length_; 251 const size_t expected_length_;
269 252
270 DISALLOW_COPY_AND_ASSIGN(ViewTextSelectionObserver); 253 DISALLOW_COPY_AND_ASSIGN(ViewTextSelectionObserver);
271 }; 254 };
272 255
273 // This class observes all the text selection updates within a WebContents. 256 // This class observes all the text selection updates within a WebContents.
274 class TextSelectionObserver : public TextInputManagerObserverBase { 257 class TextSelectionObserver : public TextInputManagerObserverBase {
275 public: 258 public:
276 explicit TextSelectionObserver(content::WebContents* web_contents) 259 explicit TextSelectionObserver(content::WebContents* web_contents)
277 : TextInputManagerObserverBase(web_contents) { 260 : TextInputManagerObserverBase(web_contents),
261 selection_changed_count_(0) {
278 tester()->SetOnTextSelectionChangedCallback(base::Bind( 262 tester()->SetOnTextSelectionChangedCallback(base::Bind(
279 &TextSelectionObserver::VerifyChange, base::Unretained(this))); 263 &TextSelectionObserver::VerifyChange, base::Unretained(this)));
280 } 264 }
281 265
282 void WaitForSelectedText(const std::string& text) { 266 void WaitForSelectedText(const std::string& text,
283 selected_text_ = text; 267 bool user_initiated = true) {
284 Wait(); 268 expected_text_ = text;
269 expected_user_initiated_ = user_initiated;
270 if (last_selected_text_.has_value() &&
271 last_selected_text_ == expected_text_ &&
272 last_user_initiated_ == expected_user_initiated_) {
273 OnSuccess();
274 } else {
275 Wait();
276 }
285 } 277 }
286 278
279 int selection_changed_count() { return selection_changed_count_; }
280
287 private: 281 private:
288 void VerifyChange() { 282 void VerifyChange() {
289 if (base::UTF16ToUTF8(tester()->GetUpdatedView()->GetSelectedText()) == 283 selection_changed_count_++;
290 selected_text_) { 284
285 last_selected_text_ =
286 base::UTF16ToUTF8(tester()->GetUpdatedView()->GetSelectedText());
287 EXPECT_TRUE(tester()->GetTextSelectionUserInitiatedForView(
288 tester()->GetUpdatedView(), &last_user_initiated_));
289
290 // Check whether the expected values are already set.
291 if (!expected_text_.has_value())
292 return;
293
294 if (last_selected_text_ == expected_text_ &&
295 last_user_initiated_ == expected_user_initiated_) {
291 OnSuccess(); 296 OnSuccess();
292 } 297 }
293 } 298 }
294 299
295 std::string selected_text_; 300 // These optional properties are also used to verify that the last and
301 // expected properties are set.
302 base::Optional<std::string> last_selected_text_;
303 base::Optional<std::string> expected_text_;
304
305 bool last_user_initiated_;
306 bool expected_user_initiated_;
307
308 int selection_changed_count_;
296 309
297 DISALLOW_COPY_AND_ASSIGN(TextSelectionObserver); 310 DISALLOW_COPY_AND_ASSIGN(TextSelectionObserver);
298 }; 311 };
299 312
300 // This class monitors all the changes in TextInputState and keeps a record of 313 // This class monitors all the changes in TextInputState and keeps a record of
301 // the active views. There is no waiting and the recording process is 314 // the active views. There is no waiting and the recording process is
302 // continuous. 315 // continuous.
303 class RecordActiveViewsObserver { 316 class RecordActiveViewsObserver {
304 public: 317 public:
305 explicit RecordActiveViewsObserver(content::WebContents* web_contents) 318 explicit RecordActiveViewsObserver(content::WebContents* web_contents)
(...skipping 667 matching lines...) Expand 10 before | Expand all | Expand 10 after
973 }; 986 };
974 987
975 for (auto* frame : frames) { 988 for (auto* frame : frames) {
976 focus_frame_and_input(frame); 989 focus_frame_and_input(frame);
977 EXPECT_TRUE(active_contents()->IsFocusedElementEditable()); 990 EXPECT_TRUE(active_contents()->IsFocusedElementEditable());
978 active_contents()->ClearFocusedElement(); 991 active_contents()->ClearFocusedElement();
979 EXPECT_FALSE(active_contents()->IsFocusedElementEditable()); 992 EXPECT_FALSE(active_contents()->IsFocusedElementEditable());
980 } 993 }
981 } 994 }
982 995
996 // The following tests verify that the TextInputManager notifies about a
997 // text selection change event, the corresponding |user_initiated| property
998 // of TextSelection is valid, and the selection clipboard is updated
999 // according to the source of the event.
1000 // See: https://crbug.com/671986
1001
1002 // Test text selection change event for non-user initiated cases
1003 // (eg. JavaScript). Non-user initiated events should not update the selection
1004 // clipboard.
1005 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1006 NonUserInitiatedTextSelection) {
1007 #if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
1008 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
1009 ASSERT_TRUE(clipboard);
1010 clipboard->Clear(ui::CLIPBOARD_TYPE_SELECTION);
1011 #endif
1012
1013 CreateIframePage("a(b, c)");
1014 std::vector<std::string> values{"node_a", "node_b", "node_c"};
1015 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1016 GetFrame(IndexVector{0}),
1017 GetFrame(IndexVector{1})};
1018
1019 for (size_t i = 0; i < frames.size(); ++i)
1020 AddInputFieldToFrame(frames[i], "text", values[i], true);
1021
1022 // Test text selection from JavaScript across frames (non-user initiated).
1023 for (size_t i = 0; i < frames.size(); ++i) {
1024 // Trigger text selection.
1025 TextSelectionObserver trigger_observer(active_contents());
1026 EXPECT_TRUE(
1027 ExecuteScript(frames[i], "document.querySelector('input').select();"));
1028 trigger_observer.WaitForSelectedText(values[i], false);
1029
1030 #if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
1031 // Non-user initiated text selection should not update the selection
1032 // clipboard. See: https://crbug.com/12392
1033 base::string16 result_text;
1034 clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &result_text);
1035 EXPECT_TRUE(result_text.empty());
1036 #endif
1037
1038 // Clear text selection.
1039 TextSelectionObserver clear_observer(active_contents());
1040 EXPECT_TRUE(ExecuteScript(frames[i], "document.getSelection().empty();"));
1041 clear_observer.WaitForSelectedText("", false);
1042 }
1043 }
1044
1045 // Test text selection change event for user initiated cases (eg. key press)
1046 // User initiated events should update the selection clipboard where it is
1047 // supported.
1048 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1049 UserInitiatedTextSelection) {
1050 #if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
1051 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
1052 ASSERT_TRUE(clipboard);
1053 clipboard->Clear(ui::CLIPBOARD_TYPE_SELECTION);
1054 #endif
1055
1056 CreateIframePage("a(b, c)");
1057 std::vector<std::string> values{"node_a", "node_b", "node_c"};
1058 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1059 GetFrame(IndexVector{0}),
1060 GetFrame(IndexVector{1})};
1061
1062 for (size_t i = 0; i < frames.size(); ++i)
1063 AddInputFieldToFrame(frames[i], "text", values[i], true);
1064
1065 // Test text selection by user input across frames (user initiated).
1066 for (size_t i = 0; i < frames.size(); ++i) {
1067 // Focus on input element.
1068 std::string result;
1069 std::string script =
1070 "function getInputField() {"
1071 " return document.querySelector('input');"
1072 "}"
1073 "function onInputFocus(e) {"
1074 " domAutomationController.setAutomationId(0);"
1075 " domAutomationController.send(getInputField().value);"
1076 "}"
1077 "getInputField().addEventListener('focus', onInputFocus);";
1078 EXPECT_TRUE(ExecuteScript(frames[i], script));
1079 EXPECT_TRUE(ExecuteScriptAndExtractString(
1080 frames[i], "window.focus(); document.querySelector('input').focus();",
1081 &result));
1082 EXPECT_EQ(values[i], result);
1083 EXPECT_EQ(frames[i], active_contents()->GetFocusedFrame());
1084
1085 // Press ctrl+a to select text in the input field.
1086 TextSelectionObserver trigger_observer(active_contents());
1087 #if !defined(OS_MACOSX)
1088 SimulateKeyPress(active_contents(), ui::DomKey::FromCharacter('A'),
1089 ui::DomCode::US_A, ui::VKEY_A, true, false, false, false);
1090 #else
1091 // On macOS the select all shortcut (Cmd+A) is handled by the browser via
1092 // keyboard accelerator. Thus we can't simulate key press on WebContents.
1093 // As a workaround, call the SelectAll directly as the shortcut would do.
1094 active_contents()->SelectAll();
1095 #endif
1096 trigger_observer.WaitForSelectedText(values[i], true);
1097
1098 #if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
1099 // User initiated text selection should update the selection clipboard.
1100 base::string16 result_text;
1101 clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &result_text);
1102 EXPECT_EQ(base::ASCIIToUTF16(values[i]), result_text);
1103 #endif
1104
1105 // Press down key to clear text selection.
1106 TextSelectionObserver clear_observer(active_contents());
1107 SimulateKeyPress(active_contents(), ui::DomKey::ARROW_DOWN,
1108 ui::DomCode::ARROW_DOWN, ui::VKEY_DOWN, false, false,
1109 false, false);
1110 clear_observer.WaitForSelectedText("", true);
1111 }
1112 }
1113
1114 // This test changes focus between input fields and checks that no selection
1115 // changed event was triggered when Blink tries to clear a potential text
1116 // selection on the previous input field where there was no text selection.
1117 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1118 TextSelectionOnFocusChange) {
1119 CreateIframePage("a(b, c)");
1120 std::vector<std::string> values{"node_a", "node_b", "node_c"};
1121 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1122 GetFrame(IndexVector{0}),
1123 GetFrame(IndexVector{1})};
1124
1125 for (size_t i = 0; i < frames.size(); ++i) {
1126 AddInputFieldToFrame(frames[i], "text", values[i], true);
1127 AddInputFieldToFrame(frames[i], "number", std::to_string(i), true);
1128 }
1129
1130 for (size_t i = 0; i < frames.size(); ++i) {
1131 std::string script =
1132 "function onInputFocus(e) {"
1133 " domAutomationController.setAutomationId(0);"
1134 " domAutomationController.send(document.activeElement.value);"
1135 "}"
1136 "var text_input = document.querySelector('input[type=text]');"
1137 "text_input.addEventListener('focus', onInputFocus);"
1138 "var number_input = document.querySelector('input[type=number]');"
1139 "number_input.addEventListener('focus', onInputFocus);";
1140 EXPECT_TRUE(ExecuteScript(frames[i], script));
1141 TextSelectionObserver selection_observer(active_contents());
1142
1143 // Focus on text input element.
1144 std::string text_result;
1145 EXPECT_TRUE(ExecuteScriptAndExtractString(
1146 frames[i],
1147 "window.focus(); document.querySelector('input[type=text]').focus();",
1148 &text_result));
1149 EXPECT_EQ(values[i], text_result);
1150 EXPECT_EQ(0, selection_observer.selection_changed_count());
1151
1152 // Focus on number input element.
1153 std::string number_result;
1154 EXPECT_TRUE(ExecuteScriptAndExtractString(
1155 frames[i],
1156 "window.focus(); document.querySelector('input[type=number]').focus();",
1157 &number_result));
1158 EXPECT_EQ(std::to_string(i), number_result);
1159 EXPECT_EQ(0, selection_observer.selection_changed_count());
1160 }
1161 }
1162
1163 // This test replaces text in input filed in IME composition mode. The text is
1164 // replaced by using text selection during the composition mode. Verify that
1165 // the text replacement doesn't trigger any selection changed event.
1166 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1167 ImeSetCompositionTextReplacement) {
1168 CreateIframePage("a(b, c)");
1169 std::vector<std::string> values{"node_a", "node_b", "node_c"};
1170 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1171 GetFrame(IndexVector{0}),
1172 GetFrame(IndexVector{1})};
1173
1174 for (size_t i = 0; i < frames.size(); ++i)
1175 AddInputFieldToFrame(frames[i], "text", values[i], true);
1176
1177 for (size_t i = 0; i < frames.size(); ++i) {
1178 // Focus on input element.
1179 EXPECT_TRUE(ExecuteScript(frames[i],
1180 "window.focus();"
1181 "document.querySelector('input').focus();"));
1182
1183 TextSelectionObserver selection_observer(active_contents());
1184
1185 // Do text replacement in composition mode.
1186 ViewCompositionRangeChangedObserver range_observer_set_composition(
1187 active_contents(), frames[i]->GetView());
1188 content::SendImeSetCompositionTextToWidget(
1189 frames[i]->GetView()->GetRenderWidgetHost(),
1190 base::UTF8ToUTF16(values[i]), std::vector<ui::CompositionUnderline>(),
1191 gfx::Range(5, 1), 0, 0);
1192 range_observer_set_composition.Wait();
1193
1194 // Check composition text.
1195 std::string result;
1196 EXPECT_TRUE(ExecuteScriptAndExtractString(
1197 frames[i],
1198 "domAutomationController.setAutomationId(0);"
1199 "var value = document.querySelector('input').value;"
1200 "domAutomationController.send(value);",
1201 &result));
1202 EXPECT_EQ("node_" + values[i], result);
1203
1204 // Text selection should not be triggered by IME composition text
1205 // replacement.
1206 EXPECT_EQ(0, selection_observer.selection_changed_count());
1207 }
1208 }
1209
983 // TODO(ekaramad): The following tests are specifically written for Aura and are 1210 // TODO(ekaramad): The following tests are specifically written for Aura and are
984 // based on InputMethodObserver. Write similar tests for Mac/Android/Mus 1211 // based on InputMethodObserver. Write similar tests for Mac/Android/Mus
985 // (crbug.com/602723). 1212 // (crbug.com/602723).
986 #if defined(USE_AURA) 1213 #if defined(USE_AURA)
987 // ----------------------------------------------------------------------------- 1214 // -----------------------------------------------------------------------------
988 // Input Method Observer Tests 1215 // Input Method Observer Tests
989 // 1216 //
990 // The following tests will make use of the InputMethodObserver to verify that 1217 // The following tests will make use of the InputMethodObserver to verify that
991 // OOPIF pages interact properly with the InputMethod through the tab's view. 1218 // OOPIF pages interact properly with the InputMethod through the tab's view.
992 1219
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after
1367 1594
1368 // Closing this WebContents while we still hold on to our TestBrowserClient. 1595 // Closing this WebContents while we still hold on to our TestBrowserClient.
1369 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( 1596 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
1370 1, TabStripModel::CLOSE_USER_GESTURE)); 1597 1, TabStripModel::CLOSE_USER_GESTURE));
1371 1598
1372 // For the cleanup of the original WebContents in tab index 0. 1599 // For the cleanup of the original WebContents in tab index 0.
1373 content::SetBrowserClientForTesting(old_browser_client); 1600 content::SetBrowserClientForTesting(old_browser_client);
1374 } 1601 }
1375 #endif // defined(MAC_OSX) 1602 #endif // defined(MAC_OSX)
1376 #endif // !defined(OS_ANDROID) 1603 #endif // !defined(OS_ANDROID)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698