Bug 1058136 - Send separate compose event for composing text; r=cpeterson
☠☠ backed out by 7991f98b920b ☠ ☠
authorJim Chen <nchen@mozilla.com>
Fri, 29 Aug 2014 14:56:42 -0400
changeset 224133 032246f68eaac7eec38558a75cae9101bdf08665
parent 224132 7e550234773177c55c88f19dd7f1be5b0fe18a63
child 224134 713e1d6f09c48f73f6ef66c6d88076530656ddae
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpeterson
bugs1058136
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1058136 - Send separate compose event for composing text; r=cpeterson
mobile/android/base/GeckoEditable.java
mobile/android/base/GeckoEvent.java
mobile/android/base/GeckoInputConnection.java
--- a/mobile/android/base/GeckoEditable.java
+++ b/mobile/android/base/GeckoEditable.java
@@ -62,17 +62,17 @@ interface GeckoEditableListener {
     final int IME_STATE_ENABLED = 1;
     final int IME_STATE_PASSWORD = 2;
     final int IME_STATE_PLUGIN = 3;
 
     void notifyIME(int type);
     void notifyIMEContext(int state, String typeHint,
                           String modeHint, String actionHint);
     void onSelectionChange(int start, int end);
-    void onTextChange(String text, int start, int oldEnd, int newEnd);
+    void onTextChange(CharSequence text, int start, int oldEnd, int newEnd);
 }
 
 /*
    GeckoEditable implements only some functions of Editable
    The field mText contains the actual underlying
    SpannableStringBuilder/Editable that contains our text.
 */
 final class GeckoEditable
@@ -126,16 +126,18 @@ final class GeckoEditable
         // For Editable.setSpan() call; use with IME_SYNCHRONIZE
         static final int TYPE_SET_SPAN = 3;
         // For Editable.removeSpan() call; use with IME_SYNCHRONIZE
         static final int TYPE_REMOVE_SPAN = 4;
         // For focus events (in notifyIME); use with IME_ACKNOWLEDGE_FOCUS
         static final int TYPE_ACKNOWLEDGE_FOCUS = 5;
         // For switching handler; use with IME_SYNCHRONIZE
         static final int TYPE_SET_HANDLER = 6;
+        // For Editable.replace() call involving compositions; use with IME_COMPOSE_TEXT
+        static final int TYPE_COMPOSE_TEXT = 7;
 
         final int mType;
         int mStart;
         int mEnd;
         CharSequence mSequence;
         Object mSpanObject;
         int mSpanFlags;
         boolean mShouldUpdate;
@@ -145,17 +147,32 @@ final class GeckoEditable
             mType = type;
         }
 
         static Action newReplaceText(CharSequence text, int start, int end) {
             if (start < 0 || start > end) {
                 throw new IllegalArgumentException(
                     "invalid replace text offsets: " + start + " to " + end);
             }
-            final Action action = new Action(TYPE_REPLACE_TEXT);
+
+            int actionType = TYPE_REPLACE_TEXT;
+
+            if (text instanceof Spanned) {
+                final Spanned spanned = (Spanned) text;
+                final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
+
+                for (Object span : spans) {
+                    if ((spanned.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
+                        actionType = TYPE_COMPOSE_TEXT;
+                        break;
+                    }
+                }
+            }
+
+            final Action action = new Action(actionType);
             action.mSequence = text;
             action.mStart = start;
             action.mEnd = end;
             return action;
         }
 
         static Action newSetSelection(int start, int end) {
             // start == -1 when the start offset should remain the same
@@ -235,16 +252,22 @@ final class GeckoEditable
             case Action.TYPE_SET_SELECTION:
             case Action.TYPE_SET_SPAN:
             case Action.TYPE_REMOVE_SPAN:
             case Action.TYPE_SET_HANDLER:
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(
                         GeckoEvent.ImeAction.IME_SYNCHRONIZE));
                 break;
 
+            case Action.TYPE_COMPOSE_TEXT:
+                // Send different event for composing text.
+                GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEComposeEvent(
+                        action.mStart, action.mEnd, action.mSequence.toString()));
+                return;
+
             case Action.TYPE_REPLACE_TEXT:
                 // try key events first
                 sendCharKeyEvents(action);
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEReplaceEvent(
                         action.mStart, action.mEnd, action.mSequence.toString()));
                 break;
 
             case Action.TYPE_ACKNOWLEDGE_FOCUS:
