1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.content.browser;
7 import android.content.ClipData;
8 import android.content.ClipboardManager;
9 import android.content.Context;
10 import android.test.suitebuilder.annotation.SmallTest;
11 import android.text.TextUtils;
13 import org.chromium.base.ThreadUtils;
14 import org.chromium.base.test.util.Feature;
15 import org.chromium.base.test.util.UrlUtils;
16 import org.chromium.content.browser.test.util.Criteria;
17 import org.chromium.content.browser.test.util.CriteriaHelper;
18 import org.chromium.content.browser.test.util.DOMUtils;
19 import org.chromium.content_shell_apk.ContentShellTestBase;
21 import java.util.concurrent.Callable;
24 * Integration tests for text selection-related behavior.
26 public class ContentViewCoreSelectionTest extends ContentShellTestBase {
27 private static final String DATA_URL = UrlUtils.encodeHtmlDataUri(
28 "<html><head><meta name=\"viewport\""
29 + "content=\"width=device-width, initial-scale=1.1, maximum-scale=1.5\" /></head>"
30 + "<body><form action=\"about:blank\">"
31 + "<input id=\"empty_input_text\" type=\"text\" />"
32 + "<br/><input id=\"input_text\" type=\"text\" value=\"SampleInputText\" />"
33 + "<br/><textarea id=\"textarea\" rows=\"2\" cols=\"20\">SampleTextArea</textarea>"
34 + "<br/><input id=\"input_password\" type=\"password\" value=\"SamplePassword\" />"
35 + "<br/><p><span id=\"plain_text_1\">SamplePlainTextOne</span></p>"
36 + "<br/><p><span id=\"plain_text_2\">SamplePlainTextTwo</span></p>"
37 + "<br/><input id=\"readonly_text\" type=\"text\" readonly value=\"Sample Text\"/>"
38 + "<br/><input id=\"disabled_text\" type=\"text\" disabled value=\"Sample Text\" />"
39 + "</form></body></html>");
40 private ContentViewCore mContentViewCore;
43 public void setUp() throws Exception {
46 launchContentShellWithUrl(DATA_URL);
47 assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
49 mContentViewCore = getContentViewCore();
50 assertWaitForPageScaleFactorMatch(1.1f);
51 assertWaitForSelectActionBarVisible(false);
52 assertWaitForPastePopupStatus(false);
56 @Feature({"TextSelection"})
57 public void testSelectionClearedAfterLossOfFocus() throws Throwable {
58 requestFocusOnUiThread(true);
60 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
61 assertWaitForSelectActionBarVisible(true);
63 requestFocusOnUiThread(false);
64 assertWaitForSelectActionBarVisible(false);
65 assertFalse(mContentViewCore.hasSelection());
67 requestFocusOnUiThread(true);
68 assertWaitForSelectActionBarVisible(false);
69 assertFalse(mContentViewCore.hasSelection());
73 @Feature({"TextSelection"})
74 public void testSelectionPreservedAfterLossOfFocusIfRequested() throws Throwable {
75 requestFocusOnUiThread(true);
77 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
78 assertWaitForSelectActionBarVisible(true);
79 assertTrue(mContentViewCore.hasSelection());
81 mContentViewCore.preserveSelectionOnNextLossOfFocus();
82 requestFocusOnUiThread(false);
83 assertWaitForSelectActionBarVisible(false);
84 assertTrue(mContentViewCore.hasSelection());
86 requestFocusOnUiThread(true);
87 assertWaitForSelectActionBarVisible(true);
88 assertTrue(mContentViewCore.hasSelection());
90 // Losing focus yet again should properly clear the selection.
91 requestFocusOnUiThread(false);
92 assertWaitForSelectActionBarVisible(false);
93 assertFalse(mContentViewCore.hasSelection());
97 @Feature({"TextSelection"})
98 public void testSelectionPreservedAfterReshown() throws Throwable {
99 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
100 assertWaitForSelectActionBarVisible(true);
101 assertTrue(mContentViewCore.hasSelection());
103 setVisibileOnUiThread(false);
104 assertWaitForSelectActionBarVisible(false);
105 assertTrue(mContentViewCore.hasSelection());
107 setVisibileOnUiThread(true);
108 assertWaitForSelectActionBarVisible(true);
109 assertTrue(mContentViewCore.hasSelection());
113 @Feature({"TextSelection"})
114 public void testSelectionPreservedAfterReattached() throws Throwable {
115 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
116 assertWaitForSelectActionBarVisible(true);
117 assertTrue(mContentViewCore.hasSelection());
119 setAttachedOnUiThread(false);
120 assertWaitForSelectActionBarVisible(false);
121 assertTrue(mContentViewCore.hasSelection());
123 setAttachedOnUiThread(true);
124 assertWaitForSelectActionBarVisible(true);
125 assertTrue(mContentViewCore.hasSelection());
129 @Feature({"TextInput"})
130 public void testPastePopupNotShownOnLongPressingNonEmptyInput() throws Throwable {
131 copyStringToClipboard("SampleTextToCopy");
132 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
133 assertWaitForPastePopupStatus(true);
134 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
135 assertWaitForSelectActionBarVisible(true);
136 assertWaitForPastePopupStatus(false);
140 @Feature({"TextInput"})
141 public void testPastePopupClearedOnTappingEmptyInput() throws Throwable {
142 copyStringToClipboard("SampleTextToCopy");
143 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
144 assertWaitForPastePopupStatus(true);
145 DOMUtils.clickNode(this, mContentViewCore, "empty_input_text");
146 assertWaitForPastePopupStatus(false);
150 @Feature({"TextInput"})
151 public void testPastePopupClearedOnTappingNonEmptyInput() throws Throwable {
152 copyStringToClipboard("SampleTextToCopy");
153 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
154 assertWaitForPastePopupStatus(true);
155 DOMUtils.clickNode(this, mContentViewCore, "input_text");
156 assertWaitForPastePopupStatus(false);
160 @Feature({"TextInput"})
161 public void testPastePopupClearedOnTappingOutsideInput() throws Throwable {
162 copyStringToClipboard("SampleTextToCopy");
163 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
164 assertWaitForPastePopupStatus(true);
165 DOMUtils.clickNode(this, mContentViewCore, "plain_text_2");
166 assertWaitForPastePopupStatus(false);
170 @Feature({"TextInput"})
171 public void testPastePopupClearedOnLongPressingOutsideInput() throws Throwable {
172 copyStringToClipboard("SampleTextToCopy");
173 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
174 assertWaitForPastePopupStatus(true);
175 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_2");
176 assertWaitForPastePopupStatus(false);
180 @Feature({"TextInput"})
181 public void testPastePopupNotShownOnLongPressingReadOnlyInput() throws Throwable {
182 copyStringToClipboard("SampleTextToCopy");
183 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
184 assertWaitForPastePopupStatus(true);
185 assertTrue(mContentViewCore.hasInsertion());
186 DOMUtils.longPressNode(this, mContentViewCore, "readonly_text");
187 assertWaitForPastePopupStatus(false);
188 assertFalse(mContentViewCore.hasInsertion());
192 @Feature({"TextInput"})
193 public void testPastePopupNotShownOnLongPressingDisabledInput() throws Throwable {
194 copyStringToClipboard("SampleTextToCopy");
195 DOMUtils.longPressNode(this, mContentViewCore, "empty_input_text");
196 assertWaitForPastePopupStatus(true);
197 assertTrue(mContentViewCore.hasInsertion());
198 DOMUtils.longPressNode(this, mContentViewCore, "disabled_text");
199 assertWaitForPastePopupStatus(false);
200 assertFalse(mContentViewCore.hasInsertion());
204 @Feature({"TextInput"})
205 public void testActionBarConfiguredCorrectlyForInput() throws Throwable {
206 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
207 assertWaitForSelectActionBarVisible(true);
208 assertTrue(mContentViewCore.hasSelection());
209 assertNotNull(mContentViewCore.getSelectActionHandler());
210 assertTrue(mContentViewCore.getSelectActionHandler().isSelectionEditable());
211 assertFalse(mContentViewCore.getSelectActionHandler().isSelectionPassword());
215 @Feature({"TextInput"})
216 public void testActionBarConfiguredCorrectlyForPassword() throws Throwable {
217 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
218 assertWaitForSelectActionBarVisible(true);
219 assertTrue(mContentViewCore.hasSelection());
220 assertNotNull(mContentViewCore.getSelectActionHandler());
221 assertTrue(mContentViewCore.getSelectActionHandler().isSelectionEditable());
222 assertTrue(mContentViewCore.getSelectActionHandler().isSelectionPassword());
226 @Feature({"TextInput"})
227 public void testActionBarConfiguredCorrectlyForPlainText() throws Throwable {
228 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
229 assertWaitForSelectActionBarVisible(true);
230 assertTrue(mContentViewCore.hasSelection());
231 assertNotNull(mContentViewCore.getSelectActionHandler());
232 assertFalse(mContentViewCore.getSelectActionHandler().isSelectionEditable());
233 assertFalse(mContentViewCore.getSelectActionHandler().isSelectionPassword());
237 @Feature({"TextInput"})
238 public void testActionBarConfiguredCorrectlyForTextArea() throws Throwable {
239 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
240 assertWaitForSelectActionBarVisible(true);
241 assertTrue(mContentViewCore.hasSelection());
242 assertNotNull(mContentViewCore.getSelectActionHandler());
243 assertTrue(mContentViewCore.getSelectActionHandler().isSelectionEditable());
244 assertFalse(mContentViewCore.getSelectActionHandler().isSelectionPassword());
248 @Feature({"TextInput"})
249 public void testSelectActionBarPlainTextCopy() throws Exception {
250 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
251 assertWaitForSelectActionBarVisible(true);
252 assertTrue(mContentViewCore.hasSelection());
253 assertNotNull(mContentViewCore.getSelectActionHandler());
254 selectActionBarCopy();
255 assertClipboardContents(mContentViewCore.getContext(), "SamplePlainTextOne");
259 @Feature({"TextInput"})
260 public void testSelectActionBarInputCopy() throws Exception {
261 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
262 assertWaitForSelectActionBarVisible(true);
263 assertTrue(mContentViewCore.hasSelection());
264 assertNotNull(mContentViewCore.getSelectActionHandler());
265 selectActionBarCopy();
266 assertClipboardContents(mContentViewCore.getContext(), "SampleInputText");
270 @Feature({"TextInput"})
271 public void testSelectActionBarPasswordCopy() throws Exception {
272 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
273 assertWaitForSelectActionBarVisible(true);
274 assertTrue(mContentViewCore.hasSelection());
275 assertNotNull(mContentViewCore.getSelectActionHandler());
276 selectActionBarCopy();
277 assertClipboardContents(mContentViewCore.getContext(), "SamplePlainTextOne");
278 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
279 assertWaitForSelectActionBarVisible(true);
280 assertTrue(mContentViewCore.hasSelection());
281 assertNotNull(mContentViewCore.getSelectActionHandler());
282 selectActionBarCopy();
283 // Copy option won't be there for Password, hence no change in Clipboard
284 // Validating with previous Clipboard content
285 assertClipboardContents(mContentViewCore.getContext(), "SamplePlainTextOne");
289 @Feature({"TextInput"})
290 public void testSelectActionBarTextAreaCopy() throws Exception {
291 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
292 assertWaitForSelectActionBarVisible(true);
293 assertTrue(mContentViewCore.hasSelection());
294 assertNotNull(mContentViewCore.getSelectActionHandler());
295 selectActionBarCopy();
296 assertClipboardContents(mContentViewCore.getContext(), "SampleTextArea");
300 @Feature({"TextSelection"})
301 public void testSelectActionBarPlainTextCut() throws Exception {
302 copyStringToClipboard("SampleTextToCopy");
303 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
304 assertWaitForSelectActionBarVisible(true);
305 assertTrue(mContentViewCore.hasSelection());
306 assertEquals(mContentViewCore.getSelectedText(), "SamplePlainTextOne");
307 assertNotNull(mContentViewCore.getSelectActionHandler());
308 selectActionBarCut();
309 assertWaitForSelectActionBarVisible(true);
310 assertTrue(mContentViewCore.hasSelection());
311 // Cut option won't be available for plain text.
312 // Hence validating previous Clipboard content.
313 assertClipboardContents(mContentViewCore.getContext(), "SampleTextToCopy");
317 @Feature({"TextInput"})
318 public void testSelectActionBarInputCut() throws Exception {
319 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
320 assertWaitForSelectActionBarVisible(true);
321 assertTrue(mContentViewCore.hasSelection());
322 assertEquals(mContentViewCore.getSelectedText(), "SampleInputText");
323 assertNotNull(mContentViewCore.getSelectActionHandler());
324 selectActionBarCut();
325 assertWaitForSelectActionBarVisible(false);
326 assertFalse(mContentViewCore.hasSelection());
327 assertClipboardContents(mContentViewCore.getContext(), "SampleInputText");
328 assertEquals(mContentViewCore.getSelectedText(), "");
332 @Feature({"TextInput"})
333 public void testSelectActionBarPasswordCut() throws Exception {
334 copyStringToClipboard("SampleTextToCopy");
335 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
336 assertWaitForSelectActionBarVisible(true);
337 assertTrue(mContentViewCore.hasSelection());
338 assertNotNull(mContentViewCore.getSelectActionHandler());
339 selectActionBarCut();
340 assertWaitForSelectActionBarVisible(true);
341 assertTrue(mContentViewCore.hasSelection());
342 // Cut option won't be there for Password, hence no change in Clipboard
343 // Validating with previous Clipboard content
344 assertClipboardContents(mContentViewCore.getContext(), "SampleTextToCopy");
348 @Feature({"TextInput"})
349 public void testSelectActionBarTextAreaCut() throws Exception {
350 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
351 assertWaitForSelectActionBarVisible(true);
352 assertTrue(mContentViewCore.hasSelection());
353 assertEquals(mContentViewCore.getSelectedText(), "SampleTextArea");
354 assertNotNull(mContentViewCore.getSelectActionHandler());
355 selectActionBarCut();
356 assertWaitForSelectActionBarVisible(false);
357 assertFalse(mContentViewCore.hasSelection());
358 assertClipboardContents(mContentViewCore.getContext(), "SampleTextArea");
359 assertEquals(mContentViewCore.getSelectedText(), "");
363 @Feature({"TextSelection"})
364 public void testSelectActionBarPlainTextSelectAll() throws Exception {
365 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
366 assertWaitForSelectActionBarVisible(true);
367 assertTrue(mContentViewCore.hasSelection());
368 assertNotNull(mContentViewCore.getSelectActionHandler());
369 selectActionBarSelectAll();
370 assertTrue(mContentViewCore.hasSelection());
371 assertWaitForSelectActionBarVisible(true);
375 @Feature({"TextInput"})
376 public void testSelectActionBarInputSelectAll() throws Exception {
377 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
378 assertWaitForSelectActionBarVisible(true);
379 assertTrue(mContentViewCore.hasSelection());
380 assertNotNull(mContentViewCore.getSelectActionHandler());
381 selectActionBarSelectAll();
382 assertTrue(mContentViewCore.hasSelection());
383 assertWaitForSelectActionBarVisible(true);
384 assertEquals(mContentViewCore.getSelectedText(), "SampleInputText");
388 @Feature({"TextInput"})
389 public void testSelectActionBarPasswordSelectAll() throws Exception {
390 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
391 assertWaitForSelectActionBarVisible(true);
392 assertTrue(mContentViewCore.hasSelection());
393 assertNotNull(mContentViewCore.getSelectActionHandler());
394 selectActionBarSelectAll();
395 assertTrue(mContentViewCore.hasSelection());
396 assertWaitForSelectActionBarVisible(true);
400 @Feature({"TextInput"})
401 public void testSelectActionBarTextAreaSelectAll() throws Exception {
402 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
403 assertWaitForSelectActionBarVisible(true);
404 assertTrue(mContentViewCore.hasSelection());
405 assertNotNull(mContentViewCore.getSelectActionHandler());
406 selectActionBarSelectAll();
407 assertTrue(mContentViewCore.hasSelection());
408 assertWaitForSelectActionBarVisible(true);
409 assertEquals(mContentViewCore.getSelectedText(), "SampleTextArea");
413 @Feature({"TextSelection"})
414 public void testSelectActionBarPlainTextPaste() throws Exception {
415 copyStringToClipboard("SampleTextToCopy");
416 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
417 assertWaitForSelectActionBarVisible(true);
418 assertTrue(mContentViewCore.hasSelection());
419 assertNotNull(mContentViewCore.getSelectActionHandler());
420 selectActionBarPaste();
421 DOMUtils.longPressNode(this, mContentViewCore, "plain_text_1");
422 assertWaitForSelectActionBarVisible(true);
423 assertTrue(mContentViewCore.hasSelection());
424 // Paste option won't be available for plain text.
425 // Hence content won't be changed.
426 assertNotSame(mContentViewCore.getSelectedText(), "SampleTextToCopy");
430 @Feature({"TextInput"})
431 public void testSelectActionBarInputPaste() throws Exception {
432 copyStringToClipboard("SampleTextToCopy");
433 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
434 assertWaitForSelectActionBarVisible(true);
435 assertTrue(mContentViewCore.hasSelection());
436 assertNotNull(mContentViewCore.getSelectActionHandler());
437 selectActionBarPaste();
438 DOMUtils.clickNode(this, mContentViewCore, "plain_text_1");
439 DOMUtils.longPressNode(this, mContentViewCore, "input_text");
440 assertWaitForSelectActionBarVisible(true);
441 assertTrue(mContentViewCore.hasSelection());
442 assertEquals(mContentViewCore.getSelectedText(), "SampleTextToCopy");
446 @Feature({"TextInput"})
447 public void testSelectActionBarPasswordPaste() throws Exception {
448 copyStringToClipboard("SampleTextToCopy");
449 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
450 assertWaitForSelectActionBarVisible(true);
451 assertTrue(mContentViewCore.hasSelection());
452 assertNotNull(mContentViewCore.getSelectActionHandler());
453 selectActionBarPaste();
454 DOMUtils.clickNode(this, mContentViewCore, "plain_text_1");
455 DOMUtils.longPressNode(this, mContentViewCore, "input_password");
456 assertWaitForSelectActionBarVisible(true);
457 assertTrue(mContentViewCore.hasSelection());
458 assertNotSame(mContentViewCore.getSelectedText(), "SampleTextToCopy");
462 @Feature({"TextInput"})
463 public void testSelectActionBarTextAreaPaste() throws Exception {
464 copyStringToClipboard("SampleTextToCopy");
465 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
466 assertWaitForSelectActionBarVisible(true);
467 assertTrue(mContentViewCore.hasSelection());
468 assertNotNull(mContentViewCore.getSelectActionHandler());
469 selectActionBarPaste();
470 DOMUtils.clickNode(this, mContentViewCore, "plain_text_1");
471 DOMUtils.longPressNode(this, mContentViewCore, "textarea");
472 assertWaitForSelectActionBarVisible(true);
473 assertTrue(mContentViewCore.hasSelection());
474 assertEquals(mContentViewCore.getSelectedText(), "SampleTextToCopy");
477 private void selectActionBarPaste() {
478 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
481 mContentViewCore.getSelectActionHandler().paste();
486 private void selectActionBarSelectAll() {
487 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
490 mContentViewCore.getSelectActionHandler().selectAll();
495 private void selectActionBarCut() {
496 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
499 mContentViewCore.getSelectActionHandler().cut();
504 private void selectActionBarCopy() {
505 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
508 mContentViewCore.getSelectActionHandler().copy();
513 private void assertClipboardContents(final Context context, final String expectedContents)
514 throws InterruptedException {
515 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
517 public boolean isSatisfied() {
518 return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
520 public Boolean call() throws Exception {
521 ClipboardManager clipboardManager =
522 (ClipboardManager) context.getSystemService(
523 Context.CLIPBOARD_SERVICE);
524 ClipData clip = clipboardManager.getPrimaryClip();
525 return clip != null && clip.getItemCount() == 1
526 && TextUtils.equals(clip.getItemAt(0).getText(), expectedContents);
533 private void assertWaitForSelectActionBarVisible(
534 final boolean visible) throws InterruptedException {
535 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
537 public boolean isSatisfied() {
538 return visible == mContentViewCore.isSelectActionBarShowing();
543 private void setVisibileOnUiThread(final boolean show) {
544 final ContentViewCore contentViewCore = mContentViewCore;
545 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
549 contentViewCore.onShow();
551 contentViewCore.onHide();
557 private void setAttachedOnUiThread(final boolean attached) {
558 final ContentViewCore contentViewCore = mContentViewCore;
559 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
563 contentViewCore.onAttachedToWindow();
565 contentViewCore.onDetachedFromWindow();
571 private void requestFocusOnUiThread(final boolean gainFocus) {
572 final ContentViewCore contentViewCore = mContentViewCore;
573 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
576 contentViewCore.onFocusChanged(gainFocus);
581 private void copyStringToClipboard(String string) {
582 ClipboardManager clipboardManager =
583 (ClipboardManager) getActivity().getSystemService(
584 Context.CLIPBOARD_SERVICE);
585 ClipData clip = ClipData.newPlainText("test", string);
586 clipboardManager.setPrimaryClip(clip);
589 private void assertWaitForPastePopupStatus(final boolean show) throws InterruptedException {
590 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
592 public boolean isSatisfied() {
593 return show == mContentViewCore.getPastePopupForTest().isShowing();