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