diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java
index 61bc11b398c96f54859413118acb7874659a077c..8c6b3d949fb0f2893ecbecca7aa1d26c7516b4e2 100644
--- a/java/src/com/android/inputmethod/event/CombinerChain.java
+++ b/java/src/com/android/inputmethod/event/CombinerChain.java
@@ -81,11 +81,11 @@ public class CombinerChain {
     }
 
     /**
-     * Pass a new event through the whole chain.
+     * Process an event through the combining chain, and return a processed event to apply.
      * @param previousEvents the list of previous events in this composition
      * @param newEvent the new event to process
      */
-    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+    public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
         final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents);
         Event event = newEvent;
         for (final Combiner combiner : mCombiners) {
@@ -97,6 +97,14 @@ public class CombinerChain {
                 break;
             }
         }
+        return event;
+    }
+
+    /**
+     * Apply a processed event.
+     * @param event the event to be applied
+     */
+    public void applyProcessedEvent(final Event event) {
         if (null != event) {
             // TODO: figure out the generic way of doing this
             if (Constants.CODE_DELETE == event.mKeyCode) {
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index cdd7822441f377ae2c5132dc9b551dcaa28668c5..2485cf41172aab0b2aca97a530ad7ef1208ea667 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -175,20 +175,30 @@ public final class WordComposer {
     }
 
     /**
-     * Process an input event.
+     * Process an event and return an event, and return a processed event to apply.
+     * @param event the unprocessed event.
+     * @return the processed event.
+     */
+    public Event processEvent(final Event event) {
+        final Event processedEvent = mCombinerChain.processEvent(mEvents, event);
+        mEvents.add(event);
+        return processedEvent;
+    }
+
+    /**
+     * Apply a processed input event.
      *
      * All input events should be supported, including software/hardware events, characters as well
      * as deletions, multiple inputs and gestures.
      *
-     * @param event the event to process.
+     * @param event the event to apply.
      */
-    public void processEvent(final Event event) {
+    public void applyProcessedEvent(final Event event) {
         final int primaryCode = event.mCodePoint;
         final int keyX = event.mX;
         final int keyY = event.mY;
         final int newIndex = size();
-        mCombinerChain.processEvent(mEvents, event);
-        mEvents.add(event);
+        mCombinerChain.applyProcessedEvent(event);
         refreshTypedWordCache();
         mCursorPositionWithinWord = mCodePointSize;
         // We may have deleted the last one.
@@ -281,7 +291,9 @@ public final class WordComposer {
             final int codePoint = Character.codePointAt(word, i);
             // We don't want to override the batch input points that are held in mInputPointers
             // (See {@link #add(int,int,int)}).
-            processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
+            final Event processedEvent =
+                    processEvent(Event.createEventForCodePointFromUnknownSource(codePoint));
+            applyProcessedEvent(processedEvent);
         }
     }
 
@@ -295,9 +307,11 @@ public final class WordComposer {
         reset();
         final int length = codePoints.length;
         for (int i = 0; i < length; ++i) {
-            processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
+            final Event processedEvent =
+                    processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i],
                     CoordinateUtils.xFromArray(coordinates, i),
                     CoordinateUtils.yFromArray(coordinates, i)));
+            applyProcessedEvent(processedEvent);
         }
         mIsResumed = true;
     }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 74d8799199eee37de4edaaf4536dda48a32f4474..bb2d304a60d951ea857be997ef4cfec30e02e2ed 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -762,7 +762,8 @@ public final class InputLogic {
             resetComposingState(false /* alsoResetLastComposedWord */);
         }
         if (isComposingWord) {
-            mWordComposer.processEvent(inputTransaction.mEvent);
+            final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
+            mWordComposer.applyProcessedEvent(processedEvent);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.isSingleLetter()) {
                 mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
@@ -933,7 +934,8 @@ public final class InputLogic {
                     mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion);
                 }
             } else {
-                mWordComposer.processEvent(inputTransaction.mEvent);
+                final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
+                mWordComposer.applyProcessedEvent(processedEvent);
             }
             if (mWordComposer.isComposingWord()) {
                 mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
index 458f22c45643a513fc7d862f4cd21907de18a0d2..c4457a1b7ffa3f6e62f2e98ce60918a9ba5d6140 100644
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
@@ -31,8 +31,12 @@ public class CombinerChain {
         mComposingWord = new StringBuilder(initialText);
     }
 
-    public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
-        mComposingWord.append(newEvent.getTextToCommit());
+    public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
+        return newEvent;
+    }
+
+    public void applyProcessedEvent(final Event event) {
+        mComposingWord.append(event.getTextToCommit());
     }
 
     public CharSequence getComposingWordWithCombiningFeedback() {