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 |