ImeTest.java revision 116680a4aac90f2aa7413d9095a592090648e557
1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Copyright 2013 The Chromium Authors. All rights reserved. 2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Use of this source code is governed by a BSD-style license that can be 3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// found in the LICENSE file. 4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovpackage org.chromium.content.browser.input; 6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.app.Activity; 8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.content.ClipData; 9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.content.ClipboardManager; 10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.content.Context; 11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.test.suitebuilder.annotation.MediumTest; 12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.test.suitebuilder.annotation.SmallTest; 13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.text.Editable; 14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.text.Selection; 15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.text.TextUtils; 16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.view.KeyEvent; 17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.view.View; 18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport android.view.inputmethod.EditorInfo; 19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.base.ThreadUtils; 21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.base.test.util.Feature; 22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.base.test.util.UrlUtils; 23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.ContentViewCore; 24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.test.util.Criteria; 25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.test.util.CriteriaHelper; 26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.test.util.DOMUtils; 27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.test.util.TestCallbackHelperContainer; 28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; 29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport org.chromium.content_shell_apk.ContentShellTestBase; 30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport java.util.ArrayList; 32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovimport java.util.concurrent.Callable; 33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/** 35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Integration tests for text input using cases based on fixed regressions. 36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovpublic class ImeTest extends ContentShellTestBase { 38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( 40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "<html><head><meta name=\"viewport\"" + 41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "content=\"width=device-width, initial-scale=2.0, maximum-scale=2.0\" /></head>" + 42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "<body><form action=\"about:blank\">" + 43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "<input id=\"input_text\" type=\"text\" /><br/>" + 44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "<input id=\"input_radio\" type=\"radio\" style=\"width:50px;height:50px\" />" + 45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "<br/><textarea id=\"textarea\" rows=\"4\" cols=\"20\"></textarea>" + 46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "</form></body></html>"); 47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private TestAdapterInputConnection mConnection; 49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private ImeAdapter mImeAdapter; 50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private ContentViewCore mContentViewCore; 52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private TestCallbackHelperContainer mCallbackContainer; 53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private TestInputMethodManagerWrapper mInputMethodManagerWrapper; 54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Override 56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void setUp() throws Exception { 57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov super.setUp(); 58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov launchContentShellWithUrl(DATA_URL); 60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading()); 61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mContentViewCore = getContentViewCore(); 62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentViewCore); 64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov getImeAdapter().setInputMethodManagerWrapper(mInputMethodManagerWrapper); 65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(0, mInputMethodManagerWrapper.getShowSoftInputCounter()); 66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mContentViewCore.setAdapterInputConnectionFactory( 67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov new TestAdapterInputConnectionFactory()); 68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mCallbackContainer = new TestCallbackHelperContainer(mContentViewCore); 70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // TODO(aurimas) remove this wait once crbug.com/179511 is fixed. 71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForPageScaleFactorMatch(2); 72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertTrue(DOMUtils.waitForNonZeroNodeBounds( 73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mContentViewCore, "input_text")); 74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DOMUtils.clickNode(this, mContentViewCore, "input_text"); 75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(true); 76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); 78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov mImeAdapter = getImeAdapter(); 79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); 81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); 82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); 83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); 84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @MediumTest 87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput", "Main"}) 88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testKeyboardDismissedAfterClickingGo() throws Throwable { 89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setComposingText(mConnection, "hello", 1); 90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, 0, 5); 91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov performGo(getAdapterInputConnection(), mCallbackContainer); 93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "", 0, 0, -1, -1); 95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(false); 96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput", "Main"}) 100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @RerunWithUpdatedContainerView 101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testGetTextUpdatesAfterEnteringText() throws Throwable { 102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setComposingText(mConnection, "h", 1); 103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "h", 1, 1, 0, 1); 104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); 105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setComposingText(mConnection, "he", 1); 107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "he", 2, 2, 0, 2); 108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); 109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setComposingText(mConnection, "hel", 1); 111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "hel", 3, 3, 0, 3); 112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); 113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov commitText(mConnection, "hel", 1); 115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 4, "hel", 3, 3, -1, -1); 116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); 117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput"}) 121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @RerunWithUpdatedContainerView 122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testImeCopy() throws Exception { 123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov commitText(mConnection, "hello", 1); 124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, -1, -1); 125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setSelection(mConnection, 2, 5); 127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "hello", 2, 5, -1, -1); 128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov copy(mImeAdapter); 130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertClipboardContents(getActivity(), "llo"); 131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput"}) 135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testEnterTextAndRefocus() throws Exception { 136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov commitText(mConnection, "hello", 1); 137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, -1, -1); 138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DOMUtils.clickNode(this, mContentViewCore, "input_radio"); 140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(false); 141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DOMUtils.clickNode(this, mContentViewCore, "input_text"); 143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(true); 144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); 145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); 146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput"}) 150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testKeyboardNotDismissedAfterCopySelection() throws Exception { 151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov commitText(mConnection, "Sample Text", 1); 152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, 153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov "Sample Text", 11, 11, -1, -1); 154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DOMUtils.clickNode(this, mContentViewCore, "input_text"); 155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(true); 156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DOMUtils.longPressNode(this, mContentViewCore, "input_text"); 157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov selectAll(mImeAdapter); 158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov copy(mImeAdapter); 159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertWaitForKeyboardStatus(true); 160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(11, Selection.getSelectionEnd(mContentViewCore.getEditableForTest())); 161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertEquals(11, Selection.getSelectionEnd(mContentViewCore.getEditableForTest())); 162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput"}) 166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testImeCut() throws Exception { 167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov commitText(mConnection, "snarful", 1); 168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "snarful", 7, 7, -1, -1); 169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setSelection(mConnection, 1, 5); 171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "snarful", 1, 5, -1, -1); 172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cut(mImeAdapter); 174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "sul", 1, 1, -1, -1); 175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov assertClipboardContents(getActivity(), "narf"); 177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @SmallTest 180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Feature({"TextInput"}) 181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void testImePaste() throws Exception { 182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ThreadUtils.runOnUiThreadBlocking(new Runnable() { 183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov @Override 184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public void run() { 185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ClipboardManager clipboardManager = 186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov (ClipboardManager) getActivity().getSystemService( 187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Context.CLIPBOARD_SERVICE); 188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov clipboardManager.setPrimaryClip(ClipData.newPlainText("blarg", "blarg")); 189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov }); 191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov paste(mImeAdapter); 193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "blarg", 5, 5, -1, -1); 194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov setSelection(mConnection, 3, 5); 196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "blarg", 3, 5, -1, -1); 197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov paste(mImeAdapter); 199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Paste is a two step process when there is a non-zero selection. 200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "bla", 3, 3, -1, -1); 201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 4, "blablarg", 8, 8, -1, -1); 202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 203ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov paste(mImeAdapter); 204 waitAndVerifyEditableCallback( 205 mConnection.mImeUpdateQueue, 5, "blablargblarg", 13, 13, -1, -1); 206 } 207 208 @SmallTest 209 @Feature({"TextInput"}) 210 public void testImeSelectAndUnSelectAll() throws Exception { 211 commitText(mConnection, "hello", 1); 212 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, -1, -1); 213 214 selectAll(mImeAdapter); 215 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "hello", 0, 5, -1, -1); 216 217 unselect(mImeAdapter); 218 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "", 0, 0, -1, -1); 219 220 assertWaitForKeyboardStatus(false); 221 } 222 223 @SmallTest 224 @Feature({"TextInput", "Main"}) 225 public void testShowImeIfNeeded() throws Throwable { 226 // showImeIfNeeded() is now implicitly called by the updated focus 227 // heuristic so no need to call explicitly. http://crbug.com/371927 228 DOMUtils.focusNode(mContentViewCore, "input_radio"); 229 assertWaitForKeyboardStatus(false); 230 231 DOMUtils.focusNode(mContentViewCore, "input_text"); 232 assertWaitForKeyboardStatus(true); 233 } 234 235 @SmallTest 236 @Feature({"TextInput", "Main"}) 237 public void testFinishComposingText() throws Throwable { 238 DOMUtils.focusNode(mContentViewCore, "input_radio"); 239 assertWaitForKeyboardStatus(false); 240 DOMUtils.focusNode(mContentViewCore, "textarea"); 241 assertWaitForKeyboardStatus(true); 242 243 mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); 244 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); 245 246 commitText(mConnection, "hllo", 1); 247 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hllo", 4, 4, -1, -1); 248 249 commitText(mConnection, " ", 1); 250 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "hllo ", 5, 5, -1, -1); 251 252 setSelection(mConnection, 1, 1); 253 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "hllo ", 1, 1, -1, -1); 254 255 setComposingRegion(mConnection, 0, 4); 256 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 4, "hllo ", 1, 1, 0, 4); 257 258 finishComposingText(mConnection); 259 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 5, "hllo ", 1, 1, -1, -1); 260 261 commitText(mConnection, "\n", 1); 262 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 6, "h\nllo ", 2, 2, -1, -1); 263 } 264 265 @SmallTest 266 @Feature({"TextInput", "Main"}) 267 public void testEnterKeyEventWhileComposingText() throws Throwable { 268 DOMUtils.focusNode(mContentViewCore, "input_radio"); 269 assertWaitForKeyboardStatus(false); 270 DOMUtils.focusNode(mContentViewCore, "textarea"); 271 assertWaitForKeyboardStatus(true); 272 273 mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); 274 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); 275 276 setComposingText(mConnection, "hello", 1); 277 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, 0, 5); 278 279 getInstrumentation().runOnMainSync(new Runnable() { 280 @Override 281 public void run() { 282 mConnection.sendKeyEvent( 283 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); 284 mConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)); 285 } 286 }); 287 288 // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. 289 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "hello", 5, 5, -1, -1); 290 // The second new line is not a user visible/editable one, it is a side-effect of Blink 291 // using <br> internally. 292 waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "hello\n\n", 6, 6, -1, -1); 293 } 294 295 private void performGo(final AdapterInputConnection inputConnection, 296 TestCallbackHelperContainer testCallbackHelperContainer) throws Throwable { 297 handleBlockingCallbackAction( 298 testCallbackHelperContainer.getOnPageFinishedHelper(), 299 new Runnable() { 300 @Override 301 public void run() { 302 inputConnection.performEditorAction(EditorInfo.IME_ACTION_GO); 303 } 304 }); 305 } 306 307 private void assertWaitForKeyboardStatus(final boolean show) throws InterruptedException { 308 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { 309 @Override 310 public boolean isSatisfied() { 311 return show == getImeAdapter().mIsShowWithoutHideOutstanding && 312 (!show || getAdapterInputConnection() != null); 313 } 314 })); 315 } 316 317 private void waitAndVerifyEditableCallback(final ArrayList<TestImeState> states, 318 final int index, String text, int selectionStart, int selectionEnd, 319 int compositionStart, int compositionEnd) throws InterruptedException { 320 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { 321 @Override 322 public boolean isSatisfied() { 323 return states.size() > index; 324 } 325 })); 326 states.get(index).assertEqualState( 327 text, selectionStart, selectionEnd, compositionStart, compositionEnd); 328 } 329 330 private void assertClipboardContents(final Activity activity, final String expectedContents) 331 throws InterruptedException { 332 assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { 333 @Override 334 public boolean isSatisfied() { 335 return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { 336 @Override 337 public Boolean call() throws Exception { 338 ClipboardManager clipboardManager = 339 (ClipboardManager) activity.getSystemService( 340 Context.CLIPBOARD_SERVICE); 341 ClipData clip = clipboardManager.getPrimaryClip(); 342 return clip != null && clip.getItemCount() == 1 343 && TextUtils.equals(clip.getItemAt(0).getText(), expectedContents); 344 } 345 }); 346 } 347 })); 348 } 349 350 private ImeAdapter getImeAdapter() { 351 return mContentViewCore.getImeAdapterForTest(); 352 } 353 354 private AdapterInputConnection getAdapterInputConnection() { 355 return mContentViewCore.getInputConnectionForTest(); 356 } 357 358 private void copy(final ImeAdapter adapter) { 359 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 360 @Override 361 public void run() { 362 adapter.copy(); 363 } 364 }); 365 } 366 367 private void cut(final ImeAdapter adapter) { 368 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 369 @Override 370 public void run() { 371 adapter.cut(); 372 } 373 }); 374 } 375 376 private void paste(final ImeAdapter adapter) { 377 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 378 @Override 379 public void run() { 380 adapter.paste(); 381 } 382 }); 383 } 384 385 private void selectAll(final ImeAdapter adapter) { 386 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 387 @Override 388 public void run() { 389 adapter.selectAll(); 390 } 391 }); 392 } 393 394 private void unselect(final ImeAdapter adapter) { 395 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 396 @Override 397 public void run() { 398 adapter.unselect(); 399 } 400 }); 401 } 402 403 private void commitText(final AdapterInputConnection connection, final CharSequence text, 404 final int newCursorPosition) { 405 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 406 @Override 407 public void run() { 408 connection.commitText(text, newCursorPosition); 409 } 410 }); 411 } 412 413 private void setSelection(final AdapterInputConnection connection, final int start, 414 final int end) { 415 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 416 @Override 417 public void run() { 418 connection.setSelection(start, end); 419 } 420 }); 421 } 422 423 private void setComposingRegion(final AdapterInputConnection connection, final int start, 424 final int end) { 425 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 426 @Override 427 public void run() { 428 connection.setComposingRegion(start, end); 429 } 430 }); 431 } 432 433 private void setComposingText(final AdapterInputConnection connection, final CharSequence text, 434 final int newCursorPosition) { 435 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 436 @Override 437 public void run() { 438 connection.setComposingText(text, newCursorPosition); 439 } 440 }); 441 } 442 443 private void finishComposingText(final AdapterInputConnection connection) { 444 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 445 @Override 446 public void run() { 447 connection.finishComposingText(); 448 } 449 }); 450 } 451 452 private static class TestAdapterInputConnectionFactory extends 453 ImeAdapter.AdapterInputConnectionFactory { 454 @Override 455 public AdapterInputConnection get(View view, ImeAdapter imeAdapter, 456 Editable editable, EditorInfo outAttrs) { 457 return new TestAdapterInputConnection(view, imeAdapter, editable, outAttrs); 458 } 459 } 460 461 private static class TestAdapterInputConnection extends AdapterInputConnection { 462 private final ArrayList<TestImeState> mImeUpdateQueue = new ArrayList<TestImeState>(); 463 464 public TestAdapterInputConnection(View view, ImeAdapter imeAdapter, 465 Editable editable, EditorInfo outAttrs) { 466 super(view, imeAdapter, editable, outAttrs); 467 } 468 469 @Override 470 public void updateState(String text, int selectionStart, int selectionEnd, 471 int compositionStart, int compositionEnd, boolean requiredAck) { 472 mImeUpdateQueue.add(new TestImeState(text, selectionStart, selectionEnd, 473 compositionStart, compositionEnd)); 474 super.updateState(text, selectionStart, selectionEnd, compositionStart, 475 compositionEnd, requiredAck); 476 } 477 } 478 479 private static class TestImeState { 480 private final String mText; 481 private final int mSelectionStart; 482 private final int mSelectionEnd; 483 private final int mCompositionStart; 484 private final int mCompositionEnd; 485 486 public TestImeState(String text, int selectionStart, int selectionEnd, 487 int compositionStart, int compositionEnd) { 488 mText = text; 489 mSelectionStart = selectionStart; 490 mSelectionEnd = selectionEnd; 491 mCompositionStart = compositionStart; 492 mCompositionEnd = compositionEnd; 493 } 494 495 public void assertEqualState(String text, int selectionStart, int selectionEnd, 496 int compositionStart, int compositionEnd) { 497 assertEquals("Text did not match", text, mText); 498 assertEquals("Selection start did not match", selectionStart, mSelectionStart); 499 assertEquals("Selection end did not match", selectionEnd, mSelectionEnd); 500 assertEquals("Composition start did not match", compositionStart, mCompositionStart); 501 assertEquals("Composition end did not match", compositionEnd, mCompositionEnd); 502 } 503 } 504} 505