Bug 1241558 - Don't ignore Gecko selection when Gecko commits composition; r=esawin

When Java is changing the composition, we should ignore the Gecko
selection. However, when Gecko is committing its composition, we should
not be ignoring the corresponding Gecko selection change. In other
words, we should only ignore selection changes when we know the change
is from Java.
This commit is contained in:
Jim Chen 2016-02-09 23:36:41 -05:00
parent d0f6c7140c
commit 45c8ed0b53
2 changed files with 67 additions and 29 deletions

View File

@ -150,18 +150,20 @@ final class GeckoEditable extends JNIObject
replied, the action is removed from the queue
*/
private static final class Action {
// For input events (keypress, etc.); use with IME_SYNCHRONIZE
// For input events (keypress, etc.); use with onImeSynchronize
static final int TYPE_EVENT = 0;
// For Editable.replace() call; use with IME_REPLACE_TEXT
// For Editable.replace() call; use with onImeReplaceText
static final int TYPE_REPLACE_TEXT = 1;
// For Editable.setSpan() call; use with IME_SYNCHRONIZE
// For Editable.setSpan() call; use with onImeSynchronize
static final int TYPE_SET_SPAN = 2;
// For Editable.removeSpan() call; use with IME_SYNCHRONIZE
// For Editable.removeSpan() call; use with onImeSynchronize
static final int TYPE_REMOVE_SPAN = 3;
// For focus events (in notifyIME); use with IME_ACKNOWLEDGE_FOCUS
// For focus events (in notifyIME); use with onImeAcknowledgeFocus
static final int TYPE_ACKNOWLEDGE_FOCUS = 4;
// For switching handler; use with IME_SYNCHRONIZE
// For switching handler; use with onImeSynchronize
static final int TYPE_SET_HANDLER = 5;
// For updating composition; use with onImeUpdateComposition
static final int TYPE_UPDATE_COMPOSITION = 6;
final int mType;
boolean mUpdateComposition;
@ -215,6 +217,13 @@ final class GeckoEditable extends JNIObject
action.mHandler = handler;
return action;
}
static Action newUpdateComposition(int start, int end) {
final Action action = new Action(TYPE_UPDATE_COMPOSITION);
action.mStart = start;
action.mEnd = end;
return action;
}
}
/* Queue of editing actions sent to Gecko thread that
@ -261,8 +270,10 @@ final class GeckoEditable extends JNIObject
case Action.TYPE_REPLACE_TEXT:
// Because we get composition styling here essentially for free,
// we don't need to check if we're in batch mode.
if (!icMaybeSendComposition(
if (icMaybeSendComposition(
action.mSequence, /* useEntireText */ true, /* notifyGecko */ false)) {
mNeedCompositionUpdate = false;
} else {
// Since we don't have a composition, we can try sending key events.
sendCharKeyEvents(action);
}
@ -273,6 +284,10 @@ final class GeckoEditable extends JNIObject
onImeAcknowledgeFocus();
break;
case Action.TYPE_UPDATE_COMPOSITION:
onImeUpdateComposition(action.mStart, action.mEnd);
break;
default:
throw new IllegalStateException("Action not processed");
}
@ -519,7 +534,8 @@ final class GeckoEditable extends JNIObject
if (found) {
icSendComposition(text, selStart, selEnd, composingStart, composingEnd);
if (notifyGecko) {
onImeUpdateComposition(composingStart, composingEnd);
mActionQueue.offer(Action.newUpdateComposition(
composingStart, composingEnd));
}
return true;
}
@ -527,7 +543,7 @@ final class GeckoEditable extends JNIObject
if (notifyGecko) {
// Set the selection by using a composition without ranges
onImeUpdateComposition(selStart, selEnd);
mActionQueue.offer(Action.newUpdateComposition(selStart, selEnd));
}
if (DEBUG) {
@ -1003,7 +1019,7 @@ final class GeckoEditable extends JNIObject
}
}
private boolean isSameText(int start, int oldEnd, CharSequence newText) {
private boolean geckoIsSameText(int start, int oldEnd, CharSequence newText) {
return oldEnd - start == newText.length() &&
TextUtils.regionMatches(mText, start, newText, 0, oldEnd - start);
}
@ -1051,15 +1067,29 @@ final class GeckoEditable extends JNIObject
// Try to preserve both old spans and new spans in action.mSequence.
// indexInText is where we can find waction.mSequence within the passed in text.
final int indexInText = TextUtils.indexOf(text, action.mSequence);
final int startWithinText = action.mStart - start;
int indexInText = TextUtils.indexOf(text, action.mSequence, startWithinText);
if (indexInText < 0 && startWithinText >= action.mSequence.length()) {
indexInText = text.toString().lastIndexOf(action.mSequence.toString(),
startWithinText);
}
if (indexInText < 0) {
// Text was changed from under us. We are force to discard any new spans.
// Text was changed from under us. We are forced to discard any new spans.
geckoReplaceText(start, oldEnd, text);
// Don't ignore the next selection change because we are forced to re-sync
// with Gecko here.
mIgnoreSelectionChange = false;
} else if (indexInText == 0 && text.length() == action.mSequence.length()) {
// The new text exactly matches our sequence, so do a direct replace.
geckoReplaceText(start, oldEnd, action.mSequence);
// Ignore the next selection change because the selection change is a
// side-effect of the replace-text event we sent.
mIgnoreSelectionChange = true;
} else {
// The sequence is embedded within the changed text, so we have to perform
// replacement in parts. First replace part of text before the sequence.
@ -1076,21 +1106,16 @@ final class GeckoEditable extends JNIObject
text.subSequence(actionEnd - start, text.length()));
}
// Ignore the next selection change because the selection change is a
// side-effect of the replace-text event we sent.
mIgnoreSelectionChange = true;
} else if (geckoIsSameText(start, oldEnd, text)) {
// Nothing to do because the text is the same. This could happen when
// the composition is updated for example, in which case we want to keep the
// Java selection.
mIgnoreSelectionChange = mIgnoreSelectionChange ||
(action != null && action.mType == Action.TYPE_UPDATE_COMPOSITION);
return;
} else {
// Gecko side initiated the text change.
if (isSameText(start, oldEnd, text)) {
// Nothing to do because the text is the same. This could happen when
// the composition is updated for example. Ignore the next selection
// change because the selection change is a side-effect of the
// update-composition event we sent.
mIgnoreSelectionChange = true;
return;
}
geckoReplaceText(start, oldEnd, text);
}

