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