Index: content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java |
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java |
index bf4c10a3fb8dbd73e129020d4e867d539b0e9fea..46b28024aa66b5d746aeaed8390e080b59de40ef 100644 |
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java |
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java |
@@ -29,8 +29,10 @@ import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; |
import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper.Range; |
import org.chromium.content_public.browser.WebContents; |
import org.chromium.content_shell_apk.ContentShellTestBase; |
+import org.chromium.ui.base.ime.TextInputType; |
import java.util.ArrayList; |
+import java.util.Arrays; |
import java.util.List; |
import java.util.concurrent.TimeoutException; |
@@ -38,16 +40,17 @@ import java.util.concurrent.TimeoutException; |
* Integration tests for text input using cases based on fixed regressions. |
*/ |
public class ImeTest extends ContentShellTestBase { |
- |
private static final String DATA_URL = UrlUtils.encodeHtmlDataUri( |
"<html><head><meta name=\"viewport\"" |
+ "content=\"width=device-width, initial-scale=2.0, maximum-scale=2.0\" /></head>" |
+ "<body><form action=\"about:blank\">" |
- + "<input id=\"input_text\" type=\"text\" /><br/>" |
+ + "<input id=\"input_text\" type=\"text\" /><br/></form><form>" |
+ "<input id=\"input_radio\" type=\"radio\" style=\"width:50px;height:50px\" />" |
+ "<br/><textarea id=\"textarea\" rows=\"4\" cols=\"20\"></textarea>" |
+ "<br/><textarea id=\"textarea2\" rows=\"4\" cols=\"20\" autocomplete=\"off\">" |
+ "</textarea>" |
+ + "<br/><input id=\"input_number1\" type=\"number\" /><br/>" |
+ + "<br/><input id=\"input_number2\" type=\"number\" /><br/>" |
+ "<br/><p><span id=\"plain_text\">This is Plain Text One</span></p>" |
+ "</form></body></html>"); |
@@ -86,20 +89,17 @@ public class ImeTest extends ContentShellTestBase { |
mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); |
mImeAdapter = getImeAdapter(); |
- // Two state updates from focus change and GestureTap. |
waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); |
- waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); |
- |
- assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
+ waitForKeyboardStates(1, 0, 1, new Integer[] {TextInputType.TEXT}); |
assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); |
assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); |
- resetUpdateStateList(); |
+ resetAllStates(); |
} |
private void assertNoFurtherStateUpdate(final int index) throws InterruptedException { |
final List<TestImeState> states = mConnectionFactory.getImeStateList(); |
- assertFalse(CriteriaHelper.pollForCriteria(new Criteria() { |
+ assertFalse(CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
@Override |
public boolean isSatisfied() { |
return states.size() > index; |
@@ -111,6 +111,7 @@ public class ImeTest extends ContentShellTestBase { |
@Feature({"TextInput", "Main"}) |
public void testSetUpGeneratesNoFurtherStateUpdate() throws Throwable { |
assertNoFurtherStateUpdate(0); |
+ waitForKeyboardStates(0, 0, 0, new Integer[] {}); |
} |
@MediumTest |
@@ -127,28 +128,22 @@ public class ImeTest extends ContentShellTestBase { |
@SmallTest |
@Feature({"TextInput", "Main"}) |
- @RerunWithUpdatedContainerView |
- public void testGetTextUpdatesAfterEnteringText() throws Throwable { |
+ public void testCommitWhileComposingText() throws Throwable { |
setComposingText("h", 1); |
waitAndVerifyStates(0, "h", 1, 1, 0, 1); |
- assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
setComposingText("he", 1); |
waitAndVerifyStates(1, "he", 2, 2, 0, 2); |
- assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
setComposingText("hel", 1); |
waitAndVerifyStates(2, "hel", 3, 3, 0, 3); |
- assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
commitText("hel", 1); |
waitAndVerifyStates(3, "hel", 3, 3, -1, -1); |
- assertEquals(1, mInputMethodManagerWrapper.getShowSoftInputCounter()); |
} |
@SmallTest |
@Feature({"TextInput"}) |
- @RerunWithUpdatedContainerView |
public void testImeCopy() throws Exception { |
commitText("hello", 1); |
waitAndVerifyStates(0, "hello", 5, 5, -1, -1); |
@@ -168,11 +163,80 @@ public class ImeTest extends ContentShellTestBase { |
DOMUtils.clickNode(this, mContentViewCore, "input_radio"); |
assertWaitForKeyboardStatus(false); |
+ waitAndVerifyStatesAndCalls(1, "", 0, 0, -1, -1); |
DOMUtils.clickNode(this, mContentViewCore, "input_text"); |
assertWaitForKeyboardStatus(true); |
- assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); |
- assertEquals(5, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); |
+ |
+ // The initial values will not be correct here because we call showSoftInput() |
+ // before updating selection range. |
+ assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelStart); |
+ assertEquals(0, mInputMethodManagerWrapper.getEditorInfo().initialSelEnd); |
+ |
+ // The values will immediately be updated. |
+ waitAndVerifyStatesAndCalls(2, "hello", 5, 5, -1, -1); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"TextInput"}) |
+ public void testShowAndHideSoftInput() throws Exception { |
+ focusElement("input_radio", false); |
+ waitAndVerifyStatesAndCalls(0, "", 0, 0, -1, -1); |
+ |
+ // hideSoftKeyboard(). |
+ waitForKeyboardStates(0, 1, 0, new Integer[] {}); |
+ |
+ // showSoftInput(), restartInput() |
+ focusElement("input_number1"); |
+ waitForKeyboardStates(1, 1, 1, new Integer[] {TextInputType.NUMBER}); |
+ |
+ focusElement("input_number2"); |
+ // Hide should never be called here. Otherwise we will see a flicker. Restarted to |
+ // reset internal states to handle the new input form. |
+ waitForKeyboardStates(2, 1, 2, new Integer[] {TextInputType.NUMBER, TextInputType.NUMBER}); |
+ |
+ focusElement("input_text"); |
+ // showSoftInput() on input_text. restartInput() on input_number1 due to focus change, |
+ // and restartInput() on input_text later. |
+ // TODO(changwan): reduce unnecessary restart input. |
+ waitForKeyboardStates(3, 1, 4, new Integer[] {TextInputType.NUMBER, TextInputType.NUMBER, |
+ TextInputType.NUMBER, TextInputType.TEXT}); |
+ |
+ focusElement("input_radio", false); |
+ // hideSoftInput(). |
+ waitForKeyboardStates(3, 2, 4, new Integer[] {TextInputType.NUMBER, TextInputType.NUMBER, |
+ TextInputType.NUMBER, TextInputType.TEXT}); |
+ } |
+ |
+ private void waitForKeyboardStates(int show, int hide, int restart, Integer[] history) |
+ throws InterruptedException { |
+ final String expected = stringifyKeyboardStates(show, hide, restart, history); |
+ assertTrue("Expected: {" + expected + "}, Actual: {" + getKeyboardStates() + "}", |
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ return expected.equals(getKeyboardStates()); |
+ } |
+ })); |
+ } |
+ |
+ private void resetAllStates() { |
+ mInputMethodManagerWrapper.resetCounters(); |
+ mConnectionFactory.clearTextInputTypeHistory(); |
+ resetUpdateStateList(); |
+ } |
+ |
+ private String getKeyboardStates() { |
+ int showCount = mInputMethodManagerWrapper.getShowSoftInputCounter(); |
+ int hideCount = mInputMethodManagerWrapper.getHideSoftInputCounter(); |
+ int restartCount = mInputMethodManagerWrapper.getRestartInputCounter(); |
+ Integer[] history = mConnectionFactory.getTextInputTypeHistory(); |
+ return stringifyKeyboardStates(showCount, hideCount, restartCount, history); |
+ } |
+ |
+ private String stringifyKeyboardStates(int show, int hide, int restart, Integer[] history) { |
+ return "show count: " + show + ", hide count: " + hide + ", restart count: " + restart |
+ + ", input type history: " + Arrays.deepToString(history); |
} |
@SmallTest |
@@ -236,13 +300,12 @@ public class ImeTest extends ContentShellTestBase { |
// Long press will first change selection region, and then trigger IME app to show up. |
// See RenderFrameImpl::didChangeSelection() and RenderWidget::didHandleGestureEvent(). |
waitAndVerifyStatesAndCalls(1, "Sample Text", 7, 11, 0, 11); |
- waitAndVerifyStatesAndCalls(2, "Sample Text", 7, 11, 0, 11); |
// Now IME app wants to finish composing text because an external selection |
// change has been detected. At least Google Latin IME and Samsung IME |
// behave this way. |
finishComposingText(); |
- waitAndVerifyStatesAndCalls(3, "Sample Text", 7, 11, -1, -1); |
+ waitAndVerifyStatesAndCalls(2, "Sample Text", 7, 11, -1, -1); |
} |
@SmallTest |
@@ -839,7 +902,7 @@ public class ImeTest extends ContentShellTestBase { |
assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
@Override |
public boolean isSatisfied() { |
- return show == getImeAdapter().mIsShowWithoutHideOutstanding |
+ return show == mInputMethodManagerWrapper.isShowWithoutHideOutstanding() |
&& (!show || getAdapterInputConnection() != null); |
} |
})); |
@@ -859,7 +922,7 @@ public class ImeTest extends ContentShellTestBase { |
final int selectionEnd, final int compositionStart, final int compositionEnd) |
throws InterruptedException { |
final List<TestImeState> states = mConnectionFactory.getImeStateList(); |
- assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { |
+ assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
@Override |
public boolean isSatisfied() { |
return states.size() > index; |
@@ -1059,8 +1122,13 @@ public class ImeTest extends ContentShellTestBase { |
} |
private void focusElement(final String id) throws InterruptedException, TimeoutException { |
+ focusElement(id, true); |
+ } |
+ |
+ private void focusElement(final String id, boolean shouldShowKeyboard) |
+ throws InterruptedException, TimeoutException { |
DOMUtils.focusNode(mWebContents, id); |
- assertWaitForKeyboardStatus(true); |
+ assertWaitForKeyboardStatus(shouldShowKeyboard); |
assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { |
@Override |
public boolean isSatisfied() { |
@@ -1078,10 +1146,12 @@ public class ImeTest extends ContentShellTestBase { |
private static class TestAdapterInputConnectionFactory extends |
ImeAdapter.AdapterInputConnectionFactory { |
private final List<TestImeState> mImeStateList = new ArrayList<>(); |
+ private final List<Integer> mTextInputTypeList = new ArrayList<>(); |
@Override |
public AdapterInputConnection get(View view, ImeAdapter imeAdapter, |
Editable editable, EditorInfo outAttrs) { |
+ mTextInputTypeList.add(imeAdapter.getTextInputType()); |
return new TestAdapterInputConnection( |
mImeStateList, view, imeAdapter, editable, outAttrs); |
} |
@@ -1089,6 +1159,16 @@ public class ImeTest extends ContentShellTestBase { |
public List<TestImeState> getImeStateList() { |
return mImeStateList; |
} |
+ |
+ public Integer[] getTextInputTypeHistory() { |
+ Integer[] result = new Integer[mTextInputTypeList.size()]; |
+ mTextInputTypeList.toArray(result); |
+ return result; |
+ } |
+ |
+ public void clearTextInputTypeHistory() { |
+ mTextInputTypeList.clear(); |
+ } |
} |
private static class TestAdapterInputConnection extends AdapterInputConnection { |