View File

@ -335,6 +335,15 @@ public:
const InputContextAction& aAction);
InputContext GetInputContext();
// RAII helper class that automatically sends an event reply through
// OnImeSynchronize, as required by events like OnImeReplaceText.
class AutoIMESynchronize {
GeckoViewSupport* const mGVS;
public:
AutoIMESynchronize(GeckoViewSupport* gvs) : mGVS(gvs) {}
~AutoIMESynchronize() { mGVS->OnImeSynchronize(); }
};
// Handle an Android KeyEvent.
void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
int32_t aMetaState, int64_t aTime, int32_t aUnicodeChar,
@ -2848,9 +2857,11 @@ nsWindow::GeckoViewSupport::OnImeAcknowledgeFocus()
{
MOZ_ASSERT(mIMEMaskEventsCount > 0);
AutoIMESynchronize as(this);
if (--mIMEMaskEventsCount > 0) {
// Still not focused; reply to events, but don't do anything else.
return OnImeSynchronize();
return;
}
// The focusing handshake sequence is complete, and Java is waiting
@ -2866,16 +2877,17 @@ nsWindow::GeckoViewSupport::OnImeAcknowledgeFocus()
notification.mTextChangeData.mRemovedEndOffset =
notification.mTextChangeData.mAddedEndOffset = INT32_MAX / 2;
NotifyIME(notification);
OnImeSynchronize();
}
void
nsWindow::GeckoViewSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
jni::String::Param aText)
{
AutoIMESynchronize as(this);
if (mIMEMaskEventsCount > 0) {
// Not focused; still reply to events, but don't do anything else.
return OnImeSynchronize();
return;
}
/*
@ -2927,7 +2939,7 @@ nsWindow::GeckoViewSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
window.DispatchEvent(event, status);
}
mIMEKeyEvents.Clear();
return OnImeSynchronize();
return;
}
{
@ -2984,7 +2996,6 @@ nsWindow::GeckoViewSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
if (mInputContext.mMayBeIMEUnaware) {
SendIMEDummyKeyEvents();
}
OnImeSynchronize();
}
void
@ -3017,6 +3028,8 @@ nsWindow::GeckoViewSupport::OnImeAddCompositionRange(
void
nsWindow::GeckoViewSupport::OnImeUpdateComposition(int32_t aStart, int32_t aEnd)
{
AutoIMESynchronize as(this);
if (mIMEMaskEventsCount > 0) {
// Not focused.
return;