summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java34
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java88
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java36
-rw-r--r--content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreInputConnectionTest.java68
-rw-r--r--content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java5
-rw-r--r--content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java10
6 files changed, 162 insertions, 79 deletions
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index a174cd3..8965192 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -26,6 +26,7 @@ import android.os.SystemClock;
import android.provider.Browser;
import android.provider.Settings;
import android.text.Editable;
+import android.text.Selection;
import android.text.TextUtils;
import android.util.Log;
import android.view.ActionMode;
@@ -419,6 +420,12 @@ public class ContentViewCore
private SmartClipDataListener mSmartClipDataListener = null;
+ // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
+ // a focused element.
+ // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
+ // state must be reflected to this to keep consistency.
+ private Editable mEditable;
+
/**
* PID used to indicate an invalid render process.
*/
@@ -453,6 +460,9 @@ public class ContentViewCore
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mGestureStateListeners = new ObserverList<GestureStateListener>();
mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
+
+ mEditable = Editable.Factory.getInstance().newEditable("");
+ Selection.setSelection(mEditable, 0);
}
/**
@@ -585,6 +595,11 @@ public class ContentViewCore
return mInputConnection;
}
+ @VisibleForTesting
+ public void setContainerViewForTest(ViewGroup view) {
+ mContainerView = view;
+ }
+
private ImeAdapter createImeAdapter(Context context) {
return new ImeAdapter(mInputMethodManagerWrapper,
new ImeAdapter.ImeAdapterDelegate() {
@@ -1499,13 +1514,19 @@ public class ContentViewCore
// enter fullscreen mode.
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
}
- mInputConnection =
- mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
+ mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
+ mEditable, outAttrs);
return mInputConnection;
}
+ @VisibleForTesting
+ public AdapterInputConnection getAdapterInputConnectionForTest() {
+ return mInputConnection;
+ }
+
+ @VisibleForTesting
public Editable getEditableForTest() {
- return mInputConnection.getEditable();
+ return mEditable;
}
/**
@@ -1524,9 +1545,7 @@ public class ContentViewCore
if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
- ImeAdapter.getTextInputTypeNone(),
- AdapterInputConnection.INVALID_SELECTION,
- AdapterInputConnection.INVALID_SELECTION);
+ ImeAdapter.getTextInputTypeNone());
mInputMethodManagerWrapper.restartInput(mContainerView);
}
mContainerViewInternals.super_onConfigurationChanged(newConfig);
@@ -2351,8 +2370,7 @@ public class ContentViewCore
if (mActionMode != null) mActionMode.invalidate();
- mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
- selectionStart, selectionEnd, showImeIfNeeded);
+ mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType, showImeIfNeeded);
if (mInputConnection != null) {
mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
index 95f2bbf..dcc08fa 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
@@ -34,6 +34,7 @@ public class AdapterInputConnection extends BaseInputConnection {
private final View mInternalView;
private final ImeAdapter mImeAdapter;
+ private final Editable mEditable;
private boolean mSingleLine;
private int mNumNestedBatchEdits = 0;
@@ -44,11 +45,17 @@ public class AdapterInputConnection extends BaseInputConnection {
private int mLastUpdateCompositionEnd = INVALID_COMPOSITION;
@VisibleForTesting
- AdapterInputConnection(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
+ AdapterInputConnection(View view, ImeAdapter imeAdapter, Editable editable,
+ EditorInfo outAttrs) {
super(view, true);
mInternalView = view;
mImeAdapter = imeAdapter;
mImeAdapter.setInputConnection(this);
+ mEditable = editable;
+ // The editable passed in might have been in use by a prior keyboard and could have had
+ // prior composition spans set. To avoid keyboard conflicts, remove all composing spans
+ // when taking ownership of an existing Editable.
+ removeComposingSpans(mEditable);
mSingleLine = true;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN
| EditorInfo.IME_FLAG_NO_EXTRACT_UI;
@@ -97,10 +104,13 @@ public class AdapterInputConnection extends BaseInputConnection {
| InputType.TYPE_NUMBER_VARIATION_NORMAL;
outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
}
- outAttrs.initialSelStart = imeAdapter.getInitialSelectionStart();
- outAttrs.initialSelEnd = imeAdapter.getInitialSelectionEnd();
- mLastUpdateSelectionStart = imeAdapter.getInitialSelectionStart();
- mLastUpdateSelectionEnd = imeAdapter.getInitialSelectionEnd();
+ outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
+ outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
+ mLastUpdateSelectionStart = Selection.getSelectionStart(mEditable);
+ mLastUpdateSelectionEnd = Selection.getSelectionEnd(mEditable);
+
+ Selection.setSelection(mEditable, outAttrs.initialSelStart, outAttrs.initialSelEnd);
+ updateSelectionIfRequired();
}
/**
@@ -120,6 +130,7 @@ public class AdapterInputConnection extends BaseInputConnection {
* selection.
* @param requireAck True when the update was not caused by IME, false otherwise.
*/
+ @VisibleForTesting
public void updateState(String text, int selectionStart, int selectionEnd, int compositionStart,
int compositionEnd, boolean requireAck) {
if (DEBUG) {
@@ -136,18 +147,17 @@ public class AdapterInputConnection extends BaseInputConnection {
compositionStart = Math.min(compositionStart, text.length());
compositionEnd = Math.min(compositionEnd, text.length());
- Editable editable = getEditable();
- String prevText = editable.toString();
+ String prevText = mEditable.toString();
boolean textUnchanged = prevText.equals(text);
if (!textUnchanged) {
- editable.replace(0, editable.length(), text);
+ mEditable.replace(0, mEditable.length(), text);
}
- Selection.setSelection(editable, selectionStart, selectionEnd);
+ Selection.setSelection(mEditable, selectionStart, selectionEnd);
if (compositionStart == compositionEnd) {
- removeComposingSpans(editable);
+ removeComposingSpans(mEditable);
} else {
super.setComposingRegion(compositionStart, compositionEnd);
}
@@ -155,16 +165,23 @@ public class AdapterInputConnection extends BaseInputConnection {
}
/**
+ * @return Editable object which contains the state of current focused editable element.
+ */
+ @Override
+ public Editable getEditable() {
+ return mEditable;
+ }
+
+ /**
* Sends selection update to the InputMethodManager unless we are currently in a batch edit or
* if the exact same selection and composition update was sent already.
*/
private void updateSelectionIfRequired() {
if (mNumNestedBatchEdits != 0) return;
- Editable editable = getEditable();
- int selectionStart = Selection.getSelectionStart(editable);
- int selectionEnd = Selection.getSelectionEnd(editable);
- int compositionStart = getComposingSpanStart(editable);
- int compositionEnd = getComposingSpanEnd(editable);
+ int selectionStart = Selection.getSelectionStart(mEditable);
+ int selectionEnd = Selection.getSelectionEnd(mEditable);
+ int compositionStart = getComposingSpanStart(mEditable);
+ int compositionEnd = getComposingSpanEnd(mEditable);
// Avoid sending update if we sent an exact update already previously.
if (mLastUpdateSelectionStart == selectionStart &&
mLastUpdateSelectionEnd == selectionEnd &&
@@ -258,11 +275,10 @@ public class AdapterInputConnection extends BaseInputConnection {
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
if (DEBUG) Log.w(TAG, "getExtractedText");
ExtractedText et = new ExtractedText();
- Editable editable = getEditable();
- et.text = editable.toString();
- et.partialEndOffset = editable.length();
- et.selectionStart = Selection.getSelectionStart(editable);
- et.selectionEnd = Selection.getSelectionEnd(editable);
+ et.text = mEditable.toString();
+ et.partialEndOffset = mEditable.length();
+ et.selectionStart = Selection.getSelectionStart(mEditable);
+ et.selectionEnd = Selection.getSelectionEnd(mEditable);
et.flags = mSingleLine ? ExtractedText.FLAG_SINGLE_LINE : 0;
return et;
}
@@ -297,9 +313,8 @@ public class AdapterInputConnection extends BaseInputConnection {
if (DEBUG) {
Log.w(TAG, "deleteSurroundingText [" + beforeLength + " " + afterLength + "]");
}
- Editable editable = getEditable();
- int availableBefore = Selection.getSelectionStart(editable);
- int availableAfter = editable.length() - Selection.getSelectionEnd(editable);
+ int availableBefore = Selection.getSelectionStart(mEditable);
+ int availableAfter = mEditable.length() - Selection.getSelectionEnd(mEditable);
beforeLength = Math.min(beforeLength, availableBefore);
afterLength = Math.min(afterLength, availableAfter);
super.deleteSurroundingText(beforeLength, afterLength);
@@ -328,15 +343,14 @@ public class AdapterInputConnection extends BaseInputConnection {
} else {
int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0) {
- Editable editable = getEditable();
- int selectionStart = Selection.getSelectionStart(editable);
- int selectionEnd = Selection.getSelectionEnd(editable);
+ int selectionStart = Selection.getSelectionStart(mEditable);
+ int selectionEnd = Selection.getSelectionEnd(mEditable);
if (selectionStart > selectionEnd) {
int temp = selectionStart;
selectionStart = selectionEnd;
selectionEnd = temp;
}
- editable.replace(selectionStart, selectionEnd,
+ mEditable.replace(selectionStart, selectionEnd,
Character.toString((char) unicodeChar));
}
}
@@ -364,8 +378,7 @@ public class AdapterInputConnection extends BaseInputConnection {
@Override
public boolean finishComposingText() {
if (DEBUG) Log.w(TAG, "finishComposingText");
- Editable editable = getEditable();
- if (getComposingSpanStart(editable) == getComposingSpanEnd(editable)) {
+ if (getComposingSpanStart(mEditable) == getComposingSpanEnd(mEditable)) {
return true;
}
@@ -382,7 +395,7 @@ public class AdapterInputConnection extends BaseInputConnection {
@Override
public boolean setSelection(int start, int end) {
if (DEBUG) Log.w(TAG, "setSelection [" + start + " " + end + "]");
- int textLength = getEditable().length();
+ int textLength = mEditable.length();
if (start < 0 || end < 0 || start > textLength || end > textLength) return true;
super.setSelection(start, end);
updateSelectionIfRequired();
@@ -405,7 +418,7 @@ public class AdapterInputConnection extends BaseInputConnection {
@Override
public boolean setComposingRegion(int start, int end) {
if (DEBUG) Log.w(TAG, "setComposingRegion [" + start + " " + end + "]");
- int textLength = getEditable().length();
+ int textLength = mEditable.length();
int a = Math.min(start, end);
int b = Math.max(start, end);
if (a < 0) a = 0;
@@ -414,7 +427,7 @@ public class AdapterInputConnection extends BaseInputConnection {
if (b > textLength) b = textLength;
if (a == b) {
- removeComposingSpans(getEditable());
+ removeComposingSpans(mEditable);
} else {
super.setComposingRegion(a, b);
}
@@ -450,12 +463,11 @@ public class AdapterInputConnection extends BaseInputConnection {
@VisibleForTesting
ImeState getImeStateForTesting() {
- Editable editable = getEditable();
- String text = editable.toString();
- int selectionStart = Selection.getSelectionStart(editable);
- int selectionEnd = Selection.getSelectionEnd(editable);
- int compositionStart = getComposingSpanStart(editable);
- int compositionEnd = getComposingSpanEnd(editable);
+ String text = mEditable.toString();
+ int selectionStart = Selection.getSelectionStart(mEditable);
+ int selectionEnd = Selection.getSelectionEnd(mEditable);
+ int compositionStart = getComposingSpanStart(mEditable);
+ int compositionEnd = getComposingSpanEnd(mEditable);
return new ImeState(text, selectionStart, selectionEnd, compositionStart, compositionEnd);
}
}
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
index 826d80e..2ce35f8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
@@ -7,6 +7,7 @@ package org.chromium.content.browser.input;
import android.os.Handler;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.text.Editable;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
@@ -63,8 +64,7 @@ public class ImeAdapter {
@Override
public void run() {
- attach(mNativeImeAdapter, sTextInputTypeNone, AdapterInputConnection.INVALID_SELECTION,
- AdapterInputConnection.INVALID_SELECTION);
+ attach(mNativeImeAdapter, sTextInputTypeNone);
dismissInput(true);
}
}
@@ -106,8 +106,6 @@ public class ImeAdapter {
private final Handler mHandler;
private DelayedDismissInput mDismissInput = null;
private int mTextInputType;
- private int mInitialSelectionStart;
- private int mInitialSelectionEnd;
@VisibleForTesting
boolean mIsShowWithoutHideOutstanding = false;
@@ -127,8 +125,9 @@ public class ImeAdapter {
* Default factory for AdapterInputConnection classes.
*/
public static class AdapterInputConnectionFactory {
- public AdapterInputConnection get(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
- return new AdapterInputConnection(view, imeAdapter, outAttrs);
+ public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
+ Editable editable, EditorInfo outAttrs) {
+ return new AdapterInputConnection(view, imeAdapter, editable, outAttrs);
}
}
@@ -162,22 +161,6 @@ public class ImeAdapter {
return mTextInputType;
}
- /**
- * Should be only used by AdapterInputConnection.
- * @return The starting index of the initial text selection.
- */
- int getInitialSelectionStart() {
- return mInitialSelectionStart;
- }
-
- /**
- * Should be only used by AdapterInputConnection.
- * @return The ending index of the initial text selection.
- */
- int getInitialSelectionEnd() {
- return mInitialSelectionEnd;
- }
-
public static int getTextInputTypeNone() {
return sTextInputTypeNone;
}
@@ -212,7 +195,7 @@ public class ImeAdapter {
}
public void attachAndShowIfNeeded(long nativeImeAdapter, int textInputType,
- int selectionStart, int selectionEnd, boolean showIfNeeded) {
+ boolean showIfNeeded) {
mHandler.removeCallbacks(mDismissInput);
// If current input type is none and showIfNeeded is false, IME should not be shown
@@ -230,7 +213,7 @@ public class ImeAdapter {
return;
}
- attach(nativeImeAdapter, textInputType, selectionStart, selectionEnd);
+ attach(nativeImeAdapter, textInputType);
mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView());
if (showIfNeeded) {
@@ -241,15 +224,12 @@ public class ImeAdapter {
}
}
- public void attach(long nativeImeAdapter, int textInputType, int selectionStart,
- int selectionEnd) {
+ public void attach(long nativeImeAdapter, int textInputType) {
if (mNativeImeAdapterAndroid != 0) {
nativeResetImeAdapter(mNativeImeAdapterAndroid);
}
mNativeImeAdapterAndroid = nativeImeAdapter;
mTextInputType = textInputType;
- mInitialSelectionStart = selectionStart;
- mInitialSelectionEnd = selectionEnd;
if (nativeImeAdapter != 0) {
nativeAttachImeAdapter(mNativeImeAdapterAndroid);
}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreInputConnectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreInputConnectionTest.java
new file mode 100644
index 0000000..8ef23b1
--- /dev/null
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreInputConnectionTest.java
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import org.chromium.content.browser.input.AdapterInputConnection;
+import org.chromium.content.browser.input.ImeAdapter;
+import org.chromium.content.browser.input.InputMethodManagerWrapper;
+import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper;
+import org.chromium.content_shell_apk.ContentShellTestBase;
+
+/**
+ * Tests that when InputConnection is recreated, the text is still retained.
+ */
+public class ContentViewCoreInputConnectionTest extends ContentShellTestBase {
+ private ContentViewCore mContentViewCore;
+ private TestImeAdapter mImeAdapter;
+ private TestInputMethodManagerWrapper mInputMethodManagerWrapper;
+
+ private static class TestImeAdapter extends ImeAdapter {
+ public TestImeAdapter(InputMethodManagerWrapper immw) {
+ super(immw, null);
+ }
+ @Override
+ public boolean hasTextInputType() {
+ return true;
+ }
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mContentViewCore = new ContentViewCore(getActivity());
+ mInputMethodManagerWrapper = new TestInputMethodManagerWrapper(mContentViewCore);
+ mImeAdapter = new TestImeAdapter(mInputMethodManagerWrapper);
+ mImeAdapter.setInputMethodManagerWrapper(new TestInputMethodManagerWrapper(
+ mContentViewCore));
+ mContentViewCore.setImeAdapterForTest(mImeAdapter);
+ mContentViewCore.setContainerViewForTest(getActivity().getActiveContentView());
+ }
+
+ /**
+ * When creating a new InputConnection (e.g. after switching software keyboard), make sure the
+ * text content in the Editable is not lost.
+ */
+ @SmallTest
+ public void testRecreateInputConnection() throws Exception {
+ EditorInfo info = new EditorInfo();
+
+ InputConnection inputConnection = mContentViewCore.onCreateInputConnection(info);
+ AdapterInputConnection adapter = mContentViewCore.getAdapterInputConnectionForTest();
+ adapter.updateState("Is this text restored?", 0, 0, 0, 0, true);
+
+ String text = mContentViewCore.getEditableForTest().toString();
+ assertEquals("Check if the initial text is stored.", "Is this text restored?", text);
+
+ // Create a new InputConnection.
+ EditorInfo info2 = new EditorInfo();
+ inputConnection = mContentViewCore.onCreateInputConnection(info2);
+
+ String newtext = mContentViewCore.getEditableForTest().toString();
+ assertEquals("Check if the string is restored.", "Is this text restored?", newtext);
+ }
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
index 9dcce92..a0b5346 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/AdapterInputConnectionTest.java
@@ -8,6 +8,7 @@ import android.content.Context;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.test.suitebuilder.annotation.MediumTest;
+import android.text.Editable;
import android.view.View;
import android.view.inputmethod.EditorInfo;
@@ -25,6 +26,7 @@ public class AdapterInputConnectionTest extends ContentShellTestBase {
private AdapterInputConnection mConnection;
private TestInputMethodManagerWrapper mWrapper;
+ private Editable mEditable;
@Override
public void setUp() throws Exception {
@@ -35,8 +37,9 @@ public class AdapterInputConnectionTest extends ContentShellTestBase {
ImeAdapterDelegate delegate = new TestImeAdapterDelegate();
ImeAdapter imeAdapter = new TestImeAdapter(mWrapper, delegate);
EditorInfo info = new EditorInfo();
+ mEditable = Editable.Factory.getInstance().newEditable("");
mConnection = new AdapterInputConnection(
- getActivity().getActiveContentView(), imeAdapter, info);
+ getActivity().getActiveContentView(), imeAdapter, mEditable, info);
}
@MediumTest
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 da5bd3d..c00b83b 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
@@ -10,6 +10,7 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Editable;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
@@ -451,16 +452,17 @@ public class ImeTest extends ContentShellTestBase {
ImeAdapter.AdapterInputConnectionFactory {
@Override
public AdapterInputConnection get(View view, ImeAdapter imeAdapter,
- EditorInfo outAttrs) {
- return new TestAdapterInputConnection(view, imeAdapter, outAttrs);
+ Editable editable, EditorInfo outAttrs) {
+ return new TestAdapterInputConnection(view, imeAdapter, editable, outAttrs);
}
}
private static class TestAdapterInputConnection extends AdapterInputConnection {
private final ArrayList<TestImeState> mImeUpdateQueue = new ArrayList<TestImeState>();
- public TestAdapterInputConnection(View view, ImeAdapter imeAdapter, EditorInfo outAttrs) {
- super(view, imeAdapter, outAttrs);
+ public TestAdapterInputConnection(View view, ImeAdapter imeAdapter,
+ Editable editable, EditorInfo outAttrs) {
+ super(view, imeAdapter, editable, outAttrs);
}
@Override