diff options
author | Alexandre Elias <aelias@chromium.org> | 2015-03-04 13:49:30 -0800 |
---|---|---|
committer | Alexandre Elias <aelias@chromium.org> | 2015-03-04 21:50:39 +0000 |
commit | 373a6eea7fb583fe839ed9e7c427bec99107edf8 (patch) | |
tree | 3f269380cde032f0ee8a793d2ab3098c8b7ccdfd | |
parent | a1f79207c7a6aab957ecb3a11789f8afdfd06877 (diff) | |
download | chromium_src-373a6eea7fb583fe839ed9e7c427bec99107edf8.zip chromium_src-373a6eea7fb583fe839ed9e7c427bec99107edf8.tar.gz chromium_src-373a6eea7fb583fe839ed9e7c427bec99107edf8.tar.bz2 |
Revert "Revert "Fix problem with accents caused by race condition.""
This reverts commit 694ea4e297e4faf085c61e097122da8bc5ae94fa.
This revert turned out to not address the bug it was intended to
(addressed with https://codereview.chromium.org/979683002 instead) so
return the 41 branch to its previous state here.
BUG=453499
Review URL: https://codereview.chromium.org/984453002
Cr-Commit-Position: refs/branch-heads/2272@{#405}
Cr-Branched-From: 827a380cfdb31aa54c8d56e63ce2c3fd8c3ba4d4-refs/heads/master@{#310958}
3 files changed, 144 insertions, 21 deletions
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 1935025..600af68 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 @@ -10,6 +10,7 @@ import android.text.InputType; import android.text.Selection; import android.text.TextUtils; import android.util.Log; +import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.BaseInputConnection; @@ -28,6 +29,7 @@ import org.chromium.ui.base.ime.TextInputType; public class AdapterInputConnection extends BaseInputConnection { private static final String TAG = "AdapterInputConnection"; private static final boolean DEBUG = false; + private static final int NO_ACCENT = 0; /** * Selection value should be -1 if not known. See EditorInfo.java for details. */ @@ -40,6 +42,7 @@ public class AdapterInputConnection extends BaseInputConnection { private boolean mSingleLine; private int mNumNestedBatchEdits = 0; + private int mPendingAccent; private int mLastUpdateSelectionStart = INVALID_SELECTION; private int mLastUpdateSelectionEnd = INVALID_SELECTION; @@ -126,6 +129,16 @@ public class AdapterInputConnection extends BaseInputConnection { updateSelectionIfRequired(); } + public static int maybeAddAccentToCharacter(int accentChar, int unicodeChar) { + if (accentChar != NO_ACCENT) { + int combinedChar = KeyEvent.getDeadChar(accentChar, unicodeChar); + if (combinedChar != 0) { + return combinedChar; + } + } + return unicodeChar; + } + /** * Updates the AdapterInputConnection's internal representation of the text being edited and * its selection and composition properties. The resulting Editable is accessible through the @@ -216,6 +229,8 @@ public class AdapterInputConnection extends BaseInputConnection { mLastUpdateSelectionEnd = selectionEnd; mLastUpdateCompositionStart = compositionStart; mLastUpdateCompositionEnd = compositionEnd; + // Change in selection or cursor position invalidates any pending accent. + mPendingAccent = NO_ACCENT; } /** @@ -370,6 +385,22 @@ public class AdapterInputConnection extends BaseInputConnection { if (DEBUG) { Log.w(TAG, "sendKeyEvent [" + event.getAction() + "] [" + event.getKeyCode() + "]"); } + + // Short-cut modifier keys so they're not affected by accents. + if (KeyEvent.isModifierKey(event.getKeyCode())) { + return mImeAdapter.translateAndSendNativeEvents(event, NO_ACCENT); + } + + // Some keys we just want to pass events straight through. This allows + // proper "repeating key" behavior with physical keyboards. + int eventKeyCode = event.getKeyCode(); + if (eventKeyCode == KeyEvent.KEYCODE_DEL || eventKeyCode == KeyEvent.KEYCODE_FORWARD_DEL) { + mPendingAccent = 0; + return mImeAdapter.translateAndSendNativeEvents(event, NO_ACCENT); + } + + int unicodeChar = event.getUnicodeChar(); + // If this is a key-up, and backspace/del or if the key has a character representation, // need to update the underlying Editable (i.e. the local representation of the text // being edited). @@ -380,26 +411,24 @@ public class AdapterInputConnection extends BaseInputConnection { } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) { deleteSurroundingText(0, 1); return true; - } else { - int unicodeChar = event.getUnicodeChar(); - if (unicodeChar != 0) { - int selectionStart = Selection.getSelectionStart(mEditable); - int selectionEnd = Selection.getSelectionEnd(mEditable); - if (selectionStart > selectionEnd) { - int temp = selectionStart; - selectionStart = selectionEnd; - selectionEnd = temp; - } - mEditable.replace(selectionStart, selectionEnd, - Character.toString((char) unicodeChar)); + } else if (unicodeChar != 0) { + int selectionStart = Selection.getSelectionStart(mEditable); + int selectionEnd = Selection.getSelectionEnd(mEditable); + if (selectionStart > selectionEnd) { + int temp = selectionStart; + selectionStart = selectionEnd; + selectionEnd = temp; } + int combinedChar = maybeAddAccentToCharacter(mPendingAccent, unicodeChar); + mEditable.replace(selectionStart, selectionEnd, + Character.toString((char) combinedChar)); } } else if (event.getAction() == KeyEvent.ACTION_DOWN) { // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed. if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { beginBatchEdit(); finishComposingText(); - mImeAdapter.translateAndSendNativeEvents(event); + mImeAdapter.translateAndSendNativeEvents(event, 0); endBatchEdit(); return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { @@ -408,7 +437,46 @@ public class AdapterInputConnection extends BaseInputConnection { return true; } } - mImeAdapter.translateAndSendNativeEvents(event); + + // Physical keyboards also have their events come through here though not + // by BaseInputConnection. In order to support "accent" key sequences + // such as "~n" or "^o" we have to record that one has been pressed + // and, if an accentable letter follows, delete the accent glyph and + // insert the composed character. + + // Copy class variable to local because class version may get indirectly + // cleared by the deleteSurroundingText() call below. + int pendingAccent = mPendingAccent; + int nextAccent = mPendingAccent; + + if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) { + pendingAccent = NO_ACCENT; + nextAccent = unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK; + } else if (pendingAccent != NO_ACCENT) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + int combined = KeyEvent.getDeadChar(pendingAccent, unicodeChar); + if (combined != 0) { + // Previous accent combines with new character to create + // a new accented character. First delete the displayed + // accent so it appears overwritten by the composition. + super.deleteSurroundingText(1, 0); + mImeAdapter.deleteSurroundingText(1, 0); + } else { + // Previous accent doesn't combine with this character + // so assume both are completely independent. + pendingAccent = NO_ACCENT; + nextAccent = NO_ACCENT; + } + } + + if (event.getAction() == KeyEvent.ACTION_UP) { + // Forget accent after release of key being accented. + nextAccent = NO_ACCENT; + } + } + + mImeAdapter.translateAndSendNativeEvents(event, pendingAccent); + mPendingAccent = nextAccent; return true; } @@ -450,6 +518,7 @@ public class AdapterInputConnection extends BaseInputConnection { if (DEBUG) Log.w(TAG, "restartInput"); getInputMethodManagerWrapper().restartInput(mInternalView); mNumNestedBatchEdits = 0; + mPendingAccent = NO_ACCENT; } /** 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 d7dba0e..f8890a6 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 @@ -322,7 +322,12 @@ public class ImeAdapter { } public boolean dispatchKeyEvent(KeyEvent event) { - return translateAndSendNativeEvents(event); + // Physical keyboards have their events come through here instead of + // AdapterInputConnection. + if (mInputConnection != null) { + return mInputConnection.sendKeyEvent(event); + } + return translateAndSendNativeEvents(event, 0); } private int shouldSendKeyEventWithKeyCode(String text) { @@ -391,11 +396,11 @@ public class ImeAdapter { translateAndSendNativeEvents(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - flags)); + flags), 0); translateAndSendNativeEvents(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - flags)); + flags), 0); } // Calls from Java to C++ @@ -430,9 +435,9 @@ public class ImeAdapter { // composition below. if (keyCode > 0 && isCommit && mLastComposeText == null && textStr.length() == 1) { mLastSyntheticKeyCode = keyCode; - return translateAndSendNativeEvents(keyEvent) + return translateAndSendNativeEvents(keyEvent, 0) && translateAndSendNativeEvents( - KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP)); + KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP), 0); } // FIXME: Use WebTextInputFlags.AutocompleteOff. We need this hack to enable merge into @@ -494,7 +499,7 @@ public class ImeAdapter { nativeFinishComposingText(mNativeImeAdapterAndroid); } - boolean translateAndSendNativeEvents(KeyEvent event) { + boolean translateAndSendNativeEvents(KeyEvent event, int accentChar) { if (mNativeImeAdapterAndroid == 0) return false; int action = event.getAction(); @@ -512,9 +517,11 @@ public class ImeAdapter { return false; } mViewEmbedder.onImeEvent(); + int unicodeChar = AdapterInputConnection.maybeAddAccentToCharacter( + accentChar, event.getUnicodeChar()); return nativeSendKeyEvent(mNativeImeAdapterAndroid, event, event.getAction(), getModifiers(event.getMetaState()), event.getEventTime(), event.getKeyCode(), - /*isSystemKey=*/false, event.getUnicodeChar()); + /*isSystemKey=*/false, unicodeChar); } boolean sendSyntheticKeyEvent(int eventType, long timestampMs, int keyCode, int modifiers, 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 c5c3e04..50828fd 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 @@ -600,6 +600,44 @@ public class ImeTest extends ContentShellTestBase { @SmallTest @Feature({"TextInput", "Main"}) + public void testAccentKeyCodesFromPhysicalKeyboard() throws Throwable { + DOMUtils.focusNode(mWebContents, "textarea"); + assertWaitForKeyboardStatus(true); + + // The calls below are a reflection of what a physical keyboard sends when the noted + // key is pressed on the device. Exercise care when altering to make sure that the test + // reflects reality. + mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); + waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); + + // H + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_H)); + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_H)); + assertEquals("h", mConnection.getTextBeforeCursor(9, 0)); + + // I + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I)); + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I)); + assertEquals("hi", mConnection.getTextBeforeCursor(9, 0)); + + // ALT-I (circomflex accent key on virtual keyboard) + dispatchKeyEvent( + mConnection, new KeyEvent( + 0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON)); + dispatchKeyEvent( + mConnection, new KeyEvent( + 0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_I, 0, KeyEvent.META_ALT_ON)); + assertEquals("hiˆ", mConnection.getTextBeforeCursor(9, 0)); + + // O (accented key) + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_O)); + assertEquals("hi", mConnection.getTextBeforeCursor(9, 0)); + dispatchKeyEvent(mConnection, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_O)); + assertEquals("hiô", mConnection.getTextBeforeCursor(9, 0)); + } + + @SmallTest + @Feature({"TextInput", "Main"}) public void testSetComposingRegionOutOfBounds() throws Throwable { DOMUtils.focusNode(mWebContents, "textarea"); assertWaitForKeyboardStatus(true); @@ -918,6 +956,15 @@ public class ImeTest extends ContentShellTestBase { }); } + private void dispatchKeyEvent(final AdapterInputConnection connection, final KeyEvent event) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + mImeAdapter.dispatchKeyEvent(event); + } + }); + } + private static class TestAdapterInputConnectionFactory extends ImeAdapter.AdapterInputConnectionFactory { @Override |