Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 package org.chromium.content.browser.input; | 5 package org.chromium.content.browser.input; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.content.ClipData; | 8 import android.content.ClipData; |
| 9 import android.content.ClipboardManager; | 9 import android.content.ClipboardManager; |
| 10 import android.content.Context; | 10 import android.content.Context; |
| 11 import android.content.res.Configuration; | 11 import android.content.res.Configuration; |
| 12 import android.os.Handler; | |
| 13 import android.os.Looper; | |
| 12 import android.test.suitebuilder.annotation.MediumTest; | 14 import android.test.suitebuilder.annotation.MediumTest; |
| 13 import android.test.suitebuilder.annotation.SmallTest; | 15 import android.test.suitebuilder.annotation.SmallTest; |
| 14 import android.text.TextUtils; | 16 import android.text.TextUtils; |
| 17 import android.util.Pair; | |
| 15 import android.view.KeyEvent; | 18 import android.view.KeyEvent; |
| 16 import android.view.View; | 19 import android.view.View; |
| 17 import android.view.inputmethod.EditorInfo; | 20 import android.view.inputmethod.EditorInfo; |
| 21 import android.view.inputmethod.InputConnection; | |
| 18 | 22 |
| 23 import org.chromium.base.ObserverList; | |
| 19 import org.chromium.base.ThreadUtils; | 24 import org.chromium.base.ThreadUtils; |
| 20 import org.chromium.base.test.util.Feature; | 25 import org.chromium.base.test.util.Feature; |
| 21 import org.chromium.base.test.util.UrlUtils; | 26 import org.chromium.base.test.util.UrlUtils; |
| 22 import org.chromium.content.browser.ContentViewCore; | 27 import org.chromium.content.browser.ContentViewCore; |
| 23 import org.chromium.content.browser.test.util.Criteria; | 28 import org.chromium.content.browser.test.util.Criteria; |
| 24 import org.chromium.content.browser.test.util.CriteriaHelper; | 29 import org.chromium.content.browser.test.util.CriteriaHelper; |
| 25 import org.chromium.content.browser.test.util.DOMUtils; | 30 import org.chromium.content.browser.test.util.DOMUtils; |
| 26 import org.chromium.content.browser.test.util.TestCallbackHelperContainer; | 31 import org.chromium.content.browser.test.util.TestCallbackHelperContainer; |
| 27 import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; | 32 import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; |
| 28 import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper.Rang e; | |
| 29 import org.chromium.content_public.browser.WebContents; | 33 import org.chromium.content_public.browser.WebContents; |
| 34 import org.chromium.content_public.browser.WebContentsObserver; | |
| 30 import org.chromium.content_shell_apk.ContentShellTestBase; | 35 import org.chromium.content_shell_apk.ContentShellTestBase; |
| 31 import org.chromium.ui.base.ime.TextInputType; | 36 import org.chromium.ui.base.ime.TextInputType; |
| 32 | 37 |
| 33 import java.util.ArrayList; | 38 import java.util.ArrayList; |
| 34 import java.util.Arrays; | 39 import java.util.Arrays; |
| 35 import java.util.List; | 40 import java.util.List; |
| 36 import java.util.concurrent.Callable; | 41 import java.util.concurrent.Callable; |
| 37 import java.util.concurrent.ExecutionException; | 42 import java.util.concurrent.ExecutionException; |
| 38 import java.util.concurrent.TimeoutException; | 43 import java.util.concurrent.TimeoutException; |
| 39 | 44 |
| 40 /** | 45 /** |
| 41 * Integration tests for text input using cases based on fixed regressions. | 46 * Integration tests for text input using cases based on fixed regressions. |
| 42 */ | 47 */ |
| 43 public class ImeTest extends ContentShellTestBase { | 48 public class ImeTest extends ContentShellTestBase { |
| 44 private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( | 49 private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( |
| 45 "<html><head><meta name=\"viewport\"" | 50 "<html><head><meta name=\"viewport\"" |
| 46 + "content=\"width=device-width\" /></head>" | 51 + "content=\"width=device-width\" /></head>" |
| 47 + "<body><form action=\"about:blank\">" | 52 + "<body><form action=\"about:blank\">" |
| 48 + "<input id=\"input_text\" type=\"text\" /><br/></form><form>" | 53 + "<input id=\"input_text\" type=\"text\" /><br/></form><form>" |
| 49 + "<br/><input id=\"input_radio\" type=\"radio\" style=\"width:50px; height:50px\" />" | 54 + "<br/><input id=\"input_radio\" type=\"radio\" style=\"width:50px; height:50px\" />" |
| 50 + "<br/><textarea id=\"textarea\" rows=\"4\" cols=\"20\"></textarea> " | 55 + "<br/><textarea id=\"textarea\" rows=\"4\" cols=\"20\"></textarea> " |
| 51 + "<br/><textarea id=\"textarea2\" rows=\"4\" cols=\"20\" autocomple te=\"off\">" | 56 + "<br/><textarea id=\"textarea2\" rows=\"4\" cols=\"20\" autocomple te=\"off\">" |
| 52 + "</textarea>" | 57 + "</textarea>" |
| 53 + "<br/><input id=\"input_number1\" type=\"number\" /><br/>" | 58 + "<br/><input id=\"input_number1\" type=\"number\" /><br/>" |
| 54 + "<br/><input id=\"input_number2\" type=\"number\" /><br/>" | 59 + "<br/><input id=\"input_number2\" type=\"number\" /><br/>" |
| 55 + "<br/><p><span id=\"plain_text\">This is Plain Text One</span></p> " | 60 + "<br/><p><span id=\"plain_text\">This is Plain Text One</span></p> " |
| 56 + "</form></body></html>"); | 61 + "</form></body></html>"); |
| 57 | 62 |
| 58 private TestAdapterInputConnection mConnection; | 63 private ChromiumBaseInputConnection mConnection; |
| 59 private TestAdapterInputConnectionFactory mConnectionFactory; | 64 private TestInputConnectionFactory mConnectionFactory; |
| 60 private ImeAdapter mImeAdapter; | 65 private ImeAdapter mImeAdapter; |
| 61 | 66 |
| 62 private ContentViewCore mContentViewCore; | 67 private ContentViewCore mContentViewCore; |
| 63 private WebContents mWebContents; | 68 private WebContents mWebContents; |
| 64 private TestCallbackHelperContainer mCallbackContainer; | 69 private TestCallbackHelperContainer mCallbackContainer; |
| 65 private TestInputMethodManagerWrapper mInputMethodManagerWrapper; | 70 private TestInputMethodManagerWrapper mInputMethodManagerWrapper; |
| 66 | 71 |
| 67 @Override | 72 @Override |
| 68 public void setUp() throws Exception { | 73 public void setUp() throws Exception { |
| 69 super.setUp(); | 74 super.setUp(); |
| 70 | 75 |
| 71 launchContentShellWithUrl(DATA_URL); | 76 launchContentShellWithUrl(DATA_URL); |
| 72 waitForActiveShellToBeDoneLoading(); | 77 waitForActiveShellToBeDoneLoading(); |
| 73 mContentViewCore = getContentViewCore(); | 78 mContentViewCore = getContentViewCore(); |
| 74 mWebContents = getWebContents(); | 79 mWebContents = getWebContents(); |
| 75 | 80 |
| 76 mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentV iewCore); | 81 mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentV iewCore); |
| 77 getImeAdapter().setInputMethodManagerWrapperForTest(mInputMethodManagerW rapper); | 82 getImeAdapter().setInputMethodManagerWrapperForTest(mInputMethodManagerW rapper); |
| 78 assertEquals(0, mInputMethodManagerWrapper.getShowSoftInputCounter()); | 83 assertEquals(0, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
| 79 mConnectionFactory = new TestAdapterInputConnectionFactory(); | 84 mConnectionFactory = |
| 85 new TestInputConnectionFactory(getImeAdapter().getInputConnectio nFactoryForTest()); | |
| 80 getImeAdapter().setInputConnectionFactory(mConnectionFactory); | 86 getImeAdapter().setInputConnectionFactory(mConnectionFactory); |
| 81 | 87 |
| 82 mCallbackContainer = new TestCallbackHelperContainer(mContentViewCore); | 88 mCallbackContainer = new TestCallbackHelperContainer(mContentViewCore); |
| 83 // TODO(aurimas) remove this wait once crbug.com/179511 is fixed. | 89 // TODO(aurimas) remove this wait once crbug.com/179511 is fixed. |
| 84 assertWaitForPageScaleFactorMatch(1); | 90 assertWaitForPageScaleFactorMatch(1); |
| 85 DOMUtils.waitForNonZeroNodeBounds(mWebContents, "input_text"); | 91 DOMUtils.waitForNonZeroNodeBounds(mWebContents, "input_text"); |
| 86 DOMUtils.clickNode(this, mContentViewCore, "input_text"); | 92 DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
| 87 assertWaitForKeyboardStatus(true); | 93 assertWaitForKeyboardStatus(true); |
| 88 | 94 |
| 89 mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); | 95 mConnection = getInputConnection(); |
| 90 mImeAdapter = getImeAdapter(); | 96 mImeAdapter = getImeAdapter(); |
| 91 | 97 |
| 92 waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); | 98 if (usingReplicaInputConnection()) { |
| 99 // This is not needed if onCreateInputConnection() can return correc t selection range. | |
| 100 waitAndVerifyUpdateSelection(0, 0, 0, -1, -1); | |
| 101 } | |
| 93 waitForKeyboardStates(1, 0, 1, new Integer[] {TextInputType.TEXT}); | 102 waitForKeyboardStates(1, 0, 1, new Integer[] {TextInputType.TEXT}); |
| 94 assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelSta rt); | 103 assertEquals(0, mConnectionFactory.getOutAttrs().initialSelStart); |
| 95 assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd ); | 104 assertEquals(0, mConnectionFactory.getOutAttrs().initialSelEnd); |
| 96 | 105 |
| 97 resetAllStates(); | 106 resetAllStates(); |
| 98 } | 107 } |
| 99 | 108 |
| 100 private void assertNoFurtherStateUpdate(final int index) throws InterruptedE xception { | |
| 101 final List<TestImeState> states = mConnectionFactory.getImeStateList(); | |
| 102 try { | |
| 103 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | |
| 104 @Override | |
| 105 public boolean isSatisfied() { | |
| 106 return states.size() > index; | |
| 107 } | |
| 108 }); | |
| 109 fail("Unexpected updates pending"); | |
| 110 } catch (AssertionError e) { | |
| 111 // TODO(tedchoc): This is horrible and should never timeout to deter mine success. | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 @MediumTest | |
| 116 @Feature({"TextInput", "Main"}) | |
| 117 public void testSetUpGeneratesNoFurtherStateUpdate() throws Throwable { | |
| 118 assertNoFurtherStateUpdate(0); | |
| 119 waitForKeyboardStates(0, 0, 0, new Integer[] {}); | |
| 120 } | |
| 121 | |
| 122 @MediumTest | 109 @MediumTest |
| 123 @Feature({"TextInput", "Main"}) | 110 @Feature({"TextInput", "Main"}) |
| 124 public void testKeyboardDismissedAfterClickingGo() throws Throwable { | 111 public void testKeyboardDismissedAfterClickingGo() throws Throwable { |
| 125 setComposingText("hello", 1); | 112 setComposingText("hello", 1); |
| 126 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5); | 113 waitAndVerifyUpdateSelection(0, 5, 5, 0, 5); |
| 127 | 114 |
| 128 performGo(mCallbackContainer); | 115 performGo(mCallbackContainer); |
| 129 | 116 |
| 130 assertWaitForKeyboardStatus(false); | 117 assertWaitForKeyboardStatus(false); |
| 131 } | 118 } |
| 119 | |
| 120 @MediumTest | |
| 121 @Feature({"TextInput", "Main"}) | |
| 122 public void testDoesNotHang_getTextAfterKeyboardHides() throws Throwable { | |
| 123 setComposingText("hello", 1); | |
| 124 waitAndVerifyUpdateSelection(0, 5, 5, 0, 5); | |
| 125 | |
| 126 performGo(mCallbackContainer); | |
| 127 | |
| 128 // This may time out if we do not get the information on time. | |
| 129 for (int i = 0; i < 100; ++i) { | |
| 130 getTextBeforeCursor(10, 0); | |
| 131 } | |
| 132 | |
| 133 assertWaitForKeyboardStatus(false); | |
| 134 } | |
| 135 | |
| 136 @MediumTest | |
| 137 @Feature({"TextInput", "Main"}) | |
| 138 public void testDoesNotHang_rendererCrashes() throws Throwable { | |
|
no sievers
2016/02/25 01:41:44
per chat: can you remove this one and the test abo
Changwan Ryu
2016/02/25 01:56:12
Removed this test as it does not test anything.
I
| |
| 139 setComposingText("hello", 1); | |
| 140 waitAndVerifyUpdateSelection(0, 5, 5, 0, 5); | |
| 141 | |
| 142 ThreadUtils.runOnUiThread(new Runnable() { | |
| 143 @Override | |
| 144 public void run() { | |
| 145 ObserverList.RewindableIterator<WebContentsObserver> observers = | |
| 146 getWebContents().getObserversForTesting(); | |
| 147 while (observers.hasNext()) { | |
| 148 observers.next().renderProcessGone(false); | |
| 149 } | |
| 150 } | |
| 151 }); | |
| 152 | |
| 153 // This may time out if we do not get the information on time. | |
| 154 for (int i = 0; i < 100; ++i) { | |
| 155 getTextBeforeCursor(10, 0); | |
| 156 } | |
| 157 | |
| 158 assertWaitForKeyboardStatus(false); | |
| 159 } | |
| 132 | 160 |
| 133 @SmallTest | 161 @SmallTest |
| 134 @Feature({"TextInput", "Main"}) | 162 @Feature({"TextInput", "Main"}) |
| 135 public void testCommitWhileComposingText() throws Throwable { | 163 public void testCommitWhileComposingText() throws Throwable { |
| 136 setComposingText("h", 1); | 164 setComposingText("h", 1); |
| 137 waitAndVerifyStates(0, "h", 1, 1, 0, 1); | 165 waitAndVerifyUpdateSelection(0, 1, 1, 0, 1); |
| 138 | 166 |
| 139 setComposingText("he", 1); | 167 setComposingText("he", 1); |
| 140 waitAndVerifyStates(1, "he", 2, 2, 0, 2); | 168 waitAndVerifyUpdateSelection(1, 2, 2, 0, 2); |
| 141 | 169 |
| 142 setComposingText("hel", 1); | 170 setComposingText("hel", 1); |
| 143 waitAndVerifyStates(2, "hel", 3, 3, 0, 3); | 171 waitAndVerifyUpdateSelection(2, 3, 3, 0, 3); |
| 144 | 172 |
| 145 commitText("hel", 1); | 173 commitText("hel", 1); |
| 146 waitAndVerifyStates(3, "hel", 3, 3, -1, -1); | 174 waitAndVerifyUpdateSelection(3, 3, 3, -1, -1); |
| 175 | |
| 176 setComposingText("lo", 1); | |
| 177 waitAndVerifyUpdateSelection(4, 5, 5, 3, 5); | |
| 178 | |
| 179 commitText("", 1); | |
| 180 waitAndVerifyUpdateSelection(5, 3, 3, -1, -1); | |
| 181 | |
| 182 assertTextsAroundCursor("hel", "", ""); | |
| 147 } | 183 } |
| 148 | 184 |
| 149 @SmallTest | 185 @SmallTest |
| 150 @Feature({"TextInput", "Main"}) | 186 @Feature({"TextInput", "Main"}) |
| 151 public void testCommitEnterKeyWhileComposingText() throws Throwable { | 187 public void testCommitEnterKeyWhileComposingText() throws Throwable { |
| 152 focusElementAndWaitForStateUpdate("textarea"); | 188 focusElementAndWaitForStateUpdate("textarea"); |
| 153 | 189 |
| 154 setComposingText("hello", 1); | 190 setComposingText("hello", 1); |
| 155 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5); | 191 waitAndVerifyUpdateSelection(0, 5, 5, 0, 5); |
| 156 | 192 |
| 157 // Cancel the current composition and replace it with enter. | 193 // Cancel the current composition and replace it with enter. |
| 158 commitText("\n", 1); | 194 commitText("\n", 1); |
| 195 waitAndVerifyUpdateSelection(1, 1, 1, -1, -1); | |
| 159 // The second new line is not a user visible/editable one, it is a side- effect of Blink | 196 // The second new line is not a user visible/editable one, it is a side- effect of Blink |
| 160 // using <br> internally. This only happens when \n is at the end. | 197 // using <br> internally. This only happens when \n is at the end. |
| 161 waitAndVerifyStatesAndCalls(1, "\n\n", 1, 1, -1, -1); | 198 assertTextsAroundCursor("\n", "", "\n"); |
| 162 | 199 |
| 163 commitText("world", 1); | 200 commitText("world", 1); |
| 164 waitAndVerifyStatesAndCalls(2, "\nworld", 6, 6, -1, -1); | 201 waitAndVerifyUpdateSelection(2, 6, 6, -1, -1); |
| 202 assertTextsAroundCursor("\nworld", "", ""); | |
| 165 } | 203 } |
| 166 | 204 |
| 167 @SmallTest | 205 @SmallTest |
| 168 @Feature({"TextInput"}) | 206 @Feature({"TextInput"}) |
| 169 public void testImeCopy() throws Exception { | 207 public void testImeCopy() throws Exception { |
| 170 commitText("hello", 1); | 208 commitText("hello", 1); |
| 171 waitAndVerifyStates(0, "hello", 5, 5, -1, -1); | 209 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 172 | 210 |
| 173 setSelection(2, 5); | 211 setSelection(2, 5); |
| 174 waitAndVerifyStates(1, "hello", 2, 5, -1, -1); | 212 waitAndVerifyUpdateSelection(1, 2, 5, -1, -1); |
| 175 | 213 |
| 176 copy(); | 214 copy(); |
| 177 assertClipboardContents(getActivity(), "llo"); | 215 assertClipboardContents(getActivity(), "llo"); |
| 178 } | 216 } |
| 179 | 217 |
| 180 @SmallTest | 218 @SmallTest |
| 181 @Feature({"TextInput"}) | 219 @Feature({"TextInput"}) |
| 182 public void testEnterTextAndRefocus() throws Exception { | 220 public void testEnterTextAndRefocus() throws Exception { |
| 183 commitText("hello", 1); | 221 commitText("hello", 1); |
| 184 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 222 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 185 | 223 restartInput(); |
| 186 DOMUtils.clickNode(this, mContentViewCore, "input_radio"); | |
| 187 assertWaitForKeyboardStatus(false); | |
| 188 | |
| 189 DOMUtils.clickNode(this, mContentViewCore, "input_text"); | 224 DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
| 190 assertWaitForKeyboardStatus(true); | 225 assertWaitForKeyboardStatus(true); |
| 191 | 226 |
| 192 assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelSta rt); | 227 assertEquals(5, mConnectionFactory.getOutAttrs().initialSelStart); |
| 193 assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd ); | 228 assertEquals(5, mConnectionFactory.getOutAttrs().initialSelEnd); |
| 194 } | 229 } |
| 195 | 230 |
| 196 @SmallTest | 231 @SmallTest |
| 197 @Feature({"TextInput"}) | 232 @Feature({"TextInput"}) |
| 198 public void testShowAndHideSoftInput() throws Exception { | 233 public void testShowAndHideSoftInput() throws Exception { |
| 199 focusElement("input_radio", false); | 234 focusElement("input_radio", false); |
| 200 | 235 |
| 201 // hideSoftKeyboard(), restartInput() | 236 // hideSoftKeyboard(), restartInput() |
| 202 waitForKeyboardStates(0, 1, 1, new Integer[] {}); | 237 waitForKeyboardStates(0, 1, 1, new Integer[] {}); |
| 203 | 238 |
| 204 // When input connection is null, we still need to set flags to prevent InputMethodService | 239 // When input connection is null, we still need to set flags to prevent InputMethodService |
| 205 // from entering fullscreen mode and from opening custom UI. | 240 // from entering fullscreen mode and from opening custom UI. |
| 206 assertNull(mInputMethodManagerWrapper.getInputConnection()); | 241 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 207 assertEquals(EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_ EXTRACT_UI, | 242 @Override |
| 208 mInputMethodManagerWrapper.getEditorInfo().imeOptions); | 243 public boolean isSatisfied() { |
| 244 return getInputConnection() == null; | |
| 245 } | |
| 246 }); | |
| 247 assertTrue( | |
| 248 (mConnectionFactory.getOutAttrs().imeOptions | |
| 249 & (EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FL AG_NO_EXTRACT_UI)) | |
| 250 != 0); | |
| 209 | 251 |
| 210 // showSoftInput(), restartInput() | 252 // showSoftInput(), restartInput() |
| 211 focusElement("input_number1"); | 253 focusElement("input_number1"); |
| 212 waitForKeyboardStates(1, 1, 2, new Integer[] {TextInputType.NUMBER}); | 254 waitForKeyboardStates(1, 1, 2, new Integer[] {TextInputType.NUMBER}); |
| 213 assertNotNull(mInputMethodManagerWrapper.getInputConnection()); | 255 assertNotNull(mInputMethodManagerWrapper.getInputConnection()); |
| 214 | 256 |
| 215 focusElement("input_number2"); | 257 focusElement("input_number2"); |
| 216 // Hide should never be called here. Otherwise we will see a flicker. Re started to | 258 // Hide should never be called here. Otherwise we will see a flicker. Re started to |
| 217 // reset internal states to handle the new input form. | 259 // reset internal states to handle the new input form. |
| 218 waitForKeyboardStates(2, 1, 3, new Integer[] {TextInputType.NUMBER, Text InputType.NUMBER}); | 260 waitForKeyboardStates(2, 1, 3, new Integer[] {TextInputType.NUMBER, Text InputType.NUMBER}); |
| 219 | 261 |
| 220 focusElement("input_text"); | 262 focusElement("input_text"); |
| 221 // showSoftInput() on input_text. restartInput() on input_number1 due to focus change, | 263 // showSoftInput() on input_text. restartInput() on input_number1 due to focus change, |
| 222 // and restartInput() on input_text later. | 264 // and restartInput() on input_text later. |
| 223 // TODO(changwan): reduce unnecessary restart input. | 265 // TODO(changwan): reduce unnecessary restart input. |
| 224 waitForKeyboardStates(3, 1, 5, new Integer[] { | 266 waitForKeyboardStates(3, 1, 5, new Integer[] { |
| 225 TextInputType.NUMBER, TextInputType.NUMBER, TextInputType.NUMBER , | 267 TextInputType.NUMBER, TextInputType.NUMBER, TextInputType.NUMBER , |
| 226 TextInputType.TEXT}); | 268 TextInputType.TEXT}); |
| 227 | 269 |
| 228 focusElement("input_radio", false); | 270 focusElement("input_radio", false); |
| 229 // hideSoftInput(), restartInput() | 271 // hideSoftInput(), restartInput() |
| 230 waitForKeyboardStates(3, 2, 6, new Integer[] { | 272 waitForKeyboardStates(3, 2, 6, new Integer[] { |
| 231 TextInputType.NUMBER, TextInputType.NUMBER, TextInputType.NUMBER , | 273 TextInputType.NUMBER, TextInputType.NUMBER, TextInputType.NUMBER , |
| 232 TextInputType.TEXT}); | 274 TextInputType.TEXT}); |
| 233 } | 275 } |
| 234 | 276 |
| 277 private void assertTextsAroundCursor( | |
| 278 CharSequence before, CharSequence selected, CharSequence after) thro ws Exception { | |
| 279 assertEquals(before, getTextBeforeCursor(100, 0)); | |
| 280 | |
| 281 CharSequence actualSelected = getSelectedText(0); | |
| 282 if (usingReplicaInputConnection() && TextUtils.isEmpty(actualSelected)) { | |
| 283 // ReplicaInputConnection will return null but ChromiumInputConnecti on will return "". | |
| 284 actualSelected = ""; | |
| 285 } | |
| 286 assertEquals(selected, actualSelected); | |
| 287 | |
| 288 if (usingReplicaInputConnection() && after.equals("\n")) { | |
| 289 // When the text ends with \n, we have a second new line that is not user | |
| 290 // visible/editable one, it is a side effect of using <br> internall y. | |
| 291 // Replica model simply deviates from the blink editor in this case. | |
| 292 assertEquals("", getTextAfterCursor(100, 0)); | |
| 293 } else { | |
| 294 assertEquals(after, getTextAfterCursor(100, 0)); | |
| 295 } | |
| 296 } | |
| 297 | |
| 235 private void waitForKeyboardStates(int show, int hide, int restart, Integer[ ] history) | 298 private void waitForKeyboardStates(int show, int hide, int restart, Integer[ ] history) |
| 236 throws InterruptedException { | 299 throws InterruptedException { |
| 237 final String expected = stringifyKeyboardStates(show, hide, restart, his tory); | 300 final String expected = stringifyKeyboardStates(show, hide, restart, his tory); |
| 238 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 301 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 239 @Override | 302 @Override |
| 240 public boolean isSatisfied() { | 303 public boolean isSatisfied() { |
| 241 updateFailureReason( | 304 updateFailureReason( |
| 242 "Expected: {" + expected + "}, Actual: {" + getKeyboardS tates() + "}"); | 305 "Expected: {" + expected + "}, Actual: {" + getKeyboardS tates() + "}"); |
| 243 return expected.equals(getKeyboardStates()); | 306 return expected.equals(getKeyboardStates()); |
| 244 } | 307 } |
| 245 }); | 308 }); |
| 246 } | 309 } |
| 247 | 310 |
| 248 private void resetAllStates() { | 311 private void resetAllStates() { |
| 249 mInputMethodManagerWrapper.resetCounters(); | 312 mInputMethodManagerWrapper.reset(); |
| 250 mConnectionFactory.clearTextInputTypeHistory(); | 313 mConnectionFactory.clearTextInputTypeHistory(); |
| 251 resetUpdateStateList(); | |
| 252 } | 314 } |
| 253 | 315 |
| 254 private String getKeyboardStates() { | 316 private String getKeyboardStates() { |
| 255 try { | 317 int showCount = mInputMethodManagerWrapper.getShowSoftInputCounter(); |
| 256 return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() { | 318 int hideCount = mInputMethodManagerWrapper.getHideSoftInputCounter(); |
| 257 @Override | 319 int restartCount = mInputMethodManagerWrapper.getRestartInputCounter(); |
| 258 public String call() throws Exception { | 320 Integer[] history = mConnectionFactory.getTextInputTypeHistory(); |
| 259 int showCount = mInputMethodManagerWrapper.getShowSoftInputC ounter(); | 321 return stringifyKeyboardStates(showCount, hideCount, restartCount, histo ry); |
| 260 int hideCount = mInputMethodManagerWrapper.getHideSoftInputC ounter(); | |
| 261 int restartCount = mInputMethodManagerWrapper.getRestartInpu tCounter(); | |
| 262 Integer[] history = mConnectionFactory.getTextInputTypeHisto ry(); | |
| 263 return stringifyKeyboardStates(showCount, hideCount, restart Count, history); | |
| 264 } | |
| 265 }); | |
| 266 } catch (ExecutionException e) { | |
| 267 e.printStackTrace(); | |
| 268 return null; | |
| 269 } | |
| 270 } | 322 } |
| 271 | 323 |
| 272 private String stringifyKeyboardStates(int show, int hide, int restart, Inte ger[] history) { | 324 private String stringifyKeyboardStates(int show, int hide, int restart, Inte ger[] history) { |
| 273 return "show count: " + show + ", hide count: " + hide + ", restart coun t: " + restart | 325 return "show count: " + show + ", hide count: " + hide + ", restart coun t: " + restart |
| 274 + ", input type history: " + Arrays.deepToString(history); | 326 + ", input type history: " + Arrays.deepToString(history); |
| 275 } | 327 } |
| 276 | 328 |
| 277 @SmallTest | 329 @SmallTest |
| 278 @Feature({"TextInput"}) | 330 @Feature({"TextInput"}) |
| 279 public void testKeyboardNotDismissedAfterCopySelection() throws Exception { | 331 public void testKeyboardNotDismissedAfterCopySelection() throws Exception { |
| 280 commitText("Sample Text", 1); | 332 commitText("Sample Text", 1); |
| 281 waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1); | 333 waitAndVerifyUpdateSelection(0, 11, 11, -1, -1); |
| 282 | 334 |
| 283 // This will select 'Text' part. | 335 // This will select 'Text' part. |
| 284 DOMUtils.clickNode(this, mContentViewCore, "input_text"); | 336 DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
| 285 assertWaitForKeyboardStatus(true); | 337 assertWaitForKeyboardStatus(true); |
| 286 | 338 |
| 287 // Wait until selection popup shows before long press. Otherwise view | 339 // Wait until selection popup shows before long press. Otherwise view |
| 288 // position may change during sleep in longPressNode implementation. | 340 // position may change during sleep in longPressNode implementation. |
| 289 assertWaitForSelectActionBarStatus(true); | 341 assertWaitForSelectActionBarStatus(true); |
| 290 | 342 |
| 291 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 343 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 292 selectAll(); | 344 selectAll(); |
| 293 copy(); | 345 copy(); |
| 294 assertWaitForKeyboardStatus(true); | 346 assertWaitForKeyboardStatus(true); |
| 295 assertEquals(11, mInputMethodManagerWrapper.getSelection().end()); | 347 assertEquals(11, mInputMethodManagerWrapper.getSelection().end()); |
| 296 } | 348 } |
| 297 | 349 |
| 298 @SmallTest | 350 @SmallTest |
| 299 @Feature({"TextInput"}) | 351 @Feature({"TextInput"}) |
| 300 public void testImeNotDismissedAfterCutSelection() throws Exception { | 352 public void testImeNotDismissedAfterCutSelection() throws Exception { |
| 301 commitText("Sample Text", 1); | 353 commitText("Sample Text", 1); |
| 302 waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, -1, -1); | 354 waitAndVerifyUpdateSelection(0, 11, 11, -1, -1); |
| 303 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 355 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 304 assertWaitForSelectActionBarStatus(true); | 356 assertWaitForSelectActionBarStatus(true); |
| 305 assertWaitForKeyboardStatus(true); | 357 assertWaitForKeyboardStatus(true); |
| 306 cut(); | 358 cut(); |
| 307 assertWaitForKeyboardStatus(true); | 359 assertWaitForKeyboardStatus(true); |
| 308 assertWaitForSelectActionBarStatus(false); | 360 assertWaitForSelectActionBarStatus(false); |
| 309 } | 361 } |
| 310 | 362 |
| 311 @SmallTest | 363 @SmallTest |
| 312 @Feature({"TextInput"}) | 364 @Feature({"TextInput"}) |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 327 commitText("Sample Text", 1); | 379 commitText("Sample Text", 1); |
| 328 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 380 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 329 assertWaitForSelectActionBarStatus(true); | 381 assertWaitForSelectActionBarStatus(true); |
| 330 } | 382 } |
| 331 | 383 |
| 332 @SmallTest | 384 @SmallTest |
| 333 @Feature({"TextInput"}) | 385 @Feature({"TextInput"}) |
| 334 public void testLongPressInputWhileComposingText() throws Exception { | 386 public void testLongPressInputWhileComposingText() throws Exception { |
| 335 assertWaitForSelectActionBarStatus(false); | 387 assertWaitForSelectActionBarStatus(false); |
| 336 setComposingText("Sample Text", 1); | 388 setComposingText("Sample Text", 1); |
| 337 waitAndVerifyStatesAndCalls(0, "Sample Text", 11, 11, 0, 11); | 389 waitAndVerifyUpdateSelection(0, 11, 11, 0, 11); |
| 338 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 390 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 339 | 391 |
| 340 assertWaitForSelectActionBarStatus(true); | 392 assertWaitForSelectActionBarStatus(true); |
| 341 | 393 |
| 342 // Long press will first change selection region, and then trigger IME a pp to show up. | 394 // Long press will first change selection region, and then trigger IME a pp to show up. |
| 343 // See RenderFrameImpl::didChangeSelection() and RenderWidget::didHandle GestureEvent(). | 395 // See RenderFrameImpl::didChangeSelection() and RenderWidget::didHandle GestureEvent(). |
| 344 waitAndVerifyStatesAndCalls(1, "Sample Text", 7, 11, 0, 11); | 396 waitAndVerifyUpdateSelection(1, 7, 11, 0, 11); |
| 345 | 397 |
| 346 // Now IME app wants to finish composing text because an external select ion | 398 // Now IME app wants to finish composing text because an external select ion |
| 347 // change has been detected. At least Google Latin IME and Samsung IME | 399 // change has been detected. At least Google Latin IME and Samsung IME |
| 348 // behave this way. | 400 // behave this way. |
| 349 finishComposingText(); | 401 finishComposingText(); |
| 350 waitAndVerifyStatesAndCalls(2, "Sample Text", 7, 11, -1, -1); | 402 waitAndVerifyUpdateSelection(2, 7, 11, -1, -1); |
| 351 } | 403 } |
| 352 | 404 |
| 353 @SmallTest | 405 @SmallTest |
| 354 @Feature({"TextInput"}) | 406 @Feature({"TextInput"}) |
| 355 public void testImeShownWhenLongPressOnAlreadySelectedText() throws Exceptio n { | 407 public void testImeShownWhenLongPressOnAlreadySelectedText() throws Exceptio n { |
| 356 assertWaitForSelectActionBarStatus(false); | 408 assertWaitForSelectActionBarStatus(false); |
| 357 commitText("Sample Text", 1); | 409 commitText("Sample Text", 1); |
| 358 | 410 |
| 359 int showCount = mInputMethodManagerWrapper.getShowSoftInputCounter(); | 411 int showCount = mInputMethodManagerWrapper.getShowSoftInputCounter(); |
| 360 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 412 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 486 assertWaitForKeyboardStatus(true); | 538 assertWaitForKeyboardStatus(true); |
| 487 assertWaitForSelectActionBarStatus(true); | 539 assertWaitForSelectActionBarStatus(true); |
| 488 DOMUtils.longPressNode(this, mContentViewCore, "textarea"); | 540 DOMUtils.longPressNode(this, mContentViewCore, "textarea"); |
| 489 assertWaitForKeyboardStatus(true); | 541 assertWaitForKeyboardStatus(true); |
| 490 } | 542 } |
| 491 | 543 |
| 492 @SmallTest | 544 @SmallTest |
| 493 @Feature({"TextInput"}) | 545 @Feature({"TextInput"}) |
| 494 public void testImeCut() throws Exception { | 546 public void testImeCut() throws Exception { |
| 495 commitText("snarful", 1); | 547 commitText("snarful", 1); |
| 496 waitAndVerifyStatesAndCalls(0, "snarful", 7, 7, -1, -1); | 548 waitAndVerifyUpdateSelection(0, 7, 7, -1, -1); |
| 497 | 549 |
| 498 setSelection(1, 5); | 550 setSelection(1, 5); |
| 499 waitAndVerifyStatesAndCalls(1, "snarful", 1, 5, -1, -1); | 551 waitAndVerifyUpdateSelection(1, 1, 5, -1, -1); |
| 500 | 552 |
| 501 cut(); | 553 cut(); |
| 502 waitAndVerifyStatesAndCalls(2, "sul", 1, 1, -1, -1); | 554 waitAndVerifyUpdateSelection(2, 1, 1, -1, -1); |
| 503 | 555 assertTextsAroundCursor("s", "", "ul"); |
| 504 assertClipboardContents(getActivity(), "narf"); | 556 assertClipboardContents(getActivity(), "narf"); |
| 505 } | 557 } |
| 506 | 558 |
| 507 @SmallTest | 559 @SmallTest |
| 508 @Feature({"TextInput"}) | 560 @Feature({"TextInput"}) |
| 509 public void testImePaste() throws Exception { | 561 public void testImePaste() throws Exception { |
| 510 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 562 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 511 @Override | 563 @Override |
| 512 public void run() { | 564 public void run() { |
| 513 ClipboardManager clipboardManager = | 565 ClipboardManager clipboardManager = |
| 514 (ClipboardManager) getActivity().getSystemService( | 566 (ClipboardManager) getActivity().getSystemService( |
| 515 Context.CLIPBOARD_SERVICE); | 567 Context.CLIPBOARD_SERVICE); |
| 516 clipboardManager.setPrimaryClip(ClipData.newPlainText("blarg", " blarg")); | 568 clipboardManager.setPrimaryClip(ClipData.newPlainText("blarg", " blarg")); |
| 517 } | 569 } |
| 518 }); | 570 }); |
| 519 | 571 |
| 520 paste(); | 572 paste(); |
| 521 waitAndVerifyStatesAndCalls(0, "blarg", 5, 5, -1, -1); | 573 // Paste is a two step process when there is a non-zero selection. |
| 574 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); | |
| 575 assertTextsAroundCursor("blarg", "", ""); | |
| 522 | 576 |
| 523 setSelection(3, 5); | 577 setSelection(3, 5); |
| 524 waitAndVerifyStatesAndCalls(1, "blarg", 3, 5, -1, -1); | 578 waitAndVerifyUpdateSelection(1, 3, 5, -1, -1); |
| 579 assertTextsAroundCursor("bla", "rg", ""); | |
| 525 | 580 |
| 526 paste(); | 581 paste(); |
| 527 // Paste is a two step process when there is a non-zero selection. | 582 // Paste is a two step process when there is a non-zero selection. |
| 528 waitAndVerifyStates(2, "bla", 3, 3, -1, -1); | 583 waitAndVerifyUpdateSelection(2, 3, 3, -1, -1); |
| 529 waitAndVerifyStatesAndCalls(3, "blablarg", 8, 8, -1, -1); | 584 waitAndVerifyUpdateSelection(3, 8, 8, -1, -1); |
| 585 assertTextsAroundCursor("blablarg", "", ""); | |
| 530 | 586 |
| 531 paste(); | 587 paste(); |
| 532 waitAndVerifyStatesAndCalls(4, "blablargblarg", 13, 13, -1, -1); | 588 waitAndVerifyUpdateSelection(4, 13, 13, -1, -1); |
| 589 assertTextsAroundCursor("blablargblarg", "", ""); | |
| 533 } | 590 } |
| 534 | 591 |
| 535 @SmallTest | 592 @SmallTest |
| 536 @Feature({"TextInput"}) | 593 @Feature({"TextInput"}) |
| 537 public void testImeSelectAndUnSelectAll() throws Exception { | 594 public void testImeSelectAndUnSelectAll() throws Exception { |
| 538 commitText("hello", 1); | 595 commitText("hello", 1); |
| 539 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 596 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 540 | 597 |
| 541 selectAll(); | 598 selectAll(); |
| 542 waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1); | 599 waitAndVerifyUpdateSelection(1, 0, 5, -1, -1); |
| 543 | 600 |
| 544 unselect(); | 601 unselect(); |
| 545 | 602 |
| 546 assertWaitForKeyboardStatus(false); | 603 assertWaitForKeyboardStatus(false); |
| 547 } | 604 } |
| 548 | 605 |
| 549 @SmallTest | 606 @SmallTest |
| 550 @Feature({"TextInput", "Main"}) | 607 @Feature({"TextInput", "Main"}) |
| 551 public void testShowImeIfNeeded() throws Throwable { | 608 public void testShowImeIfNeeded() throws Throwable { |
| 552 // showImeIfNeeded() is now implicitly called by the updated focus | 609 // showImeIfNeeded() is now implicitly called by the updated focus |
| 553 // heuristic so no need to call explicitly. http://crbug.com/371927 | 610 // heuristic so no need to call explicitly. http://crbug.com/371927 |
| 554 DOMUtils.focusNode(mWebContents, "input_radio"); | 611 DOMUtils.focusNode(mWebContents, "input_radio"); |
| 555 assertWaitForKeyboardStatus(false); | 612 assertWaitForKeyboardStatus(false); |
| 556 | 613 |
| 557 DOMUtils.focusNode(mWebContents, "input_text"); | 614 DOMUtils.focusNode(mWebContents, "input_text"); |
| 558 assertWaitForKeyboardStatus(true); | 615 assertWaitForKeyboardStatus(true); |
| 559 } | 616 } |
| 560 | 617 |
| 561 @SmallTest | 618 @SmallTest |
| 562 @Feature({"TextInput", "Main"}) | 619 @Feature({"TextInput", "Main"}) |
| 563 public void testFinishComposingText() throws Throwable { | 620 public void testFinishComposingText() throws Throwable { |
| 564 focusElementAndWaitForStateUpdate("textarea"); | 621 focusElementAndWaitForStateUpdate("textarea"); |
| 565 | 622 |
| 566 commitText("hllo", 1); | 623 commitText("hllo", 1); |
| 567 waitAndVerifyStatesAndCalls(0, "hllo", 4, 4, -1, -1); | 624 waitAndVerifyUpdateSelection(0, 4, 4, -1, -1); |
| 568 | 625 |
| 569 commitText(" ", 1); | 626 commitText(" ", 1); |
| 570 waitAndVerifyStatesAndCalls(1, "hllo ", 5, 5, -1, -1); | 627 waitAndVerifyUpdateSelection(1, 5, 5, -1, -1); |
| 571 | 628 |
| 572 setSelection(1, 1); | 629 setSelection(1, 1); |
| 573 waitAndVerifyStatesAndCalls(2, "hllo ", 1, 1, -1, -1); | 630 waitAndVerifyUpdateSelection(2, 1, 1, -1, -1); |
| 631 assertTextsAroundCursor("h", "", "llo "); | |
| 574 | 632 |
| 575 setComposingRegion(0, 4); | 633 setComposingRegion(0, 4); |
| 576 waitAndVerifyStatesAndCalls(3, "hllo ", 1, 1, 0, 4); | 634 waitAndVerifyUpdateSelection(3, 1, 1, 0, 4); |
| 577 | 635 |
| 578 finishComposingText(); | 636 finishComposingText(); |
| 579 waitAndVerifyStatesAndCalls(4, "hllo ", 1, 1, -1, -1); | 637 waitAndVerifyUpdateSelection(4, 1, 1, -1, -1); |
| 580 | 638 |
| 581 commitText("\n", 1); | 639 commitText("\n", 1); |
| 582 waitAndVerifyStatesAndCalls(5, "h\nllo ", 2, 2, -1, -1); | 640 waitAndVerifyUpdateSelection(5, 2, 2, -1, -1); |
| 641 assertTextsAroundCursor("h\n", "", "llo "); | |
| 583 } | 642 } |
| 584 | 643 |
| 585 /* | 644 // http://crbug.com/445499 |
| 586 @SmallTest | 645 @SmallTest |
| 587 @Feature({"TextInput", "Main"}) | 646 @Feature({"TextInput", "Main"}) |
| 588 http://crbug.com/445499 | |
| 589 */ | |
| 590 public void testDeleteText() throws Throwable { | 647 public void testDeleteText() throws Throwable { |
| 591 focusElement("textarea"); | 648 focusElement("textarea"); |
| 592 | 649 |
| 593 // The calls below are a reflection of what the stock Google Keyboard (A ndr | 650 // The calls below are a reflection of what the stock Google Keyboard (A ndr |
| 594 // when the noted key is touched on screen. | 651 // when the noted key is touched on screen. |
| 595 // H | 652 // H |
| 596 resetUpdateStateList(); | |
| 597 setComposingText("h", 1); | 653 setComposingText("h", 1); |
| 598 assertUpdateStateCall(1000); | 654 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 599 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | |
| 600 | 655 |
| 601 // O | 656 // O |
| 602 resetUpdateStateList(); | |
| 603 setComposingText("ho", 1); | 657 setComposingText("ho", 1); |
| 604 assertUpdateStateCall(1000); | 658 assertEquals("ho", getTextBeforeCursor(9, 0)); |
| 605 assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); | |
| 606 | 659 |
| 607 resetUpdateStateList(); | |
| 608 setComposingText("h", 1); | 660 setComposingText("h", 1); |
| 609 assertUpdateStateCall(1000); | |
| 610 setComposingRegion(0, 1); | 661 setComposingRegion(0, 1); |
| 611 setComposingText("h", 1); | 662 setComposingText("h", 1); |
| 612 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | 663 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 613 | 664 |
| 614 // I | 665 // I |
| 615 setComposingText("hi", 1); | 666 setComposingText("hi", 1); |
| 616 assertEquals("hi", mConnection.getTextBeforeCursor(9, 0)); | 667 assertEquals("hi", getTextBeforeCursor(9, 0)); |
| 617 | 668 |
| 618 // SPACE | 669 // SPACE |
| 619 commitText("hi", 1); | 670 commitText("hi", 1); |
| 620 commitText(" ", 1); | 671 commitText(" ", 1); |
| 621 assertEquals("hi ", mConnection.getTextBeforeCursor(9, 0)); | 672 assertEquals("hi ", getTextBeforeCursor(9, 0)); |
| 622 | 673 |
| 623 // DEL | 674 // DEL |
| 624 deleteSurroundingText(1, 0); | 675 deleteSurroundingText(1, 0); |
| 625 setComposingRegion(0, 2); | 676 setComposingRegion(0, 2); |
| 626 assertEquals("hi", mConnection.getTextBeforeCursor(9, 0)); | 677 assertEquals("hi", getTextBeforeCursor(9, 0)); |
| 627 | 678 |
| 628 setComposingText("h", 1); | 679 setComposingText("h", 1); |
| 629 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | 680 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 630 | 681 |
| 631 commitText("", 1); | 682 commitText("", 1); |
| 632 assertEquals("", mConnection.getTextBeforeCursor(9, 0)); | 683 assertEquals("", getTextBeforeCursor(9, 0)); |
| 633 | 684 |
| 634 // DEL (on empty input) | 685 // DEL (on empty input) |
| 635 deleteSurroundingText(1, 0); // DEL on empty still sends 1,0 | 686 deleteSurroundingText(1, 0); // DEL on empty still sends 1,0 |
| 636 assertEquals("", mConnection.getTextBeforeCursor(9, 0)); | 687 assertEquals("", getTextBeforeCursor(9, 0)); |
| 637 } | 688 } |
| 638 | 689 |
| 639 | 690 |
| 640 /* | |
| 641 @SmallTest | 691 @SmallTest |
| 642 @Feature({"TextInput", "Main"}) | 692 @Feature({"TextInput", "Main"}) |
| 643 */ | |
| 644 public void testSwipingText() throws Throwable { | 693 public void testSwipingText() throws Throwable { |
| 645 focusElement("textarea"); | 694 focusElement("textarea"); |
| 646 | 695 |
| 647 // The calls below are a reflection of what the stock Google Keyboard (A ndroid 4.4) sends | 696 // The calls below are a reflection of what the stock Google Keyboard (A ndroid 4.4) sends |
| 648 // when the word is swiped on the soft keyboard. Exercise care when alt ering to make sure | 697 // when the word is swiped on the soft keyboard. Exercise care when alt ering to make sure |
| 649 // that the test reflects reality. If this test breaks, it's possible t hat code has | 698 // that the test reflects reality. If this test breaks, it's possible t hat code has |
| 650 // changed and different calls need to be made instead. | 699 // changed and different calls need to be made instead. |
| 651 // "three" | 700 // "three" |
| 652 resetUpdateStateList(); | |
| 653 setComposingText("three", 1); | 701 setComposingText("three", 1); |
| 654 assertUpdateStateCall(1000); | 702 assertEquals("three", getTextBeforeCursor(99, 0)); |
| 655 assertEquals("three", mConnection.getTextBeforeCursor(99, 0)); | |
| 656 | 703 |
| 657 // "word" | 704 // "word" |
| 658 commitText("three", 1); | 705 commitText("three", 1); |
| 659 commitText(" ", 1); | 706 commitText(" ", 1); |
| 660 setComposingText("word", 1); | 707 setComposingText("word", 1); |
| 661 resetUpdateStateList(); | 708 assertEquals("three word", getTextBeforeCursor(99, 0)); |
| 662 assertUpdateStateCall(1000); | |
| 663 assertEquals("three word", mConnection.getTextBeforeCursor(99, 0)); | |
| 664 | 709 |
| 665 // "test" | 710 // "test" |
| 666 commitText("word", 1); | 711 commitText("word", 1); |
| 667 commitText(" ", 1); | 712 commitText(" ", 1); |
| 668 resetUpdateStateList(); | |
| 669 setComposingText("test", 1); | 713 setComposingText("test", 1); |
| 670 assertUpdateStateCall(1000); | 714 assertEquals("three word test", getTextBeforeCursor(99, 0)); |
| 671 assertEquals("three word test", mConnection.getTextBeforeCursor(99, 0)); | |
| 672 } | 715 } |
| 673 | 716 |
| 674 @SmallTest | 717 @SmallTest |
| 675 @Feature({"TextInput", "Main"}) | 718 @Feature({"TextInput", "Main"}) |
| 676 public void testDeleteMultiCharacterCodepoint() throws Throwable { | 719 public void testDeleteMultiCharacterCodepoint() throws Throwable { |
| 677 // This smiley is a multi character codepoint. | 720 // This smiley is a multi character codepoint. |
| 678 final String smiley = "\uD83D\uDE0A"; | 721 final String smiley = "\uD83D\uDE0A"; |
| 679 | 722 |
| 680 commitText(smiley, 1); | 723 commitText(smiley, 1); |
| 681 waitAndVerifyStatesAndCalls(0, smiley, 2, 2, -1, -1); | 724 waitAndVerifyUpdateSelection(0, 2, 2, -1, -1); |
| 725 assertTextsAroundCursor(smiley, "", ""); | |
| 682 | 726 |
| 683 // DEL, sent via dispatchKeyEvent like it is in Android WebView or a phy sical keyboard. | 727 // DEL, sent via dispatchKeyEvent like it is in Android WebView or a phy sical keyboard. |
| 684 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); | 728 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); |
| 685 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; | 729 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; |
| 686 | 730 |
| 687 waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); | 731 waitAndVerifyUpdateSelection(1, 0, 0, -1, -1); |
| 688 | 732 |
| 689 // Make sure that we accept further typing after deleting the smiley. | 733 // Make sure that we accept further typing after deleting the smiley. |
| 690 setComposingText("s", 1); | 734 setComposingText("s", 1); |
| 691 waitAndVerifyStatesAndCalls(2, "s", 1, 1, 0, 1); | |
| 692 setComposingText("sm", 1); | 735 setComposingText("sm", 1); |
| 693 waitAndVerifyStatesAndCalls(3, "sm", 2, 2, 0, 2); | 736 waitAndVerifyUpdateSelection(2, 1, 1, 0, 1); |
| 737 waitAndVerifyUpdateSelection(3, 2, 2, 0, 2); | |
| 694 } | 738 } |
| 695 | 739 |
| 696 @SmallTest | 740 @SmallTest |
| 697 @Feature({"TextInput", "Main"}) | 741 @Feature({"TextInput", "Main"}) |
| 698 public void testBackspaceKeycode() throws Throwable { | 742 public void testBackspaceKeycode() throws Throwable { |
| 699 focusElement("textarea"); | 743 focusElement("textarea"); |
| 700 | 744 |
| 701 // H | 745 // H |
| 702 resetUpdateStateList(); | |
| 703 commitText("h", 1); | 746 commitText("h", 1); |
| 704 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | 747 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 705 assertUpdateStateCall(1000); | |
| 706 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | |
| 707 | 748 |
| 708 // O | 749 // O |
| 709 resetUpdateStateList(); | |
| 710 commitText("o", 1); | 750 commitText("o", 1); |
| 711 assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); | 751 assertEquals("ho", getTextBeforeCursor(9, 0)); |
| 712 assertUpdateStateCall(1000); | |
| 713 assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); | |
| 714 | 752 |
| 715 // DEL, sent via dispatchKeyEvent like it is in Android WebView or a phy sical keyboard. | 753 // DEL, sent via dispatchKeyEvent like it is in Android WebView or a phy sical keyboard. |
| 716 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); | 754 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); |
| 717 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; | 755 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; |
| 718 | 756 |
| 719 // DEL | 757 // DEL |
| 720 resetUpdateStateList(); | 758 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 721 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | |
| 722 assertUpdateStateCall(1000); | |
| 723 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | |
| 724 } | 759 } |
| 725 | 760 |
| 726 @SmallTest | 761 @SmallTest |
| 727 @Feature({"TextInput", "Main"}) | 762 @Feature({"TextInput", "Main"}) |
| 728 public void testRepeatBackspaceKeycode() throws Throwable { | 763 public void testRepeatBackspaceKeycode() throws Throwable { |
| 729 focusElement("textarea"); | 764 focusElement("textarea"); |
| 730 | 765 |
| 731 // H | 766 // H |
| 732 resetUpdateStateList(); | |
| 733 commitText("h", 1); | 767 commitText("h", 1); |
| 734 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | 768 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 735 assertUpdateStateCall(1000); | |
| 736 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | |
| 737 | 769 |
| 738 // O | 770 // O |
| 739 resetUpdateStateList(); | |
| 740 commitText("o", 1); | 771 commitText("o", 1); |
| 741 assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); | 772 assertEquals("ho", getTextBeforeCursor(9, 0)); |
| 742 assertUpdateStateCall(1000); | |
| 743 assertEquals("ho", mConnection.getTextBeforeCursor(9, 0)); | |
| 744 | 773 |
| 745 // Multiple keydowns should each delete one character (this is for physi cal keyboard | 774 // Multiple keydowns should each delete one character (this is for physi cal keyboard |
| 746 // key-repeat). | 775 // key-repeat). |
| 747 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); | 776 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); |
| 748 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); | 777 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); |
| 749 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; | 778 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; |
| 750 | 779 |
| 751 // DEL | 780 // DEL |
| 752 resetUpdateStateList(); | 781 assertEquals("", getTextBeforeCursor(9, 0)); |
| 753 assertEquals("", mConnection.getTextBeforeCursor(9, 0)); | |
| 754 assertUpdateStateCall(1000); | |
| 755 assertEquals("", mConnection.getTextBeforeCursor(9, 0)); | |
| 756 } | 782 } |
| 757 | 783 |
| 758 @SmallTest | 784 @SmallTest |
| 759 @Feature({"TextInput", "Main"}) | 785 @Feature({"TextInput", "Main"}) |
| 760 public void testPhysicalKeyboard() throws Throwable { | 786 public void testPhysicalKeyboard() throws Throwable { |
| 761 focusElementAndWaitForStateUpdate("textarea"); | 787 focusElementAndWaitForStateUpdate("textarea"); |
| 762 | 788 |
| 763 // Type 'a' using a physical keyboard. | 789 // Type 'a' using a physical keyboard. |
| 764 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A)) ; | 790 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A)) ; |
| 765 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A)); | 791 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A)); |
| 766 waitAndVerifyStatesAndCalls(0, "a", 1, 1, -1, -1); | 792 waitAndVerifyUpdateSelection(0, 1, 1, -1, -1); |
| 767 | 793 |
| 768 // Type 'enter' key. | 794 // Type 'enter' key. |
| 769 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); | 795 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); |
| 770 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); | 796 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); |
| 771 waitAndVerifyStatesAndCalls(1, "a\n\n", 2, 2, -1, -1); | 797 waitAndVerifyUpdateSelection(1, 2, 2, -1, -1); |
| 798 assertTextsAroundCursor("a\n", "", "\n"); | |
| 772 | 799 |
| 773 // Type 'b'. | 800 // Type 'b'. |
| 774 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)) ; | 801 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)) ; |
| 775 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); | 802 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); |
| 776 waitAndVerifyStatesAndCalls(2, "a\nb", 3, 3, -1, -1); | 803 waitAndVerifyUpdateSelection(2, 3, 3, -1, -1); |
| 804 assertTextsAroundCursor("a\nb", "", ""); | |
| 777 } | 805 } |
| 778 | 806 |
| 779 @SmallTest | 807 @SmallTest |
| 780 @Feature({"TextInput", "Main"}) | 808 @Feature({"TextInput", "Main"}) |
| 781 public void testPhysicalKeyboard_AccentKeyCodes() throws Throwable { | 809 public void testPhysicalKeyboard_AccentKeyCodes() throws Throwable { |
| 782 focusElementAndWaitForStateUpdate("textarea"); | 810 focusElementAndWaitForStateUpdate("textarea"); |
| 783 | 811 |
| 784 // h | 812 // h |
| 785 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H)) ; | 813 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H)) ; |
| 786 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H)); | 814 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H)); |
| 787 assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); | 815 assertEquals("h", getTextBeforeCursor(9, 0)); |
| 788 waitAndVerifyStatesAndCalls(0, "h", 1, 1, -1, -1); | 816 waitAndVerifyUpdateSelection(0, 1, 1, -1, -1); |
| 789 | 817 |
| 790 // ALT-i (circumflex accent key on virtual keyboard) | 818 // ALT-i (circumflex accent key on virtual keyboard) |
| 791 dispatchKeyEvent(new KeyEvent( | 819 dispatchKeyEvent(new KeyEvent( |
| 792 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); | 820 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); |
| 793 assertUpdateStateCall(1000); | 821 assertEquals("hˆ", getTextBeforeCursor(9, 0)); |
| 794 assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0)); | |
| 795 dispatchKeyEvent(new KeyEvent( | 822 dispatchKeyEvent(new KeyEvent( |
| 796 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); | 823 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); |
| 797 assertEquals("hˆ", mConnection.getTextBeforeCursor(9, 0)); | 824 assertEquals("hˆ", getTextBeforeCursor(9, 0)); |
| 798 waitAndVerifyStatesAndCalls(1, "hˆ", 2, 2, 1, 2); | 825 waitAndVerifyUpdateSelection(1, 2, 2, 1, 2); |
| 799 | 826 |
| 800 // o | 827 // o |
| 801 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O)) ; | 828 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O)) ; |
| 802 assertUpdateStateCall(1000); | 829 assertEquals("hô", getTextBeforeCursor(9, 0)); |
| 803 assertEquals("hô", mConnection.getTextBeforeCursor(9, 0)); | |
| 804 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O)); | 830 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O)); |
| 805 assertEquals("hô", mConnection.getTextBeforeCursor(9, 0)); | 831 assertEquals("hô", getTextBeforeCursor(9, 0)); |
| 806 waitAndVerifyStatesAndCalls(2, "hô", 2, 2, -1, -1); | 832 waitAndVerifyUpdateSelection(2, 2, 2, -1, -1); |
| 807 | 833 |
| 808 // ALT-i | 834 // ALT-i |
| 809 dispatchKeyEvent(new KeyEvent( | 835 dispatchKeyEvent(new KeyEvent( |
| 810 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); | 836 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); |
| 811 assertUpdateStateCall(1000); | |
| 812 dispatchKeyEvent(new KeyEvent( | 837 dispatchKeyEvent(new KeyEvent( |
| 813 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); | 838 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); |
| 814 assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0)); | 839 assertEquals("hôˆ", getTextBeforeCursor(9, 0)); |
| 815 waitAndVerifyStatesAndCalls(3, "hôˆ", 3, 3, 2, 3); | 840 waitAndVerifyUpdateSelection(3, 3, 3, 2, 3); |
| 816 | 841 |
| 817 // ALT-i again should have no effect | 842 // ALT-i again should have no effect |
| 818 dispatchKeyEvent(new KeyEvent( | 843 dispatchKeyEvent(new KeyEvent( |
| 819 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); | 844 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); |
| 820 assertUpdateStateCall(1000); | |
| 821 dispatchKeyEvent(new KeyEvent( | 845 dispatchKeyEvent(new KeyEvent( |
| 822 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); | 846 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); |
| 823 assertEquals("hôˆ", mConnection.getTextBeforeCursor(9, 0)); | 847 assertEquals("hôˆ", getTextBeforeCursor(9, 0)); |
| 824 | 848 |
| 825 // b (cannot be accented, should just appear after) | 849 // b (cannot be accented, should just appear after) |
| 826 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)) ; | 850 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)) ; |
| 827 assertUpdateStateCall(1000); | 851 assertEquals("hôˆb", getTextBeforeCursor(9, 0)); |
| 828 assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); | |
| 829 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); | 852 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_B)); |
| 830 assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); | 853 assertEquals("hôˆb", getTextBeforeCursor(9, 0)); |
| 831 // A transitional state due to finishComposingText. | 854 int index = 4; |
| 832 waitAndVerifyStates(4, "hôˆ", 3, 3, -1, -1); | 855 if (usingReplicaInputConnection()) { |
| 833 waitAndVerifyStatesAndCalls(5, "hôˆb", 4, 4, -1, -1); | 856 // A transitional state due to finishComposingText. |
| 857 waitAndVerifyUpdateSelection(index++, 3, 3, -1, -1); | |
| 858 } | |
| 859 waitAndVerifyUpdateSelection(index++, 4, 4, -1, -1); | |
| 834 | 860 |
| 835 // ALT-i | 861 // ALT-i |
| 836 dispatchKeyEvent(new KeyEvent( | 862 dispatchKeyEvent(new KeyEvent( |
| 837 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); | 863 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META _ALT_ON)); |
| 838 assertUpdateStateCall(1000); | |
| 839 dispatchKeyEvent(new KeyEvent( | 864 dispatchKeyEvent(new KeyEvent( |
| 840 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); | 865 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_A LT_ON)); |
| 841 assertEquals("hôˆbˆ", mConnection.getTextBeforeCursor(9, 0)); | 866 assertEquals("hôˆbˆ", getTextBeforeCursor(9, 0)); |
| 842 waitAndVerifyStatesAndCalls(6, "hôˆbˆ", 5, 5, 4, 5); | 867 waitAndVerifyUpdateSelection(index++, 5, 5, 4, 5); |
| 843 | 868 |
| 844 // Backspace | 869 // Backspace |
| 845 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); | 870 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL )); |
| 846 assertUpdateStateCall(1000); | 871 assertEquals("hôˆb", getTextBeforeCursor(9, 0)); |
| 847 assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); | |
| 848 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; | 872 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)) ; |
| 849 assertEquals("hôˆb", mConnection.getTextBeforeCursor(9, 0)); | 873 assertEquals("hôˆb", getTextBeforeCursor(9, 0)); |
| 850 // A transitional state due to finishComposingText in deleteSurroundingT extImpl. | 874 if (usingReplicaInputConnection()) { |
| 851 waitAndVerifyStates(7, "hôˆbˆ", 5, 5, -1, -1); | 875 // A transitional state due to finishComposingText in deleteSurround ingTextImpl. |
| 852 waitAndVerifyStatesAndCalls(8, "hôˆb", 4, 4, -1, -1); | 876 waitAndVerifyUpdateSelection(index++, 5, 5, -1, -1); |
| 877 } | |
| 878 waitAndVerifyUpdateSelection(index++, 4, 4, -1, -1); | |
| 853 } | 879 } |
| 854 | 880 |
| 855 @SmallTest | 881 @SmallTest |
| 856 @Feature({"TextInput", "Main"}) | 882 @Feature({"TextInput", "Main"}) |
| 857 public void testSetComposingRegionOutOfBounds() throws Throwable { | 883 public void testSetComposingRegionOutOfBounds() throws Throwable { |
| 858 focusElement("textarea"); | 884 focusElement("textarea"); |
| 859 setComposingText("hello", 1); | 885 setComposingText("hello", 1); |
| 860 | 886 |
| 861 setComposingRegion(0, 0); | 887 setComposingRegion(0, 0); |
| 862 setComposingRegion(0, 9); | 888 setComposingRegion(0, 9); |
| 863 setComposingRegion(9, 0); | 889 setComposingRegion(9, 0); |
| 864 } | 890 } |
| 865 | 891 |
| 866 @SmallTest | 892 @SmallTest |
| 867 @Feature({"TextInput", "Main"}) | 893 @Feature({"TextInput", "Main"}) |
| 868 public void testEnterKey_AfterCommitText() throws Throwable { | 894 public void testEnterKey_AfterCommitText() throws Throwable { |
| 869 focusElementAndWaitForStateUpdate("textarea"); | 895 focusElementAndWaitForStateUpdate("textarea"); |
| 870 | 896 |
| 871 commitText("hello", 1); | 897 commitText("hello", 1); |
| 872 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 898 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 873 | 899 |
| 874 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); | 900 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); |
| 875 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); | 901 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); |
| 876 // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. | 902 waitAndVerifyUpdateSelection(1, 6, 6, -1, -1); |
| 877 // The second new line is not a user visible/editable one, it is a side- effect of Blink | 903 assertTextsAroundCursor("hello\n", "", "\n"); |
| 878 // using <br> internally. This only happens when \n is at the end. | |
| 879 waitAndVerifyStatesAndCalls(1, "hello\n\n", 6, 6, -1, -1); | |
| 880 | 904 |
| 881 commitText("world", 1); | 905 commitText("world", 1); |
| 882 waitAndVerifyStatesAndCalls(2, "hello\nworld", 11, 11, -1, -1); | 906 waitAndVerifyUpdateSelection(2, 11, 11, -1, -1); |
| 907 assertTextsAroundCursor("hello\nworld", "", ""); | |
| 883 } | 908 } |
| 884 | 909 |
| 885 @SmallTest | 910 @SmallTest |
| 886 @Feature({"TextInput", "Main"}) | 911 @Feature({"TextInput", "Main"}) |
| 887 public void testEnterKey_WhileComposingText() throws Throwable { | 912 public void testEnterKey_WhileComposingText() throws Throwable { |
| 888 focusElementAndWaitForStateUpdate("textarea"); | 913 focusElementAndWaitForStateUpdate("textarea"); |
| 889 | 914 |
| 890 setComposingText("hello", 1); | 915 setComposingText("hello", 1); |
| 891 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, 0, 5); | 916 waitAndVerifyUpdateSelection(0, 5, 5, 0, 5); |
| 917 | |
| 918 // IME app should call this, otherwise enter key should clear the curren t composition. | |
| 919 finishComposingText(); | |
| 920 waitAndVerifyUpdateSelection(1, 5, 5, -1, -1); | |
| 892 | 921 |
| 893 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); | 922 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENT ER)); |
| 894 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); | 923 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER )); |
| 895 | 924 |
| 896 // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. | |
| 897 // A transitional state due to finishComposingText. | |
| 898 waitAndVerifyStates(1, "hello", 5, 5, -1, -1); | |
| 899 // The second new line is not a user visible/editable one, it is a side- effect of Blink | 925 // The second new line is not a user visible/editable one, it is a side- effect of Blink |
| 900 // using <br> internally. This only happens when \n is at the end. | 926 // using <br> internally. This only happens when \n is at the end. |
| 901 waitAndVerifyStatesAndCalls(2, "hello\n\n", 6, 6, -1, -1); | 927 waitAndVerifyUpdateSelection(2, 6, 6, -1, -1); |
| 902 | 928 |
| 903 commitText("world", 1); | 929 commitText("world", 1); |
| 904 waitAndVerifyStatesAndCalls(3, "hello\nworld", 11, 11, -1, -1); | 930 waitAndVerifyUpdateSelection(3, 11, 11, -1, -1); |
| 931 assertTextsAroundCursor("hello\nworld", "", ""); | |
| 905 } | 932 } |
| 906 | 933 |
| 907 @SmallTest | 934 @SmallTest |
| 908 @Feature({"TextInput", "Main"}) | 935 @Feature({"TextInput", "Main"}) |
| 909 public void testDpadKeyCodesWhileSwipingText() throws Throwable { | 936 public void testDpadKeyCodesWhileSwipingText() throws Throwable { |
| 910 focusElement("textarea"); | 937 focusElement("textarea"); |
| 911 | 938 |
| 912 // DPAD_CENTER should cause keyboard to appear | 939 // DPAD_CENTER should cause keyboard to appear |
| 913 resetUpdateStateList(); | |
| 914 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_CENTER)); | 940 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_CENTER)); |
| 915 assertUpdateStateCall(1000); | 941 |
| 942 // TODO(changwan): should really check this. | |
| 916 } | 943 } |
| 917 | 944 |
| 918 @SmallTest | 945 @SmallTest |
| 919 @Feature({"TextInput", "Main"}) | 946 @Feature({"TextInput", "Main"}) |
| 920 public void testNavigateTextWithDpadKeyCodes() throws Throwable { | 947 public void testNavigateTextWithDpadKeyCodes() throws Throwable { |
| 921 focusElementAndWaitForStateUpdate("textarea"); | 948 focusElementAndWaitForStateUpdate("textarea"); |
| 922 | 949 |
| 923 commitText("hello", 1); | 950 commitText("hello", 1); |
| 924 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 951 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 925 | 952 |
| 926 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_LEFT)); | 953 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_LEFT)); |
| 927 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_ LEFT)); | 954 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_ LEFT)); |
| 928 | 955 |
| 929 // Ideally getTextBeforeCursor immediately after dispatchKeyEvent should return a correct | 956 if (usingReplicaInputConnection()) { |
| 930 // value, but we have a stop-gap solution in render_widget_input_handler and it make take | 957 // Ideally getTextBeforeCursor immediately after dispatchKeyEvent sh ould return a |
| 931 // some round trip time until we get the correct value. | 958 // correct value, but we have a stop-gap solution in render_widget_i nput_handler and it |
| 932 // TODO(changwan): Change the test not to wait when we fix it. | 959 // make take some round trip time until we get the correct value. |
| 933 waitUntilGetCharacterBeforeCursorBecomes("l"); | 960 waitUntilGetCharacterBeforeCursorBecomes("l"); |
| 961 } else { | |
| 962 assertTextsAroundCursor("hell", "", "o"); | |
| 963 } | |
| 934 } | 964 } |
| 935 | 965 |
| 936 private void waitUntilGetCharacterBeforeCursorBecomes(final String expectedT ext) | 966 private void waitUntilGetCharacterBeforeCursorBecomes(final String expectedT ext) |
| 937 throws InterruptedException { | 967 throws InterruptedException { |
| 938 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 968 pollForCriteriaOnThread(new Criteria() { |
| 939 @Override | 969 @Override |
| 940 public boolean isSatisfied() { | 970 public boolean isSatisfied() { |
| 941 String actualText = (String) mConnection.getTextBeforeCursor(1, 0); | 971 String actualText = (String) mConnection.getTextBeforeCursor(1, 0); |
| 942 updateFailureReason("actualText: " + actualText); | 972 updateFailureReason("actualText: " + actualText); |
| 943 return expectedText.equals(actualText); | 973 return expectedText.equals(actualText); |
| 944 } | 974 } |
| 945 }); | 975 }); |
| 946 } | 976 } |
| 947 | 977 |
| 978 private void pollForCriteriaOnThread(final Criteria criteria) throws Interru ptedException { | |
| 979 final Callable<Boolean> callable = new Callable<Boolean>() { | |
| 980 @Override | |
| 981 public Boolean call() throws Exception { | |
| 982 return criteria.isSatisfied(); | |
| 983 } | |
| 984 }; | |
| 985 CriteriaHelper.pollForCriteria(new Criteria() { | |
| 986 @Override | |
| 987 public boolean isSatisfied() { | |
| 988 try { | |
| 989 return runBlockingOnImeThread(callable); | |
| 990 } catch (Exception e) { | |
| 991 e.printStackTrace(); | |
| 992 fail(); | |
| 993 return false; | |
| 994 } | |
| 995 } | |
| 996 | |
| 997 @Override | |
| 998 public String getFailureReason() { | |
| 999 return criteria.getFailureReason(); | |
| 1000 } | |
| 1001 }); | |
| 1002 } | |
| 1003 | |
| 948 @SmallTest | 1004 @SmallTest |
| 949 @Feature({"TextInput"}) | 1005 @Feature({"TextInput"}) |
| 950 public void testPastePopupShowAndHide() throws Throwable { | 1006 public void testPastePopupShowAndHide() throws Throwable { |
| 951 commitText("hello", 1); | 1007 commitText("hello", 1); |
| 952 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 1008 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 953 | 1009 |
| 954 selectAll(); | 1010 selectAll(); |
| 955 waitAndVerifyStatesAndCalls(1, "hello", 0, 5, -1, -1); | 1011 waitAndVerifyUpdateSelection(1, 0, 5, -1, -1); |
| 1012 assertTextsAroundCursor("", "hello", ""); | |
| 956 | 1013 |
| 957 cut(); | 1014 cut(); |
| 958 waitAndVerifyStatesAndCalls(2, "", 0, 0, -1, -1); | 1015 waitAndVerifyUpdateSelection(2, 0, 0, -1, -1); |
| 1016 assertTextsAroundCursor("", "", ""); | |
| 959 | 1017 |
| 960 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 1018 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 961 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1019 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 962 @Override | 1020 @Override |
| 963 public boolean isSatisfied() { | 1021 public boolean isSatisfied() { |
| 964 return mContentViewCore.isPastePopupShowing(); | 1022 return mContentViewCore.isPastePopupShowing(); |
| 965 } | 1023 } |
| 966 }); | 1024 }); |
| 967 | 1025 |
| 968 DOMUtils.clickNode(this, mContentViewCore, "input_text"); | 1026 DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
| 969 assertWaitForKeyboardStatus(true); | 1027 assertWaitForKeyboardStatus(true); |
| 970 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 1028 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 971 setComposingText("h", 1); | 1029 setComposingText("h", 1); |
| 972 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1030 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 973 @Override | 1031 @Override |
| 974 public boolean isSatisfied() { | 1032 public boolean isSatisfied() { |
| 975 return !mContentViewCore.isPastePopupShowing(); | 1033 return !mContentViewCore.isPastePopupShowing(); |
| 976 } | 1034 } |
| 977 }); | 1035 }); |
| 978 assertFalse(mContentViewCore.hasInsertion()); | 1036 assertFalse(mContentViewCore.hasInsertion()); |
| 979 } | 1037 } |
| 980 | 1038 |
| 981 @SmallTest | 1039 @SmallTest |
| 982 @Feature({"TextInput"}) | 1040 @Feature({"TextInput"}) |
| 983 public void testSelectionClearedOnKeyEvent() throws Throwable { | 1041 public void testSelectionClearedOnKeyEvent() throws Throwable { |
| 984 commitText("hello", 1); | 1042 commitText("hello", 1); |
| 985 waitAndVerifyStatesAndCalls(0, "hello", 5, 5, -1, -1); | 1043 waitAndVerifyUpdateSelection(0, 5, 5, -1, -1); |
| 986 | 1044 |
| 987 DOMUtils.clickNode(this, mContentViewCore, "input_text"); | 1045 DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
| 988 assertWaitForKeyboardStatus(true); | 1046 assertWaitForKeyboardStatus(true); |
| 989 assertWaitForSelectActionBarStatus(true); | 1047 assertWaitForSelectActionBarStatus(true); |
| 990 | 1048 |
| 991 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); | 1049 DOMUtils.longPressNode(this, mContentViewCore, "input_text"); |
| 992 assertWaitForSelectActionBarStatus(true); | 1050 assertWaitForSelectActionBarStatus(true); |
| 993 | 1051 |
| 994 setComposingText("h", 1); | 1052 setComposingText("h", 1); |
| 995 assertWaitForSelectActionBarStatus(false); | 1053 assertWaitForSelectActionBarStatus(false); |
| 996 assertFalse(mContentViewCore.hasSelection()); | 1054 assertFalse(mContentViewCore.hasSelection()); |
| 997 } | 1055 } |
| 998 | 1056 |
| 999 @SmallTest | 1057 @SmallTest |
| 1000 @Feature({"TextInput"}) | 1058 @Feature({"TextInput"}) |
| 1001 public void testTextHandlesPreservedWithDpadNavigation() throws Throwable { | 1059 public void testTextHandlesPreservedWithDpadNavigation() throws Throwable { |
| 1002 DOMUtils.longPressNode(this, mContentViewCore, "plain_text"); | 1060 DOMUtils.longPressNode(this, mContentViewCore, "plain_text"); |
| 1003 assertWaitForSelectActionBarStatus(true); | 1061 assertWaitForSelectActionBarStatus(true); |
| 1004 assertTrue(mContentViewCore.hasSelection()); | 1062 assertTrue(mContentViewCore.hasSelection()); |
| 1005 | 1063 |
| 1006 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_DOWN)); | 1064 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPA D_DOWN)); |
| 1007 assertWaitForSelectActionBarStatus(true); | 1065 assertWaitForSelectActionBarStatus(true); |
| 1008 assertTrue(mContentViewCore.hasSelection()); | 1066 assertTrue(mContentViewCore.hasSelection()); |
| 1009 } | 1067 } |
| 1010 | 1068 |
| 1011 @MediumTest | 1069 @MediumTest |
| 1012 @Feature({"TextInput"}) | 1070 @Feature({"TextInput"}) |
| 1013 public void testRestartInputWhileComposingText() throws Throwable { | 1071 public void testRestartInputWhileComposingText() throws Throwable { |
| 1014 setComposingText("abc", 1); | 1072 setComposingText("abc", 1); |
| 1015 waitAndVerifyStatesAndCalls(0, "abc", 3, 3, 0, 3); | 1073 waitAndVerifyUpdateSelection(0, 3, 3, 0, 3); |
| 1016 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1074 restartInput(); |
| 1017 @Override | |
| 1018 public void run() { | |
| 1019 mImeAdapter.restartInput(); | |
| 1020 } | |
| 1021 }); | |
| 1022 // We don't do anything when input gets restarted. But we depend on Andr oid's | 1075 // We don't do anything when input gets restarted. But we depend on Andr oid's |
| 1023 // InputMethodManager and/or input methods to call finishComposingText() in setting | 1076 // InputMethodManager and/or input methods to call finishComposingText() in setting |
| 1024 // current input connection as active or finishing the current input con nection. | 1077 // current input connection as active or finishing the current input con nection. |
| 1025 assertNoFurtherStateUpdate(1); | 1078 Thread.sleep(1000); |
| 1079 assertEquals(1, mInputMethodManagerWrapper.getUpdateSelectionList().size ()); | |
| 1080 } | |
| 1081 | |
| 1082 @MediumTest | |
| 1083 @Feature({"TextInput"}) | |
| 1084 public void testRestartInputKeepsTextAndCursor() throws Exception { | |
| 1085 commitText("ab", 2); | |
| 1086 restartInput(); | |
| 1087 assertEquals("ab", getTextBeforeCursor(10, 0)); | |
| 1026 } | 1088 } |
| 1027 | 1089 |
| 1028 private void performGo(TestCallbackHelperContainer testCallbackHelperContain er) | 1090 private void performGo(TestCallbackHelperContainer testCallbackHelperContain er) |
| 1029 throws Throwable { | 1091 throws Throwable { |
| 1030 final AdapterInputConnection inputConnection = mConnection; | 1092 final InputConnection inputConnection = mConnection; |
| 1093 final Callable<Void> callable = new Callable<Void>() { | |
| 1094 @Override | |
| 1095 public Void call() throws Exception { | |
| 1096 inputConnection.performEditorAction(EditorInfo.IME_ACTION_GO); | |
| 1097 return null; | |
| 1098 } | |
| 1099 }; | |
| 1100 | |
| 1031 handleBlockingCallbackAction( | 1101 handleBlockingCallbackAction( |
| 1032 testCallbackHelperContainer.getOnPageFinishedHelper(), | 1102 testCallbackHelperContainer.getOnPageFinishedHelper(), |
| 1033 new Runnable() { | 1103 new Runnable() { |
| 1034 @Override | 1104 @Override |
| 1035 public void run() { | 1105 public void run() { |
| 1036 inputConnection.performEditorAction(EditorInfo.IME_ACTIO N_GO); | 1106 try { |
| 1107 runBlockingOnImeThread(callable); | |
| 1108 } catch (Exception e) { | |
| 1109 e.printStackTrace(); | |
| 1110 fail(); | |
| 1111 } | |
| 1037 } | 1112 } |
| 1038 }); | 1113 }); |
| 1039 } | 1114 } |
| 1040 | 1115 |
| 1041 private void assertWaitForKeyboardStatus(final boolean show) throws Interrup tedException { | 1116 private void assertWaitForKeyboardStatus(final boolean show) throws Interrup tedException { |
| 1042 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1117 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1043 @Override | 1118 @Override |
| 1044 public boolean isSatisfied() { | 1119 public boolean isSatisfied() { |
| 1045 // We do not check the other way around: in some cases we need t o keep | 1120 // We do not check the other way around: in some cases we need t o keep |
| 1046 // input connection even when the last known status is 'hidden'. | 1121 // input connection even when the last known status is 'hidden'. |
| 1047 // See crbug.com/569332 for more details. | 1122 if (show && getInputConnection() == null) { |
| 1048 if (show && getAdapterInputConnection() == null) { | |
| 1049 updateFailureReason("input connection should not be null."); | 1123 updateFailureReason("input connection should not be null."); |
| 1050 return false; | 1124 return false; |
| 1051 } | 1125 } |
| 1052 updateFailureReason("expected show: " + show); | 1126 updateFailureReason("expected show: " + show); |
| 1053 return show == mInputMethodManagerWrapper.isShowWithoutHideOutst anding(); | 1127 return show == mInputMethodManagerWrapper.isShowWithoutHideOutst anding(); |
| 1054 } | 1128 } |
| 1055 }); | 1129 }); |
| 1056 } | 1130 } |
| 1057 | 1131 |
| 1058 private void assertWaitForSelectActionBarStatus( | 1132 private void assertWaitForSelectActionBarStatus( |
| 1059 final boolean show) throws InterruptedException { | 1133 final boolean show) throws InterruptedException { |
| 1060 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1134 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1061 @Override | 1135 @Override |
| 1062 public boolean isSatisfied() { | 1136 public boolean isSatisfied() { |
| 1063 return show == mContentViewCore.isSelectActionBarShowing(); | 1137 return show == mContentViewCore.isSelectActionBarShowing(); |
| 1064 } | 1138 } |
| 1065 }); | 1139 }); |
| 1066 } | 1140 } |
| 1067 | 1141 |
| 1068 private void waitAndVerifyStates(final int index, String text, final int sel ectionStart, | 1142 private void waitAndVerifyUpdateSelection(final int index, final int selecti onStart, |
| 1069 final int selectionEnd, final int compositionStart, final int compos itionEnd) | 1143 final int selectionEnd, final int compositionStart, final int compos itionEnd) |
| 1070 throws InterruptedException { | 1144 throws InterruptedException { |
| 1071 final List<TestImeState> states = mConnectionFactory.getImeStateList(); | 1145 final List<Pair<Range, Range>> states = mInputMethodManagerWrapper.getUp dateSelectionList(); |
| 1072 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1146 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1073 @Override | 1147 @Override |
| 1074 public boolean isSatisfied() { | 1148 public boolean isSatisfied() { |
| 1075 return states.size() > index; | 1149 return states.size() > index; |
| 1076 } | 1150 } |
| 1077 }); | 1151 }); |
| 1078 states.get(index).assertEqualState( | 1152 Pair<Range, Range> selection = states.get(index); |
| 1079 text, selectionStart, selectionEnd, compositionStart, compositio nEnd); | 1153 assertEquals(selectionStart, selection.first.start()); |
| 1154 assertEquals(selectionEnd, selection.first.end()); | |
| 1155 assertEquals(compositionStart, selection.second.start()); | |
| 1156 assertEquals(compositionEnd, selection.second.end()); | |
| 1080 } | 1157 } |
| 1081 | 1158 |
| 1082 private void waitAndVerifyStatesAndCalls(final int index, String text, final int selectionStart, | 1159 private void resetUpdateSelectionList() { |
| 1083 final int selectionEnd, final int compositionStart, final int compos itionEnd) | 1160 mInputMethodManagerWrapper.getUpdateSelectionList().clear(); |
| 1084 throws InterruptedException { | |
| 1085 waitAndVerifyStates( | |
| 1086 index, text, selectionStart, selectionEnd, compositionStart, com positionEnd); | |
| 1087 | |
| 1088 // Wait and verify calls to InputMethodManager. | |
| 1089 final Range selection = new Range(selectionStart, selectionEnd); | |
| 1090 final Range composition = new Range(compositionStart, compositionEnd); | |
| 1091 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | |
| 1092 @Override | |
| 1093 public boolean isSatisfied() { | |
| 1094 updateFailureReason( | |
| 1095 "Actual selection was: " + mInputMethodManagerWrapper.ge tSelection() | |
| 1096 + ", and actual composition was: " | |
| 1097 + mInputMethodManagerWrapper.getComposition()); | |
| 1098 return mInputMethodManagerWrapper.getSelection().equals(selectio n) | |
| 1099 && mInputMethodManagerWrapper.getComposition().equals(co mposition); | |
| 1100 } | |
| 1101 }); | |
| 1102 } | |
| 1103 | |
| 1104 private void resetUpdateStateList() { | |
| 1105 mConnectionFactory.getImeStateList().clear(); | |
| 1106 } | |
| 1107 | |
| 1108 private void assertUpdateStateCall(int maxms) throws Exception { | |
| 1109 while (mConnectionFactory.getImeStateList().size() == 0 && maxms > 0) { | |
| 1110 try { | |
| 1111 Thread.sleep(50); | |
| 1112 } catch (Exception e) { | |
| 1113 // Not really a problem since we're just going to sleep again. | |
| 1114 } | |
| 1115 maxms -= 50; | |
| 1116 } | |
| 1117 assertTrue(mConnectionFactory.getImeStateList().size() > 0); | |
| 1118 } | 1161 } |
| 1119 | 1162 |
| 1120 private void assertClipboardContents(final Activity activity, final String e xpectedContents) | 1163 private void assertClipboardContents(final Activity activity, final String e xpectedContents) |
| 1121 throws InterruptedException { | 1164 throws InterruptedException { |
| 1122 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { | 1165 CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
| 1123 @Override | 1166 @Override |
| 1124 public boolean isSatisfied() { | 1167 public boolean isSatisfied() { |
| 1125 ClipboardManager clipboardManager = | 1168 ClipboardManager clipboardManager = |
| 1126 (ClipboardManager) activity.getSystemService(Context.CLI PBOARD_SERVICE); | 1169 (ClipboardManager) activity.getSystemService(Context.CLI PBOARD_SERVICE); |
| 1127 ClipData clip = clipboardManager.getPrimaryClip(); | 1170 ClipData clip = clipboardManager.getPrimaryClip(); |
| 1128 return clip != null && clip.getItemCount() == 1 | 1171 return clip != null && clip.getItemCount() == 1 |
| 1129 && TextUtils.equals(clip.getItemAt(0).getText(), expecte dContents); | 1172 && TextUtils.equals(clip.getItemAt(0).getText(), expecte dContents); |
| 1130 } | 1173 } |
| 1131 }); | 1174 }); |
| 1132 } | 1175 } |
| 1133 | 1176 |
| 1134 private ImeAdapter getImeAdapter() { | 1177 private ImeAdapter getImeAdapter() { |
| 1135 return mContentViewCore.getImeAdapterForTest(); | 1178 return mContentViewCore.getImeAdapterForTest(); |
| 1136 } | 1179 } |
| 1137 | 1180 |
| 1138 private AdapterInputConnection getAdapterInputConnection() { | 1181 private ChromiumBaseInputConnection getInputConnection() { |
| 1139 return mContentViewCore.getImeAdapterForTest().getInputConnectionForTest (); | 1182 try { |
| 1183 return ThreadUtils.runOnUiThreadBlocking(new Callable<ChromiumBaseIn putConnection>() { | |
| 1184 @Override | |
| 1185 public ChromiumBaseInputConnection call() { | |
| 1186 return mContentViewCore.getImeAdapterForTest().getInputConne ctionForTest(); | |
| 1187 } | |
| 1188 }); | |
| 1189 } catch (ExecutionException e) { | |
| 1190 e.printStackTrace(); | |
| 1191 fail(); | |
| 1192 return null; | |
| 1193 } | |
| 1194 } | |
| 1195 | |
| 1196 private void restartInput() { | |
| 1197 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
| 1198 @Override | |
| 1199 public void run() { | |
| 1200 mImeAdapter.restartInput(); | |
| 1201 } | |
| 1202 }); | |
| 1140 } | 1203 } |
| 1141 | 1204 |
| 1142 private void copy() { | 1205 private void copy() { |
| 1143 final WebContents webContents = mWebContents; | 1206 final WebContents webContents = mWebContents; |
| 1144 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1207 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1145 @Override | 1208 @Override |
| 1146 public void run() { | 1209 public void run() { |
| 1147 webContents.copy(); | 1210 webContents.copy(); |
| 1148 } | 1211 } |
| 1149 }); | 1212 }); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1182 private void unselect() { | 1245 private void unselect() { |
| 1183 final WebContents webContents = mWebContents; | 1246 final WebContents webContents = mWebContents; |
| 1184 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1247 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1185 @Override | 1248 @Override |
| 1186 public void run() { | 1249 public void run() { |
| 1187 webContents.unselect(); | 1250 webContents.unselect(); |
| 1188 } | 1251 } |
| 1189 }); | 1252 }); |
| 1190 } | 1253 } |
| 1191 | 1254 |
| 1192 private void commitText(final CharSequence text, final int newCursorPosition ) { | 1255 private <T> T runBlockingOnImeThread(Callable<T> c) throws Exception { |
| 1193 final AdapterInputConnection connection = mConnection; | 1256 return ImeTestUtils.runBlockingOnHandler(mConnectionFactory.getHandler() , c); |
| 1194 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1257 } |
| 1258 | |
| 1259 private boolean commitText(final CharSequence text, final int newCursorPosit ion) | |
| 1260 throws Exception { | |
| 1261 final ChromiumBaseInputConnection connection = mConnection; | |
| 1262 return runBlockingOnImeThread(new Callable<Boolean>() { | |
| 1195 @Override | 1263 @Override |
| 1196 public void run() { | 1264 public Boolean call() { |
| 1197 connection.commitText(text, newCursorPosition); | 1265 return connection.commitText(text, newCursorPosition); |
| 1198 } | 1266 } |
| 1199 }); | 1267 }); |
| 1200 } | 1268 } |
| 1201 | 1269 |
| 1202 private void setSelection(final int start, final int end) { | 1270 private boolean setSelection(final int start, final int end) throws Exceptio n { |
| 1203 final AdapterInputConnection connection = mConnection; | 1271 final ChromiumBaseInputConnection connection = mConnection; |
| 1204 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1272 return runBlockingOnImeThread(new Callable<Boolean>() { |
| 1205 @Override | 1273 @Override |
| 1206 public void run() { | 1274 public Boolean call() { |
| 1207 connection.setSelection(start, end); | 1275 return connection.setSelection(start, end); |
| 1208 } | 1276 } |
| 1209 }); | 1277 }); |
| 1210 } | 1278 } |
| 1211 | 1279 |
| 1212 private void setComposingRegion(final int start, final int end) { | 1280 private boolean setComposingRegion(final int start, final int end) throws Ex ception { |
| 1213 final AdapterInputConnection connection = mConnection; | 1281 final ChromiumBaseInputConnection connection = mConnection; |
| 1214 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1282 return runBlockingOnImeThread(new Callable<Boolean>() { |
| 1215 @Override | 1283 @Override |
| 1216 public void run() { | 1284 public Boolean call() { |
| 1217 connection.setComposingRegion(start, end); | 1285 return connection.setComposingRegion(start, end); |
| 1218 } | 1286 } |
| 1219 }); | 1287 }); |
| 1220 } | 1288 } |
| 1221 | 1289 |
| 1222 private void setComposingText(final CharSequence text, final int newCursorPo sition) { | 1290 private boolean setComposingText(final CharSequence text, final int newCurso rPosition) |
| 1223 final AdapterInputConnection connection = mConnection; | 1291 throws Exception { |
| 1224 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1292 final ChromiumBaseInputConnection connection = mConnection; |
| 1293 return runBlockingOnImeThread(new Callable<Boolean>() { | |
| 1225 @Override | 1294 @Override |
| 1226 public void run() { | 1295 public Boolean call() { |
| 1227 connection.setComposingText(text, newCursorPosition); | 1296 return connection.setComposingText(text, newCursorPosition); |
| 1228 } | 1297 } |
| 1229 }); | 1298 }); |
| 1230 } | 1299 } |
| 1231 | 1300 |
| 1232 private void finishComposingText() { | 1301 private boolean finishComposingText() throws Exception { |
| 1233 final AdapterInputConnection connection = mConnection; | 1302 final ChromiumBaseInputConnection connection = mConnection; |
| 1234 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1303 return runBlockingOnImeThread(new Callable<Boolean>() { |
| 1235 @Override | 1304 @Override |
| 1236 public void run() { | 1305 public Boolean call() { |
| 1237 connection.finishComposingText(); | 1306 return connection.finishComposingText(); |
| 1238 } | 1307 } |
| 1239 }); | 1308 }); |
| 1240 } | 1309 } |
| 1241 | 1310 |
| 1242 private void deleteSurroundingText(final int before, final int after) { | 1311 private boolean deleteSurroundingText(final int before, final int after) thr ows Exception { |
| 1243 final AdapterInputConnection connection = mConnection; | 1312 final ChromiumBaseInputConnection connection = mConnection; |
| 1244 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1313 return runBlockingOnImeThread(new Callable<Boolean>() { |
| 1245 @Override | 1314 @Override |
| 1246 public void run() { | 1315 public Boolean call() { |
| 1247 connection.deleteSurroundingText(before, after); | 1316 return connection.deleteSurroundingText(before, after); |
| 1248 } | 1317 } |
| 1249 }); | 1318 }); |
| 1250 } | 1319 } |
| 1320 | |
| 1321 private CharSequence getTextBeforeCursor(final int length, final int flags) throws Exception { | |
| 1322 final ChromiumBaseInputConnection connection = mConnection; | |
| 1323 return runBlockingOnImeThread(new Callable<CharSequence>() { | |
| 1324 @Override | |
| 1325 public CharSequence call() { | |
| 1326 return connection.getTextBeforeCursor(length, flags); | |
| 1327 } | |
| 1328 }); | |
| 1329 } | |
| 1330 | |
| 1331 private CharSequence getSelectedText(final int flags) throws Exception { | |
| 1332 final ChromiumBaseInputConnection connection = mConnection; | |
| 1333 return runBlockingOnImeThread(new Callable<CharSequence>() { | |
| 1334 @Override | |
| 1335 public CharSequence call() { | |
| 1336 return connection.getSelectedText(flags); | |
| 1337 } | |
| 1338 }); | |
| 1339 } | |
| 1340 | |
| 1341 private CharSequence getTextAfterCursor(final int length, final int flags) t hrows Exception { | |
| 1342 final ChromiumBaseInputConnection connection = mConnection; | |
| 1343 return runBlockingOnImeThread(new Callable<CharSequence>() { | |
| 1344 @Override | |
| 1345 public CharSequence call() { | |
| 1346 return connection.getTextAfterCursor(length, flags); | |
| 1347 } | |
| 1348 }); | |
| 1349 } | |
| 1251 | 1350 |
| 1252 private void dispatchKeyEvent(final KeyEvent event) { | 1351 private void dispatchKeyEvent(final KeyEvent event) { |
| 1253 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | 1352 ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| 1254 @Override | 1353 @Override |
| 1255 public void run() { | 1354 public void run() { |
| 1256 mImeAdapter.dispatchKeyEvent(event); | 1355 mImeAdapter.dispatchKeyEvent(event); |
| 1257 } | 1356 } |
| 1258 }); | 1357 }); |
| 1259 } | 1358 } |
| 1260 | 1359 |
| 1261 /** | 1360 /** |
| 1262 * Focus element, wait for a single state update, reset state update list. | 1361 * Focus element, wait for a single state update, reset state update list. |
| 1263 * @param id ID of the element to focus. | 1362 * @param id ID of the element to focus. |
| 1264 */ | 1363 */ |
| 1265 private void focusElementAndWaitForStateUpdate(String id) | 1364 private void focusElementAndWaitForStateUpdate(String id) |
| 1266 throws InterruptedException, TimeoutException { | 1365 throws InterruptedException, TimeoutException { |
| 1267 focusElement(id); | 1366 focusElement(id); |
| 1268 waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); | 1367 waitAndVerifyUpdateSelection(0, 0, 0, -1, -1); |
| 1269 resetUpdateStateList(); | 1368 resetUpdateSelectionList(); |
| 1270 } | 1369 } |
| 1271 | 1370 |
| 1272 private void focusElement(final String id) throws InterruptedException, Time outException { | 1371 private void focusElement(final String id) throws InterruptedException, Time outException { |
| 1273 focusElement(id, true); | 1372 focusElement(id, true); |
| 1274 } | 1373 } |
| 1275 | 1374 |
| 1276 private void focusElement(final String id, boolean shouldShowKeyboard) | 1375 private void focusElement(final String id, boolean shouldShowKeyboard) |
| 1277 throws InterruptedException, TimeoutException { | 1376 throws InterruptedException, TimeoutException { |
| 1278 DOMUtils.focusNode(mWebContents, id); | 1377 DOMUtils.focusNode(mWebContents, id); |
| 1279 assertWaitForKeyboardStatus(shouldShowKeyboard); | 1378 assertWaitForKeyboardStatus(shouldShowKeyboard); |
| 1280 CriteriaHelper.pollForCriteria(new Criteria() { | 1379 CriteriaHelper.pollForCriteria(new Criteria() { |
| 1281 @Override | 1380 @Override |
| 1282 public boolean isSatisfied() { | 1381 public boolean isSatisfied() { |
| 1283 try { | 1382 try { |
| 1284 return id.equals(DOMUtils.getFocusedNode(mWebContents)); | 1383 return id.equals(DOMUtils.getFocusedNode(mWebContents)); |
| 1285 } catch (Exception e) { | 1384 } catch (Exception e) { |
| 1286 return false; | 1385 return false; |
| 1287 } | 1386 } |
| 1288 } | 1387 } |
| 1289 }); | 1388 }); |
| 1290 // When we focus another element, the connection may be recreated. | 1389 // When we focus another element, the connection may be recreated. |
| 1291 mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); | 1390 mConnection = getInputConnection(); |
| 1292 } | 1391 } |
| 1293 | 1392 |
| 1294 private static class TestAdapterInputConnectionFactory extends | 1393 private boolean usingReplicaInputConnection() { |
| 1295 ImeAdapter.AdapterInputConnectionFactory { | 1394 return mConnectionFactory.getHandler().getLooper() == Looper.getMainLoop er(); |
| 1296 private final List<TestImeState> mImeStateList = new ArrayList<>(); | 1395 } |
| 1396 | |
| 1397 private static class TestInputConnectionFactory implements ChromiumBaseInput Connection.Factory { | |
| 1398 private final ChromiumBaseInputConnection.Factory mFactory; | |
| 1399 | |
| 1297 private final List<Integer> mTextInputTypeList = new ArrayList<>(); | 1400 private final List<Integer> mTextInputTypeList = new ArrayList<>(); |
| 1401 private EditorInfo mOutAttrs; | |
| 1402 | |
| 1403 public TestInputConnectionFactory(ChromiumBaseInputConnection.Factory fa ctory) { | |
| 1404 mFactory = factory; | |
| 1405 } | |
| 1298 | 1406 |
| 1299 @Override | 1407 @Override |
| 1300 public AdapterInputConnection get(View view, ImeAdapter imeAdapter, int initialSelStart, | 1408 public ChromiumBaseInputConnection initializeAndGet(View view, ImeAdapte r imeAdapter, |
| 1301 int initialSelEnd, EditorInfo outAttrs) { | 1409 int inputType, int inputFlags, int selectionStart, int selection End, |
| 1302 mTextInputTypeList.add(imeAdapter.getTextInputType()); | 1410 EditorInfo outAttrs) { |
| 1303 return new TestAdapterInputConnection( | 1411 mTextInputTypeList.add(inputType); |
| 1304 mImeStateList, view, imeAdapter, initialSelStart, initialSel End, outAttrs); | 1412 mOutAttrs = outAttrs; |
| 1413 return mFactory.initializeAndGet(view, imeAdapter, inputType, inputF lags, | |
| 1414 selectionStart, selectionEnd, outAttrs); | |
| 1305 } | 1415 } |
| 1306 | 1416 |
| 1307 public List<TestImeState> getImeStateList() { | 1417 @Override |
| 1308 return mImeStateList; | 1418 public Handler getHandler() { |
| 1419 return mFactory.getHandler(); | |
| 1309 } | 1420 } |
| 1310 | 1421 |
| 1311 public Integer[] getTextInputTypeHistory() { | 1422 public Integer[] getTextInputTypeHistory() { |
| 1312 Integer[] result = new Integer[mTextInputTypeList.size()]; | 1423 Integer[] result = new Integer[mTextInputTypeList.size()]; |
| 1313 mTextInputTypeList.toArray(result); | 1424 mTextInputTypeList.toArray(result); |
| 1314 return result; | 1425 return result; |
| 1315 } | 1426 } |
| 1316 | 1427 |
| 1317 public void clearTextInputTypeHistory() { | 1428 public void clearTextInputTypeHistory() { |
| 1318 mTextInputTypeList.clear(); | 1429 mTextInputTypeList.clear(); |
| 1319 } | 1430 } |
| 1320 } | |
| 1321 | 1431 |
| 1322 private static class TestAdapterInputConnection extends AdapterInputConnecti on { | 1432 public EditorInfo getOutAttrs() { |
| 1323 private final List<TestImeState> mImeStateList; | 1433 return mOutAttrs; |
| 1324 | |
| 1325 public TestAdapterInputConnection(List<TestImeState> imeStateList, View view, | |
| 1326 ImeAdapter imeAdapter, int initialSelStart, int initialSelEnd, | |
| 1327 EditorInfo outAttrs) { | |
| 1328 super(view, imeAdapter, initialSelStart, initialSelEnd, outAttrs); | |
| 1329 mImeStateList = imeStateList; | |
| 1330 } | |
| 1331 | |
| 1332 @Override | |
| 1333 public void updateState(String text, int selectionStart, int selectionEn d, | |
| 1334 int compositionStart, int compositionEnd, boolean requiredAck) { | |
| 1335 mImeStateList.add(new TestImeState( | |
| 1336 text, selectionStart, selectionEnd, compositionStart, compos itionEnd)); | |
| 1337 super.updateState(text, selectionStart, selectionEnd, compositionSta rt, | |
| 1338 compositionEnd, requiredAck); | |
| 1339 } | |
| 1340 } | |
| 1341 | |
| 1342 private static class TestImeState { | |
| 1343 private final String mText; | |
| 1344 private final int mSelectionStart; | |
| 1345 private final int mSelectionEnd; | |
| 1346 private final int mCompositionStart; | |
| 1347 private final int mCompositionEnd; | |
| 1348 | |
| 1349 public TestImeState(String text, int selectionStart, int selectionEnd, | |
| 1350 int compositionStart, int compositionEnd) { | |
| 1351 mText = text; | |
| 1352 mSelectionStart = selectionStart; | |
| 1353 mSelectionEnd = selectionEnd; | |
| 1354 mCompositionStart = compositionStart; | |
| 1355 mCompositionEnd = compositionEnd; | |
| 1356 } | |
| 1357 | |
| 1358 public void assertEqualState(String text, int selectionStart, int select ionEnd, | |
| 1359 int compositionStart, int compositionEnd) { | |
| 1360 assertEquals("Text did not match", text, mText); | |
| 1361 assertEquals("Selection start did not match", selectionStart, mSelec tionStart); | |
| 1362 assertEquals("Selection end did not match", selectionEnd, mSelection End); | |
| 1363 assertEquals("Composition start did not match", compositionStart, mC ompositionStart); | |
| 1364 assertEquals("Composition end did not match", compositionEnd, mCompo sitionEnd); | |
| 1365 } | 1434 } |
| 1366 } | 1435 } |
| 1367 } | 1436 } |
| OLD | NEW |