@@ -680,16 +703,22 @@ final class GeckoEditable
         }
         final Action action = mActionQueue.peek();
 
         if (DEBUG) {
             Log.d(LOGTAG, "reply: Action(" +
                           getConstantName(Action.class, "TYPE_", action.mType) + ")");
         }
         switch (action.mType) {
+        case Action.TYPE_COMPOSE_TEXT:
+            // Compositions don't trigger text change notification, so notify manually.
+            onTextChange(action.mSequence, action.mStart, action.mEnd,
+                         action.mStart + action.mSequence.length());
+            break;
+
         case Action.TYPE_SET_SELECTION:
             final int len = mText.length();
             final int curStart = Selection.getSelectionStart(mText);
             final int curEnd = Selection.getSelectionEnd(mText);
             // start == -1 when the start offset should remain the same
             // end == -1 when the end offset should remain the same
             final int selStart = Math.min(action.mStart < 0 ? curStart : action.mStart, len);
             final int selEnd = Math.min(action.mEnd < 0 ? curEnd : action.mEnd, len);
@@ -867,17 +896,17 @@ final class GeckoEditable
     private void geckoReplaceText(int start, int oldEnd, CharSequence newText) {
         // Don't use replace() because Gingerbread has a bug where if the replaced text
         // has the same spans as the original text, the spans will end up being deleted
         mText.delete(start, oldEnd);
         mText.insert(start, newText);
     }
 
     @Override
-    public void onTextChange(final String text, final int start,
+    public void onTextChange(final CharSequence text, final int start,
                       final int unboundedOldEnd, final int unboundedNewEnd) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             StringBuilder sb = new StringBuilder("onTextChange(");
             debugAppend(sb, text);
             sb.append(", ").append(start).append(", ")
                 .append(unboundedOldEnd).append(", ")
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -142,17 +142,18 @@ public class GeckoEvent {
     @JNITarget
     public enum ImeAction {
         IME_SYNCHRONIZE(0),
         IME_REPLACE_TEXT(1),
         IME_SET_SELECTION(2),
         IME_ADD_COMPOSITION_RANGE(3),
         IME_UPDATE_COMPOSITION(4),
         IME_REMOVE_COMPOSITION(5),
-        IME_ACKNOWLEDGE_FOCUS(6);
+        IME_ACKNOWLEDGE_FOCUS(6),
+        IME_COMPOSE_TEXT(7);
 
         public final int value;
 
         private ImeAction(int value) {
             this.value = value;
         }
     }
 
@@ -595,20 +596,27 @@ public class GeckoEvent {
     }
 
     public static GeckoEvent createIMEKeyEvent(KeyEvent k) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_KEY_EVENT);
         event.initKeyEvent(k, 0);
         return event;
     }
 
-    public static GeckoEvent createIMEReplaceEvent(int start, int end,
-                                                   String text) {
+    public static GeckoEvent createIMEReplaceEvent(int start, int end, String text) {
+        return createIMETextEvent(false, start, end, text);
+    }
+
+    public static GeckoEvent createIMEComposeEvent(int start, int end, String text) {
+        return createIMETextEvent(true, start, end, text);
+    }
+
+    private static GeckoEvent createIMETextEvent(boolean compose, int start, int end, String text) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_EVENT);
-        event.mAction = ImeAction.IME_REPLACE_TEXT.value;
+        event.mAction = (compose ? ImeAction.IME_COMPOSE_TEXT : ImeAction.IME_REPLACE_TEXT).value;
         event.mStart = start;
         event.mEnd = end;
         event.mCharacters = text;
         return event;
     }
 
     public static GeckoEvent createIMESelectEvent(int start, int end) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_EVENT);
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -425,17 +425,17 @@ class GeckoInputConnection
         }
         mBatchSelectionChanged = false;
         mBatchTextChanged = false;
 
         // Do not reset mIMEState here; see comments in notifyIMEContext
     }
 
     @Override
-    public void onTextChange(String text, int start, int oldEnd, int newEnd) {
+    public void onTextChange(CharSequence text, int start, int oldEnd, int newEnd) {
 
         if (mUpdateRequest == null) {
             // Android always expects selection updates when not in extracted mode;
             // in extracted mode, the selection is reported through updateExtractedText
             final Editable editable = getEditable();
             if (editable != null) {
                 onSelectionChange(Selection.getSelectionStart(editable),
                                   Selection.getSelectionEnd(editable));