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