Bug 1343075 - Use GeckoEditableSupport from PuppetWidget; r=masayuki r=rbarker r=snorp r=esawin
authorJim Chen <nchen@mozilla.com>
Tue, 07 Mar 2017 22:34:39 -0500
changeset 346422 f3b22a31ea36bbf4d620c0dcdf7f6b831ea7b24b
parent 346421 9061c4b3f7aef7f6535828f05816159dae80763b
child 346423 4e90c48f26179dc59150ae002a46666014fd3cbc
push id87803
push usernchen@mozilla.com
push dateWed, 08 Mar 2017 03:35:34 +0000
treeherdermozilla-inbound@f3b22a31ea36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, rbarker, snorp, esawin
bugs1343075
milestone55.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 1343075 - Use GeckoEditableSupport from PuppetWidget; r=masayuki r=rbarker r=snorp r=esawin Bug 1343075 - 1a. Add TextEventDispatcherListener::GetIMEUpdatePreference; r=masayuki Add a GetIMEUpdatePreference method to TextEventDispatcherListener to optionally control which IME notifications are received by NotifyIME. This patch also makes nsBaseWidget forward its GetIMEUpdatePreference call to the widget's native TextEventDispatcherListener. Bug 1343075 - 1b. Implement GetIMEUpdatePreference for all TextEventDispatcherListener; r=masayuki This patch implements GetIMEUpdatePreference for all TextEventDispatcherListener implementations, by moving previous implementations of nsIWidget::GetIMEUpdatePreference. Bug 1343075 - 2. Allow setting a PuppetWidget's native TextEventDispatcherListener; r=masayuki In PuppetWidget, add getter and setter for the widget's native TextEventDispatcherListener. This allows overriding of PuppetWidget's default IME handling. For example, on Android, the PuppetWidget's native TextEventDispatcherListener will communicate directly with Java IME code in the main process. Bug 1343075 - 3. Add AIDL interface for main process; r=rbarker Add AIDL definition and implementation for an interface for the main process that child processes can access. Bug 1343075 - 4. Set Gecko thread JNIEnv for child process; r=snorp Add a JNIEnv* parameter to XRE_SetAndroidChildFds, which is used to set the Gecko thread JNIEnv for child processes. XRE_SetAndroidChildFds is the only Android-specific entry point for child processes, so I think it's the most logical place to initialize JNI. Bug 1343075 - 5. Support multiple remote GeckoEditableChild; r=esawin Support remote GeckoEditableChild instances that are created in the content processes and connect to the parent process GeckoEditableParent through binders. Support having multiple GeckoEditableChild instances in GeckoEditable by keeping track of which child is currently focused, and only allow calls to/from the focused child by using access tokens. Bug 1343075 - 6. Add method to get GeckoEditableParent instance; r=esawin Add IProcessManager.getEditableParent, which a content process can call to get the GeckoEditableParent instance that corresponds to a given content process tab, from the main process. Bug 1343075 - 7. Support GeckoEditableSupport in content processes; r=esawin Support creating and running GeckoEditableSupport attached to a PuppetWidget in content processes. Because we don't know PuppetWidget's lifetime as well as nsWindow's, when attached to PuppetWidget, we need to attach/detach our native object on focus/blur, respectively. Bug 1343075 - 8. Connect GeckoEditableSupport on PuppetWidget creation; r=esawin Listen to the "tab-child-created" notification and attach our content process GeckoEditableSupport to the new PuppetWidget. Bug 1343075 - 9. Update auto-generated bindings; r=me
dom/base/TextInputProcessor.cpp
dom/base/TextInputProcessor.h
mobile/android/base/Makefile.in
mobile/android/base/moz.build
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/IGeckoEditableParent.aidl
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IProcessManager.aidl
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableChild.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
mozglue/android/APKOpen.cpp
toolkit/xre/Bootstrap.cpp
toolkit/xre/Bootstrap.h
toolkit/xre/nsEmbedFunctions.cpp
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/TextEventDispatcherListener.h
widget/android/GeckoEditableSupport.cpp
widget/android/GeckoEditableSupport.h
widget/android/GeckoProcessManager.h
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/jni/Utils.cpp
widget/android/nsAppShell.cpp
widget/android/nsAppShell.h
widget/android/nsWindow.cpp
widget/android/nsWindow.h
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/gtk/IMContextWrapper.cpp
widget/gtk/IMContextWrapper.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/windows/WinTextEventDispatcherListener.cpp
widget/windows/WinTextEventDispatcherListener.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
xpcom/build/nsXULAppAPI.h
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -693,16 +693,23 @@ TextInputProcessor::NotifyIME(TextEventD
       CancelCompositionInternal();
       return NS_OK;
     }
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
+NS_IMETHODIMP_(nsIMEUpdatePreference)
+TextInputProcessor::GetIMEUpdatePreference()
+{
+  // TextInputProcessor::NotifyIME does not require extra change notifications.
+  return nsIMEUpdatePreference();
+}
+
 NS_IMETHODIMP_(void)
 TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
 {
   // If This is called while this is being initialized, ignore the call.
   if (!mDispatcher) {
     return;
   }
   MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -27,16 +27,19 @@ public:
   TextInputProcessor();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITEXTINPUTPROCESSOR
 
   // TextEventDispatcherListener
   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                        const IMENotification& aNotification) override;
+
+  NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
+
   NS_IMETHOD_(void)
     OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
 
   NS_IMETHOD_(void) WillDispatchKeyboardEvent(
                       TextEventDispatcher* aTextEventDispatcher,
                       WidgetKeyboardEvent& aKeyboardEvent,
                       uint32_t aIndexOfKeypress,
                       void* aData) override;
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -574,16 +574,17 @@ media_pkg := org/mozilla/gecko/media
 $(aidl_target_path)/$(media_pkg)/%.java:$(aidl_src_path)/$(media_pkg)/%.aidl
 	@echo "Processing AIDL: $< => $@"
 	$(AIDL) -p$(ANDROID_SDK)/framework.aidl -I$(aidl_src_path) -o$(aidl_target_path) $<
 
 GECKOVIEW_AIDLS = \
   org/mozilla/gecko/IGeckoEditableChild.aidl \
   org/mozilla/gecko/IGeckoEditableParent.aidl \
   org/mozilla/gecko/process/IChildProcess.aidl \
+  org/mozilla/gecko/process/IProcessManager.aidl \
   $(NULL)
 
 geckoview_aidl_src_path := $(topsrcdir)/mobile/android/geckoview/src/main/aidl
 geckoview_aidl_target_path := generated
 geckoview_aidl_targets := $(addprefix $(geckoview_aidl_target_path)/,$(GECKOVIEW_AIDLS:.aidl=.java))
 
 $(geckoview_aidl_targets): $(geckoview_aidl_target_path)/%.java: $(geckoview_aidl_src_path)/%.aidl
 	@echo "Processing AIDL: $< => $@"
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -1186,9 +1186,10 @@ gbjar.sources += ['generated/org/mozilla
     'media/IMediaDrmBridgeCallbacks.java',
     'media/IMediaManager.java',
 ]]
 
 gvjar.sources += ['generated/org/mozilla/gecko/' + x for x in [
     'IGeckoEditableChild.java',
     'IGeckoEditableParent.java',
     'process/IChildProcess.java',
+    'process/IProcessManager.java',
 ]]
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/IGeckoEditableParent.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/IGeckoEditableParent.aidl
@@ -1,30 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
+import android.os.IBinder;
 import android.view.KeyEvent;
 
 import org.mozilla.gecko.IGeckoEditableChild;
 
 // Interface for GeckoEditable calls from child to parent
 interface IGeckoEditableParent {
     // Notify an IME event of a type defined in GeckoEditableListener.
-    void notifyIME(int type);
+    void notifyIME(IGeckoEditableChild child, int type);
 
     // Notify a change in editor state or type.
     void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint);
 
     // Notify a change in editor selection.
-    void onSelectionChange(int start, int end);
+    void onSelectionChange(IBinder token, int start, int end);
 
     // Notify a change in editor text.
-    void onTextChange(in CharSequence text, int start, int unboundedOldEnd);
+    void onTextChange(IBinder token, in CharSequence text,
+                      int start, int unboundedOldEnd);
 
     // Perform the default action associated with a key event.
-    void onDefaultKeyEvent(in KeyEvent event);
+    void onDefaultKeyEvent(IBinder token, in KeyEvent event);
 
     // Update the screen location of current composition.
-    void updateCompositionRects(in RectF[] rects);
+    void updateCompositionRects(IBinder token, in RectF[] rects);
 }
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
@@ -1,12 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.process;
+
+import org.mozilla.gecko.process.IProcessManager;
+
 import android.os.ParcelFileDescriptor;
 
 interface IChildProcess {
     void stop();
     int getPid();
-    void start(in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
+    void start(in IProcessManager procMan, in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IProcessManager.aidl
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.process;
+
+import org.mozilla.gecko.IGeckoEditableParent;
+
+interface IProcessManager {
+    IGeckoEditableParent getEditableParent(long contentId, long tabId);
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
@@ -14,16 +14,17 @@ import java.lang.reflect.Proxy;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
 
 import android.graphics.RectF;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.NoCopySpan;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -56,27 +57,30 @@ final class GeckoEditable extends IGecko
     private KeyCharacterMap mKeyMap;
 
     // mIcRunHandler is the Handler that currently runs Gecko-to-IC Runnables
     // mIcPostHandler is the Handler to post Gecko-to-IC Runnables to
     // The two can be different when switching from one handler to another
     private Handler mIcRunHandler;
     private Handler mIcPostHandler;
 
-    /* package */ IGeckoEditableChild mEditableChild;
+    // Parent process child used as a default for key events.
+    /* package */ IGeckoEditableChild mDefaultChild; // Used by IC thread.
+    // Parent or content process child that has the focus.
+    /* package */ IGeckoEditableChild mFocusedChild; // Used by IC thread.
+    /* package */ IBinder mFocusedToken; // Used by Gecko/binder thread.
     /* package */ GeckoEditableListener mListener;
     /* package */ GeckoView mView;
 
     /* package */ boolean mInBatchMode; // Used by IC thread
     /* package */ boolean mNeedSync; // Used by IC thread
     // Gecko side needs an updated composition from Java;
     private boolean mNeedUpdateComposition; // Used by IC thread
     private boolean mSuppressKeyUp; // Used by IC thread
 
-    private boolean mGeckoFocused; // Used by Gecko thread
     private boolean mIgnoreSelectionChange; // Used by Gecko thread
 
     private static final int IME_RANGE_CARETPOSITION = 1;
     private static final int IME_RANGE_RAWINPUT = 2;
     private static final int IME_RANGE_SELECTEDRAWTEXT = 3;
     private static final int IME_RANGE_CONVERTEDTEXT = 4;
     private static final int IME_RANGE_SELECTEDCONVERTEDTEXT = 5;
 
@@ -87,18 +91,19 @@ final class GeckoEditable extends IGecko
     private static final int IME_RANGE_LINE_DOUBLE = 4;
     private static final int IME_RANGE_LINE_WAVY = 5;
 
     private static final int IME_RANGE_UNDERLINE = 1;
     private static final int IME_RANGE_FORECOLOR = 2;
     private static final int IME_RANGE_BACKCOLOR = 4;
     private static final int IME_RANGE_LINECOLOR = 8;
 
-    private void onKeyEvent(KeyEvent event, int action, int savedMetaState,
-                            boolean isSynthesizedImeKey) throws RemoteException {
+    private void onKeyEvent(final IGeckoEditableChild child, KeyEvent event, int action,
+                            int savedMetaState, boolean isSynthesizedImeKey)
+            throws RemoteException {
         // Use a separate action argument so we can override the key's original action,
         // e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate
         // a new key event just to change its action field.
         //
         // Normally we expect event.getMetaState() to reflect the current meta-state; however,
         // some software-generated key events may not have event.getMetaState() set, e.g. key
         // events from Swype. Therefore, it's necessary to combine the key's meta-states
         // with the meta-states that we keep separately in KeyListener
@@ -112,17 +117,17 @@ final class GeckoEditable extends IGecko
                 unicodeChar >= ' '               ? unicodeChar :
                 unmodifiedMetaState != metaState ? unmodifiedUnicodeChar : 0;
 
         // If a modifier (e.g. meta key) caused a different character to be entered, we
         // drop that modifier from the metastate for the generated keypress event.
         final int keyPressMetaState = (unicodeChar >= ' ' &&
                 unicodeChar != unmodifiedUnicodeChar) ? unmodifiedMetaState : metaState;
 
-        mEditableChild.onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
+        child.onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
                    metaState, keyPressMetaState, event.getEventTime(),
                    domPrintableKeyValue, event.getRepeatCount(), event.getFlags(),
                    isSynthesizedImeKey, event);
     }
 
     /**
      * Class that encapsulates asynchronous text editing. There are two copies of the
      * text, a current copy and a shadow copy. Both can be modified independently through
@@ -162,44 +167,36 @@ final class GeckoEditable extends IGecko
             // Merge the new change into any existing change.
             mCurrentStart = Math.min(mCurrentStart, start);
             mCurrentOldEnd += Math.max(0, oldEnd - mCurrentNewEnd);
             mCurrentNewEnd = newEnd + Math.max(0, mCurrentNewEnd - oldEnd);
         }
 
         public synchronized void currentReplace(final int start, final int end,
                                                 final CharSequence newText) {
-            if (DEBUG) {
-                ThreadUtils.assertOnGeckoThread();
-            }
+            // On Gecko or binder thread.
             mCurrentText.replace(start, end, newText);
             addCurrentChangeLocked(start, end, start + newText.length());
         }
 
         public synchronized void currentSetSelection(final int start, final int end) {
-            if (DEBUG) {
-                ThreadUtils.assertOnGeckoThread();
-            }
+            // On Gecko or binder thread.
             Selection.setSelection(mCurrentText, start, end);
             mCurrentSelectionChanged = true;
         }
 
         public synchronized void currentSetSpan(final Object obj, final int start,
                                                 final int end, final int flags) {
-            if (DEBUG) {
-                ThreadUtils.assertOnGeckoThread();
-            }
+            // On Gecko or binder thread.
             mCurrentText.setSpan(obj, start, end, flags);
             addCurrentChangeLocked(start, end, end);
         }
 
         public synchronized void currentRemoveSpan(final Object obj) {
-            if (DEBUG) {
-                ThreadUtils.assertOnGeckoThread();
-            }
+            // On Gecko or binder thread.
             if (obj == null) {
                 mCurrentText.clearSpans();
                 addCurrentChangeLocked(0, mCurrentText.length(), mCurrentText.length());
                 return;
             }
             final int start = mCurrentText.getSpanStart(obj);
             final int end = mCurrentText.getSpanEnd(obj);
             if (start < 0 || end < 0) {
@@ -207,19 +204,17 @@ final class GeckoEditable extends IGecko
             }
             mCurrentText.removeSpan(obj);
             addCurrentChangeLocked(start, end, end);
         }
 
         // Return Spanned instead of Editable because the returned object is supposed to
         // be read-only. Editing should be done through one of the current*** methods.
         public Spanned getCurrentText() {
-            if (DEBUG) {
-                ThreadUtils.assertOnGeckoThread();
-            }
+            // On Gecko or binder thread.
             return mCurrentText;
         }
 
         private void addShadowChange(final int start, final int oldEnd, final int newEnd) {
             // Merge the new change into any existing change.
             mShadowStart = Math.min(mShadowStart, start);
             mShadowOldEnd += Math.max(0, oldEnd - mShadowNewEnd);
             mShadowNewEnd = newEnd + Math.max(0, mShadowNewEnd - oldEnd);
@@ -433,18 +428,18 @@ final class GeckoEditable extends IGecko
 
     private void icOfferAction(final Action action) {
         if (DEBUG) {
             assertOnIcThread();
             Log.d(LOGTAG, "offer: Action(" +
                           getConstantName(Action.class, "TYPE_", action.mType) + ")");
         }
 
-        if (mListener == null) {
-            // We haven't initialized or we've been destroyed.
+        if (mFocusedChild == null || mListener == null) {
+            // We haven't been focused or initialized, or we've been destroyed.
             return;
         }
 
         mActions.offer(action);
 
         try {
             icPerformAction(action);
         } catch (final RemoteException e) {
@@ -453,57 +448,57 @@ final class GeckoEditable extends IGecko
             mActions.remove(action);
         }
     }
 
     private void icPerformAction(final Action action) throws RemoteException {
         switch (action.mType) {
         case Action.TYPE_EVENT:
         case Action.TYPE_SET_HANDLER:
-            mEditableChild.onImeSynchronize();
+            mFocusedChild.onImeSynchronize();
             break;
 
         case Action.TYPE_SET_SPAN:
             mText.shadowSetSpan(action.mSpanObject, action.mStart,
                                 action.mEnd, action.mSpanFlags);
             action.mSequence = TextUtils.substring(
                     mText.getShadowText(), action.mStart, action.mEnd);
 
             mNeedUpdateComposition |= (action.mSpanFlags & Spanned.SPAN_INTERMEDIATE) == 0 &&
                     ((action.mSpanFlags & Spanned.SPAN_COMPOSING) != 0 ||
                      action.mSpanObject == Selection.SELECTION_START ||
                      action.mSpanObject == Selection.SELECTION_END);
 
-            mEditableChild.onImeSynchronize();
+            mFocusedChild.onImeSynchronize();
             break;
 
         case Action.TYPE_REMOVE_SPAN:
             final int flags = mText.getShadowText().getSpanFlags(action.mSpanObject);
             mText.shadowRemoveSpan(action.mSpanObject);
 
             mNeedUpdateComposition |= (flags & Spanned.SPAN_INTERMEDIATE) == 0 &&
                     (flags & Spanned.SPAN_COMPOSING) != 0;
 
-            mEditableChild.onImeSynchronize();
+            mFocusedChild.onImeSynchronize();
             break;
 
         case Action.TYPE_REPLACE_TEXT:
             // Always sync text after a replace action, so that if the Gecko
             // text is not changed, we will revert the shadow text to before.
             mNeedSync = true;
 
             // Because we get composition styling here essentially for free,
             // we don't need to check if we're in batch mode.
             if (!icMaybeSendComposition(
                     action.mSequence, /* useEntireText */ true, /* notifyGecko */ false)) {
                 // Since we don't have a composition, we can try sending key events.
                 sendCharKeyEvents(action);
             }
             mText.shadowReplace(action.mStart, action.mEnd, action.mSequence);
-            mEditableChild.onImeReplaceText(
+            mFocusedChild.onImeReplaceText(
                     action.mStart, action.mEnd, action.mSequence.toString());
             break;
 
         default:
             throw new IllegalStateException("Action not processed");
         }
     }
 
@@ -543,17 +538,17 @@ final class GeckoEditable extends IGecko
                 continue;
             }
             if (event.getAction() == KeyEvent.ACTION_UP && mSuppressKeyUp) {
                 continue;
             }
             if (DEBUG) {
                 Log.d(LOGTAG, "sending: " + event);
             }
-            onKeyEvent(event, event.getAction(),
+            onKeyEvent(mFocusedChild, event, event.getAction(),
                        /* metaState */ 0, /* isSynthesizedImeKey */ true);
         }
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private GeckoEditable(final GeckoView v) {
         if (DEBUG) {
             // Called by nsWindow.
@@ -570,17 +565,17 @@ final class GeckoEditable extends IGecko
 
         mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
 
         onViewChange(v);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void setDefaultEditableChild(final IGeckoEditableChild child) {
-        mEditableChild = child;
+        mDefaultChild = child;
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void onViewChange(final GeckoView v) {
         if (DEBUG) {
             // Called by nsWindow.
             ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "onViewChange(" + v + ")");
@@ -696,25 +691,25 @@ final class GeckoEditable extends IGecko
             if (useEntireText && (selStart < 0 || selEnd < 0)) {
                 selStart = composingEnd;
                 selEnd = composingEnd;
             }
 
             if (found) {
                 icSendComposition(text, selStart, selEnd, composingStart, composingEnd);
                 if (notifyGecko) {
-                    mEditableChild.onImeUpdateComposition(composingStart, composingEnd);
+                    mFocusedChild.onImeUpdateComposition(composingStart, composingEnd);
                 }
                 return true;
             }
         }
 
         if (notifyGecko) {
             // Set the selection by using a composition without ranges
-            mEditableChild.onImeUpdateComposition(selStart, selEnd);
+            mFocusedChild.onImeUpdateComposition(selStart, selEnd);
         }
 
         if (DEBUG) {
             Log.d(LOGTAG, "icSendComposition(): no composition");
         }
         return false;
     }
 
@@ -728,17 +723,17 @@ final class GeckoEditable extends IGecko
                                              composingStart + ", " + composingEnd + ")");
         }
         if (DEBUG) {
             Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd);
             Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd);
         }
 
         if (selEnd >= composingStart && selEnd <= composingEnd) {
-            mEditableChild.onImeAddCompositionRange(
+            mFocusedChild.onImeAddCompositionRange(
                     selEnd - composingStart, selEnd - composingStart,
                     IME_RANGE_CARETPOSITION, 0, 0, false, 0, 0, 0);
         }
 
         int rangeStart = composingStart;
         TextPaint tp = new TextPaint();
         TextPaint emptyTp = new TextPaint();
         // set initial foreground color to 0, because we check for tp.getColor() == 0
@@ -801,17 +796,17 @@ final class GeckoEditable extends IGecko
                     rangeStyles |= IME_RANGE_FORECOLOR;
                     rangeForeColor = tp.getColor();
                 }
                 if (tp.bgColor != 0) {
                     rangeStyles |= IME_RANGE_BACKCOLOR;
                     rangeBackColor = tp.bgColor;
                 }
             }
-            mEditableChild.onImeAddCompositionRange(
+            mFocusedChild.onImeAddCompositionRange(
                     rangeStart - composingStart, rangeEnd - composingStart,
                     rangeType, rangeStyles, rangeLineStyle, rangeBoldLine,
                     rangeForeColor, rangeBackColor, rangeLineColor);
             rangeStart = rangeEnd;
 
             if (DEBUG) {
                 Log.d(LOGTAG, " added " + rangeType +
                               " : " + Integer.toHexString(rangeStyles) +
@@ -833,18 +828,27 @@ final class GeckoEditable extends IGecko
            We are actually sending two events to Gecko here,
            1. Event from the event parameter (key event)
            2. Sync event from the icOfferAction call
            The first event is a normal event that does not reply back to us,
            the second sync event will have a reply, during which we see that there is a pending
            event-type action, and update the shadow text accordingly.
         */
         try {
+            if (mFocusedChild == null) {
+                // Not focused; send simple key event to chrome window.
+                onKeyEvent(mDefaultChild, event, action, metaState,
+                           /* isSynthesizedImeKey */ false);
+                return;
+            }
+
+            // Focused; key event may go to chrome window or to content window.
             icMaybeSendComposition();
-            onKeyEvent(event, action, metaState, /* isSynthesizedImeKey */ false);
+            onKeyEvent(mFocusedChild, event, action, metaState,
+                       /* isSynthesizedImeKey */ false);
             icOfferAction(new Action(Action.TYPE_EVENT));
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
         }
     }
 
     @Override
     public Editable getEditable() {
@@ -947,26 +951,26 @@ final class GeckoEditable extends IGecko
     @Override // GeckoEditableClient
     public void postToInputConnection(final Runnable runnable) {
         mIcPostHandler.post(runnable);
     }
 
     @Override // GeckoEditableClient
     public void requestCursorUpdates(int requestMode) {
         try {
-            mEditableChild.onImeRequestCursorUpdates(requestMode);
+            if (mFocusedChild != null) {
+                mFocusedChild.onImeRequestCursorUpdates(requestMode);
+            }
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
         }
     }
 
     private void geckoSetIcHandler(final Handler newHandler) {
-        if (DEBUG) {
-            ThreadUtils.assertOnGeckoThread();
-        }
+        // On Gecko or binder thread.
         mIcPostHandler.post(new Runnable() { // posting to old IC thread
             @Override
             public void run() {
                 synchronized (newHandler) {
                     mIcRunHandler = newHandler;
                     newHandler.notify();
                 }
             }
@@ -975,23 +979,20 @@ final class GeckoEditable extends IGecko
         // At this point, all future Runnables should be posted to the new IC thread, but
         // we don't switch mIcRunHandler yet because there may be pending Runnables on the
         // old IC thread still waiting to run.
         mIcPostHandler = newHandler;
     }
 
     private void geckoActionReply(final Action action) {
         // On Gecko or binder thread.
-        if (!mGeckoFocused) {
-            if (DEBUG) {
-                Log.d(LOGTAG, "discarding stale reply");
-            }
+        if (action == null) {
+            Log.w(LOGTAG, "Mismatched reply");
             return;
         }
-
         if (DEBUG) {
             Log.d(LOGTAG, "reply: Action(" +
                           getConstantName(Action.class, "TYPE_", action.mType) + ")");
         }
         switch (action.mType) {
         case Action.TYPE_SET_SPAN:
             final int len = mText.getCurrentText().length();
             if (action.mStart > len || action.mEnd > len ||
@@ -1010,29 +1011,64 @@ final class GeckoEditable extends IGecko
             break;
 
         case Action.TYPE_SET_HANDLER:
             geckoSetIcHandler(action.mHandler);
             break;
         }
     }
 
+    private synchronized boolean binderCheckToken(final IBinder token,
+                                                  final boolean allowNull) {
+        // Verify that we're getting an IME notification from the currently focused child.
+        if (mFocusedToken == token || (mFocusedToken == null && allowNull)) {
+            return true;
+        }
+        Log.w(LOGTAG, "Invalid token");
+        return false;
+    }
+
     @Override // IGeckoEditableParent
-    public void notifyIME(final int type) {
+    public void notifyIME(final IGeckoEditableChild child, final int type) {
         // On Gecko or binder thread.
         if (DEBUG) {
             // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply()
             if (type != GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
                 Log.d(LOGTAG, "notifyIME(" +
                               getConstantName(GeckoEditableListener.class, "NOTIFY_IME_", type) +
                               ")");
             }
         }
 
-        if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
+        final IBinder token = child.asBinder();
+        if (type == GeckoEditableListener.NOTIFY_IME_OF_TOKEN) {
+            synchronized (this) {
+                if (mFocusedToken != null && mFocusedToken != token &&
+                        mFocusedToken.pingBinder()) {
+                    // Focused child already exists and is alive.
+                    Log.w(LOGTAG, "Already focused");
+                    return;
+                }
+                mFocusedToken = token;
+                return;
+            }
+        } else if (type == GeckoEditableListener.NOTIFY_IME_OPEN_VKB) {
+            // Always from parent process.
+            ThreadUtils.assertOnGeckoThread();
+        } else if (!binderCheckToken(token, /* allowNull */ false)) {
+            return;
+        }
+
+        if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
+            synchronized (this) {
+                onTextChange(token, "", 0, Integer.MAX_VALUE);
+                mActions.clear();
+                mFocusedToken = null;
+            }
+        } else if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
             geckoActionReply(mActions.poll());
             if (!mActions.isEmpty()) {
                 // Only post to IC thread below when the queue is empty.
                 return;
             }
         }
 
         mIcPostHandler.post(new Runnable() {
@@ -1041,62 +1077,67 @@ final class GeckoEditable extends IGecko
                 if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
                     if (mNeedSync) {
                         icSyncShadowText();
                     }
                     return;
                 }
 
                 if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS && mListener != null) {
+                    mFocusedChild = child;
                     mNeedSync = false;
                     mText.syncShadowText(/* listener */ null);
+                } else if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
+                    mFocusedChild = null;
                 }
 
                 if (mListener != null) {
                     mListener.notifyIME(type);
                 }
             }
         });
-
-        // Update the mGeckoFocused flag.
-        if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
-            mGeckoFocused = false;
-        } else if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS) {
-            mGeckoFocused = true;
-        }
     }
 
     @Override // IGeckoEditableParent
     public void notifyIMEContext(final int state, final String typeHint,
-                                  final String modeHint, final String actionHint) {
+                                 final String modeHint, final String actionHint) {
         // On Gecko or binder thread.
         if (DEBUG) {
             Log.d(LOGTAG, "notifyIMEContext(" +
                           getConstantName(GeckoEditableListener.class, "IME_STATE_", state) +
                           ", \"" + typeHint + "\", \"" + modeHint + "\", \"" + actionHint + "\")");
         }
 
+        // Don't check token for notifyIMEContext, because the calls all come
+        // from the parent process.
+        ThreadUtils.assertOnGeckoThread();
+
         mIcPostHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mListener == null) {
                     return;
                 }
                 mListener.notifyIMEContext(state, typeHint, modeHint, actionHint);
             }
         });
     }
 
     @Override // IGeckoEditableParent
-    public void onSelectionChange(final int start, final int end) {
+    public void onSelectionChange(final IBinder token,
+                                  final int start, final int end) {
         // On Gecko or binder thread.
         if (DEBUG) {
             Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")");
         }
 
+        if (!binderCheckToken(token, /* allowNull */ false)) {
+            return;
+        }
+
         if (mIgnoreSelectionChange) {
             mIgnoreSelectionChange = false;
         } else {
             mText.currentSetSelection(start, end);
         }
 
         mIcPostHandler.post(new Runnable() {
             @Override
@@ -1107,26 +1148,30 @@ final class GeckoEditable extends IGecko
     }
 
     private boolean geckoIsSameText(int start, int oldEnd, CharSequence newText) {
         return oldEnd - start == newText.length() && TextUtils.regionMatches(
                 mText.getCurrentText(), start, newText, 0, oldEnd - start);
     }
 
     @Override // IGeckoEditableParent
-    public void onTextChange(final CharSequence text, final int start,
-                             final int unboundedOldEnd) {
+    public void onTextChange(final IBinder token, final CharSequence text,
+                             final int start, final int unboundedOldEnd) {
         // On Gecko or binder thread.
         if (DEBUG) {
             StringBuilder sb = new StringBuilder("onTextChange(");
             debugAppend(sb, text).append(", ").append(start).append(", ")
                                  .append(unboundedOldEnd).append(")");
             Log.d(LOGTAG, sb.toString());
         }
 
+        if (!binderCheckToken(token, /* allowNull */ false)) {
+            return;
+        }
+
         final int currentLength = mText.getCurrentText().length();
         final int oldEnd = unboundedOldEnd > currentLength ? currentLength : unboundedOldEnd;
         final int newEnd = start + text.length();
         final Action action = mActions.peek();
 
         if (start == 0 && unboundedOldEnd > currentLength) {
             // | oldEnd > currentLength | signals entire text is cleared (e.g. for
             // newly-focused editors). Simply replace the text in that case; replace in
@@ -1222,46 +1267,55 @@ final class GeckoEditable extends IGecko
             mIgnoreSelectionChange = false;
         }
 
         // onTextChange is always followed by onSelectionChange, so we let
         // onSelectionChange schedule a shadow text sync.
     }
 
     @Override // IGeckoEditableParent
-    public void onDefaultKeyEvent(final KeyEvent event) {
+    public void onDefaultKeyEvent(final IBinder token, final KeyEvent event) {
         // On Gecko or binder thread.
         if (DEBUG) {
             StringBuilder sb = new StringBuilder("onDefaultKeyEvent(");
             sb.append("action=").append(event.getAction()).append(", ")
                 .append("keyCode=").append(event.getKeyCode()).append(", ")
                 .append("metaState=").append(event.getMetaState()).append(", ")
                 .append("time=").append(event.getEventTime()).append(", ")
                 .append("repeatCount=").append(event.getRepeatCount()).append(")");
             Log.d(LOGTAG, sb.toString());
         }
 
+        // Allow default key processing even if we're not focused.
+        if (!binderCheckToken(token, /* allowNull */ true)) {
+            return;
+        }
+
         mIcPostHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mListener == null) {
                     return;
                 }
                 mListener.onDefaultKeyEvent(event);
             }
         });
     }
 
     @Override // IGeckoEditableParent
-    public void updateCompositionRects(final RectF[] rects) {
+    public void updateCompositionRects(final IBinder token, final RectF[] rects) {
         // On Gecko or binder thread.
         if (DEBUG) {
             Log.d(LOGTAG, "updateCompositionRects(rects.length = " + rects.length + ")");
         }
 
+        if (!binderCheckToken(token, /* allowNull */ false)) {
+            return;
+        }
+
         mIcPostHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mListener == null) {
                     return;
                 }
                 mListener.updateCompositionRects(rects);
             }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableChild.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableChild.java
@@ -21,23 +21,75 @@ import android.view.KeyEvent;
  * process has an instance of GeckoEditableChild, which communicates with the
  * GeckoEditableParent instance in the main process.
  */
 final class GeckoEditableChild extends JNIObject implements IGeckoEditableChild {
 
     private static final boolean DEBUG = false;
     private static final String LOGTAG = "GeckoEditableChild";
 
+    private final class RemoteChild extends IGeckoEditableChild.Stub {
+        @Override // IGeckoEditableChild
+        public void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
+                               int keyPressMetaState, long time, int domPrintableKeyValue,
+                               int repeatCount, int flags, boolean isSynthesizedImeKey,
+                               KeyEvent event) {
+            GeckoEditableChild.this.onKeyEvent(
+                    action, keyCode, scanCode, metaState, keyPressMetaState, time,
+                    domPrintableKeyValue, repeatCount, flags, isSynthesizedImeKey, event);
+        }
+
+        @Override // IGeckoEditableChild
+        public void onImeSynchronize() {
+            GeckoEditableChild.this.onImeSynchronize();
+        }
+
+        @Override // IGeckoEditableChild
+        public void onImeReplaceText(int start, int end, String text) {
+            GeckoEditableChild.this.onImeReplaceText(start, end, text);
+        }
+
+        @Override // IGeckoEditableChild
+        public void onImeAddCompositionRange(int start, int end, int rangeType,
+                                             int rangeStyles, int rangeLineStyle,
+                                             boolean rangeBoldLine, int rangeForeColor,
+                                             int rangeBackColor, int rangeLineColor) {
+            GeckoEditableChild.this.onImeAddCompositionRange(
+                    start, end, rangeType, rangeStyles, rangeLineStyle, rangeBoldLine,
+                    rangeForeColor, rangeBackColor, rangeLineColor);
+        }
+
+        @Override // IGeckoEditableChild
+        public void onImeUpdateComposition(int start, int end) {
+            GeckoEditableChild.this.onImeUpdateComposition(start, end);
+        }
+
+        @Override // IGeckoEditableChild
+        public void onImeRequestCursorUpdates(int requestMode) {
+            GeckoEditableChild.this.onImeRequestCursorUpdates(requestMode);
+        }
+    }
+
     private final IGeckoEditableParent mEditableParent;
+    private final IGeckoEditableChild mEditableChild;
 
     private int mCurrentTextLength; // Used by Gecko thread
 
     @WrapForJNI(calledFrom = "gecko")
     /* package */ GeckoEditableChild(final IGeckoEditableParent editableParent) {
         mEditableParent = editableParent;
+
+        final IBinder binder = editableParent.asBinder();
+        if (binder.queryLocalInterface(IGeckoEditableParent.class.getName()) != null) {
+            // IGeckoEditableParent is local; i.e. we're in the main process.
+            mEditableChild = this;
+        } else {
+            // IGeckoEditableParent is remote; i.e. we're in a content process.
+            mEditableChild = new RemoteChild();
+        }
     }
 
     @WrapForJNI(dispatchTo = "proxy") @Override // IGeckoEditableChild
     public native void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
                                   int keyPressMetaState, long time, int domPrintableKeyValue,
                                   int repeatCount, int flags, boolean isSynthesizedImeKey,
                                   KeyEvent event);
 
@@ -62,17 +114,18 @@ final class GeckoEditableChild extends J
     @Override // JNIObject
     protected void disposeNative() {
         // Disposal happens in native code.
         throw new UnsupportedOperationException();
     }
 
     @Override // IInterface
     public IBinder asBinder() {
-        return null;
+        // Return the GeckoEditableParent's binder as our binder for comparison purposes.
+        return mEditableParent.asBinder();
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void notifyIME(final int type) {
         if (DEBUG) {
             ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "notifyIME(" + GeckoEditable.getConstantName(
                           GeckoEditableListener.class, "NOTIFY_IME_", type) + ")");
@@ -87,17 +140,17 @@ final class GeckoEditableChild extends J
             // Composition should have been canceled on the parent side through text
             // update notifications. We cannot verify that here because we don't
             // keep track of spans on the child side, but it's simple to add the
             // check to the parent side if ever needed.
             return;
         }
 
         try {
-            mEditableParent.notifyIME(type);
+            mEditableParent.notifyIME(mEditableChild, type);
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
             return;
         }
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void notifyIMEContext(final int state, final String typeHint,
@@ -125,17 +178,17 @@ final class GeckoEditableChild extends J
 
         final int currentLength = mCurrentTextLength;
         if (start < 0 || start > currentLength || end < 0 || end > currentLength) {
             Log.e(LOGTAG, "invalid selection notification range: " +
                   start + " to " + end + ", length: " + currentLength);
             throw new IllegalArgumentException("invalid selection notification range");
         }
 
-        mEditableParent.onSelectionChange(start, end);
+        mEditableParent.onSelectionChange(mEditableChild.asBinder(), start, end);
     }
 
     @WrapForJNI(calledFrom = "gecko", exceptionMode = "ignore")
     private void onTextChange(final CharSequence text, final int start,
                               final int unboundedOldEnd, final int unboundedNewEnd)
             throws RemoteException {
         if (DEBUG) {
             ThreadUtils.assertOnGeckoThread();
@@ -161,17 +214,17 @@ final class GeckoEditableChild extends J
         if (unboundedOldEnd <= currentLength && unboundedNewEnd != (start + text.length())) {
             Log.e(LOGTAG, "newEnd does not match text: " + unboundedNewEnd + " vs " +
                   (start + text.length()));
             throw new IllegalArgumentException("newEnd does not match text");
         }
 
         mCurrentTextLength += start + text.length() - oldEnd;
         // Need unboundedOldEnd so GeckoEditable can distinguish changed text vs cleared text.
-        mEditableParent.onTextChange(text, start, unboundedOldEnd);
+        mEditableParent.onTextChange(mEditableChild.asBinder(), text, start, unboundedOldEnd);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void onDefaultKeyEvent(final KeyEvent event) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             StringBuilder sb = new StringBuilder("onDefaultKeyEvent(");
@@ -179,29 +232,29 @@ final class GeckoEditableChild extends J
                 .append("keyCode=").append(event.getKeyCode()).append(", ")
                 .append("metaState=").append(event.getMetaState()).append(", ")
                 .append("time=").append(event.getEventTime()).append(", ")
                 .append("repeatCount=").append(event.getRepeatCount()).append(")");
             Log.d(LOGTAG, sb.toString());
         }
 
         try {
-            mEditableParent.onDefaultKeyEvent(event);
+            mEditableParent.onDefaultKeyEvent(mEditableChild.asBinder(), event);
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
         }
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private void updateCompositionRects(final RectF[] rects) {
         if (DEBUG) {
             // GeckoEditableListener methods should all be called from the Gecko thread
             ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "updateCompositionRects(rects.length = " + rects.length + ")");
         }
 
         try {
-            mEditableParent.updateCompositionRects(rects);
+            mEditableParent.updateCompositionRects(mEditableChild.asBinder(), rects);
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
         }
     }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java
@@ -12,16 +12,18 @@ import android.view.KeyEvent;
 
 /**
  * Interface for the Editable to listen on the Gecko thread, as well as for the IC thread to listen
  * to the Editable.
  */
 interface GeckoEditableListener {
     // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
     @WrapForJNI
+    int NOTIFY_IME_OF_TOKEN = -3;
+    @WrapForJNI
     int NOTIFY_IME_OPEN_VKB = -2;
     @WrapForJNI
     int NOTIFY_IME_REPLY_EVENT = -1;
     @WrapForJNI
     int NOTIFY_IME_OF_FOCUS = 1;
     @WrapForJNI
     int NOTIFY_IME_OF_BLUR = 2;
     @WrapForJNI
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -136,25 +136,26 @@ public class GeckoThread extends Thread 
     private String[] mArgs;
 
     // Main process parameters
     private GeckoProfile mProfile;
     private String mExtraArgs;
     private boolean mDebugging;
 
     // Child process parameters
-    private int mCrashFileDescriptor;
-    private int mIPCFileDescriptor;
+    private int mCrashFileDescriptor = -1;
+    private int mIPCFileDescriptor = -1;
 
     GeckoThread() {
         setName("Gecko");
     }
 
-    private boolean isChildProcess() {
-        return mIPCFileDescriptor != -1;
+    @WrapForJNI
+    private static boolean isChildProcess() {
+        return INSTANCE.mIPCFileDescriptor != -1;
     }
 
     private synchronized boolean init(final GeckoProfile profile, final String[] args,
                                       final String extraArgs, final boolean debugging,
                                       final int crashFd, final int ipcFd) {
         ThreadUtils.assertOnUiThread();
 
         if (mInitialized) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.process;
 
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.IGeckoEditableParent;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.DeadObjectException;
@@ -20,24 +22,33 @@ import android.support.v4.util.SimpleArr
 import android.view.Surface;
 import android.util.Log;
 
 import java.io.IOException;
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 import java.util.Map;
 
-public final class GeckoProcessManager {
+public final class GeckoProcessManager extends IProcessManager.Stub {
     private static final String LOGTAG = "GeckoProcessManager";
     private static final GeckoProcessManager INSTANCE = new GeckoProcessManager();
 
     public static GeckoProcessManager getInstance() {
         return INSTANCE;
     }
 
+    @WrapForJNI(stubName = "GetEditableParent")
+    private static native IGeckoEditableParent nativeGetEditableParent(long contentId,
+                                                                       long tabId);
+
+    @Override // IProcessManager
+    public IGeckoEditableParent getEditableParent(final long contentId, final long tabId) {
+        return nativeGetEditableParent(contentId, tabId);
+    }
+
     private static final class ChildConnection implements ServiceConnection, IBinder.DeathRecipient {
         public final String mType;
         private boolean mWait = false;
         public IChildProcess mChild = null;
         public int mPid = 0;
         public ChildConnection(String type) {
             mType = type;
         }
@@ -145,17 +156,17 @@ public final class GeckoProcessManager {
                 GeckoAppShell.getApplicationContext().unbindService(connection);
                 return 0;
             }
             ParcelFileDescriptor crashPfd = null;
             if (crashFd >= 0) {
                 crashPfd = ParcelFileDescriptor.fromFd(crashFd);
             }
             ParcelFileDescriptor ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
-            connection.mChild.start(args, crashPfd, ipcPfd);
+            connection.mChild.start(this, args, crashPfd, ipcPfd);
             if (crashPfd != null) {
                 crashPfd.close();
             }
             ipcPfd.close();
             synchronized(mConnections) {
                 mConnections.put(type, connection);
             }
             return connection.mPid;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
@@ -1,45 +1,59 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.process;
 
 import org.mozilla.gecko.annotation.JNITarget;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.IGeckoEditableParent;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.RemoteException;
 import android.util.Log;
 
 public class GeckoServiceChildProcess extends Service {
 
     static private String LOGTAG = "GeckoServiceChildProcess";
 
-    private boolean serviceStarted;
+    private static IProcessManager sProcessManager;
 
     static private void stop() {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
-                Process.killProcess(Process.myPid());;
+                Process.killProcess(Process.myPid());
             }
         });
     }
 
+    @WrapForJNI(calledFrom = "gecko")
+    private static IGeckoEditableParent getEditableParent(final long contentId,
+                                                          final long tabId) {
+        try {
+            return sProcessManager.getEditableParent(contentId, tabId);
+        } catch (final RemoteException e) {
+            Log.e(LOGTAG, "Cannot get editable", e);
+            return null;
+        }
+    }
+
     public void onCreate() {
         super.onCreate();
     }
 
     public void onDestroy() {
         super.onDestroy();
     }
 
@@ -54,24 +68,26 @@ public class GeckoServiceChildProcess ex
         }
 
         @Override
         public int getPid() {
             return Process.myPid();
         }
 
         @Override
-        public void start(final String[] args,
+        public void start(final IProcessManager procMan,
+                          final String[] args,
                           final ParcelFileDescriptor crashReporterPfd,
                           final ParcelFileDescriptor ipcPfd) {
-            if (serviceStarted) {
+            if (sProcessManager != null) {
                 Log.e(LOGTAG, "Attempting to start a service that has already been started.");
                 return;
             }
-            serviceStarted = true;
+            sProcessManager = procMan;
+
             final int crashReporterFd = crashReporterPfd != null ? crashReporterPfd.detachFd() : -1;
             final int ipcFd = ipcPfd != null ? ipcPfd.detachFd() : -1;
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     GeckoAppShell.ensureCrashHandling();
                     GeckoAppShell.setApplicationContext(getApplicationContext());
                     if (GeckoThread.initChildProcess(args, crashReporterFd, ipcFd)) {
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -430,17 +430,17 @@ Java_org_mozilla_gecko_mozglue_GeckoLoad
       FreeArgv(argv, argc);
       return;
     }
 
     ElfLoader::Singleton.ExpectShutdown(false);
     gBootstrap->GeckoStart(jenv, argv, argc, sAppData);
     ElfLoader::Singleton.ExpectShutdown(true);
   } else {
-    gBootstrap->XRE_SetAndroidChildFds(crashFd, ipcFd);
+    gBootstrap->XRE_SetAndroidChildFds(jenv, crashFd, ipcFd);
     gBootstrap->XRE_SetProcessType(argv[argc - 1]);
 
     XREChildData childData;
     gBootstrap->XRE_InitChildProcess(argc - 1, argv, &childData);
   }
 
   gBootstrap.reset();
   FreeArgv(argv, argc);
--- a/toolkit/xre/Bootstrap.cpp
+++ b/toolkit/xre/Bootstrap.cpp
@@ -69,18 +69,18 @@ public:
     ::XRE_EnableSameExecutableForContentProc();
   }
 
 #ifdef MOZ_WIDGET_ANDROID
   virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc, const StaticXREAppData& aAppData) override {
     ::GeckoStart(aEnv, argv, argc, aAppData);
   }
 
-  virtual void XRE_SetAndroidChildFds(int aCrashFd, int aIPCFd) override {
-    ::XRE_SetAndroidChildFds(aCrashFd, aIPCFd);
+  virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aCrashFd, int aIPCFd) override {
+    ::XRE_SetAndroidChildFds(aEnv, aCrashFd, aIPCFd);
   }
 #endif
 
 #ifdef LIBFUZZER
   virtual void XRE_LibFuzzerSetDriver(LibFuzzerDriver aDriver) override {
     ::XRE_LibFuzzerSetDriver(aDriver);
   }
 #endif
--- a/toolkit/xre/Bootstrap.h
+++ b/toolkit/xre/Bootstrap.h
@@ -108,17 +108,17 @@ public:
 
   virtual nsresult XRE_InitChildProcess(int argc, char* argv[], const XREChildData* aChildData) = 0;
 
   virtual void XRE_EnableSameExecutableForContentProc() = 0;
 
 #ifdef MOZ_WIDGET_ANDROID
   virtual void GeckoStart(JNIEnv* aEnv, char** argv, int argc, const StaticXREAppData& aAppData) = 0;
 
-  virtual void XRE_SetAndroidChildFds(int aCrashFd, int aIPCFd) = 0;
+  virtual void XRE_SetAndroidChildFds(JNIEnv* aEnv, int aCrashFd, int aIPCFd) = 0;
 #endif
 
 #ifdef LIBFUZZER
   virtual void XRE_LibFuzzerSetDriver(LibFuzzerDriver aDriver) = 0;
 #endif
 
 #ifdef MOZ_IPDL_TESTS
   virtual int XRE_RunIPDLTest(int argc, char **argv) = 0;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -47,16 +47,17 @@
 #include "nsGDKErrorHandler.h"
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "chrome/common/child_process.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "chrome/common/ipc_channel.h"
+#include "mozilla/jni/Utils.h"
 #endif //  defined(MOZ_WIDGET_ANDROID)
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/IOThreadChild.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "ScopedXREEmbed.h"
 
@@ -234,18 +235,19 @@ XRE_ChildProcessTypeToString(GeckoProces
 namespace mozilla {
 namespace startup {
 GeckoProcessType sChildProcessType = GeckoProcessType_Default;
 } // namespace startup
 } // namespace mozilla
 
 #if defined(MOZ_WIDGET_ANDROID)
 void
-XRE_SetAndroidChildFds (int crashFd, int ipcFd)
+XRE_SetAndroidChildFds (JNIEnv* env, int crashFd, int ipcFd)
 {
+  mozilla::jni::SetGeckoThreadEnv(env);
 #if defined(MOZ_CRASHREPORTER)
   CrashReporter::SetNotificationPipeForChild(crashFd);
 #endif // defined(MOZ_CRASHREPORTER)
   IPC::Channel::SetClientChannelFd(ipcFd);
 }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
 void
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -688,16 +688,21 @@ PuppetWidget::RequestIMEToCommitComposit
 
   // NOTE: PuppetWidget might be destroyed already.
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
+  if (mNativeTextEventDispatcherListener) {
+    // Use mNativeTextEventDispatcherListener for IME notifications.
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
   switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       return RequestIMEToCommitComposition(false);
     case REQUEST_TO_CANCEL_COMPOSITION:
       return RequestIMEToCommitComposition(true);
     case NOTIFY_IME_OF_FOCUS:
     case NOTIFY_IME_OF_BLUR:
       return NotifyIMEOfFocusChange(aIMENotification);
@@ -852,16 +857,21 @@ PuppetWidget::NotifyIMEOfCompositionUpda
   }
   mTabChild->SendNotifyIMECompositionUpdate(mContentCache, aIMENotification);
   return NS_OK;
 }
 
 nsIMEUpdatePreference
 PuppetWidget::GetIMEUpdatePreference()
 {
+  if (mNativeTextEventDispatcherListener) {
+    // Use mNativeTextEventDispatcherListener for IME preference.
+    return mNativeTextEventDispatcherListener->GetIMEUpdatePreference();
+  }
+
   // e10s requires IME content cache in in the TabParent for handling query
   // content event only with the parent process.  Therefore, this process
   // needs to receive a lot of information from the focused editor to sent
   // the latest content to the parent process.
   if (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
     // But if a plugin has focus, we cannot receive text nor selection change
     // in the plugin.  Therefore, PuppetWidget needs to receive only position
     // change event for updating the editor rect cache.
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -21,16 +21,17 @@
 #include "nsBaseWidget.h"
 #include "nsCOMArray.h"
 #include "nsIKeyEventInPluginCallback.h"
 #include "nsIScreenManager.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentCache.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/TextEventDispatcherListener.h"
 
 namespace mozilla {
 
 namespace dom {
 class TabChild;
 } // namespace dom
 
 namespace widget {
@@ -178,16 +179,20 @@ public:
   // This is used after a compositor reset.
   LayerManager* RecreateLayerManager(PLayerTransactionChild* aShadowManager);
 
   virtual void SetInputContext(const InputContext& aContext,
                                const InputContextAction& aAction) override;
   virtual InputContext GetInputContext() override;
   virtual NativeIMEContext GetNativeIMEContext() override;
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
+  TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override
+  { return mNativeTextEventDispatcherListener; }
+  void SetNativeTextEventDispatcherListener(TextEventDispatcherListener* aListener)
+  { mNativeTextEventDispatcherListener = aListener; }
 
   virtual void SetCursor(nsCursor aCursor) override;
   virtual nsresult SetCursor(imgIContainer* aCursor,
                              uint32_t aHotspotX, uint32_t aHotspotY) override;
 
   virtual void ClearCachedCursor() override;
 
   // Gets the DPI of the screen corresponding to this widget.
@@ -371,16 +376,18 @@ private:
   InfallibleTArray<mozilla::CommandInt> mMultiLineCommands;
   InfallibleTArray<mozilla::CommandInt> mRichTextCommands;
 
   nsCOMPtr<imgIContainer> mCustomCursor;
   uint32_t mCursorHotspotX, mCursorHotspotY;
 
   nsCOMArray<nsIKeyEventInPluginCallback> mKeyEventInPluginCallbacks;
 
+  RefPtr<TextEventDispatcherListener> mNativeTextEventDispatcherListener;
+
 protected:
   bool mEnabled;
   bool mVisible;
 
 private:
   bool mNeedIMEStateInit;
   bool mNativeKeyCommandsValid;
 };
--- a/widget/TextEventDispatcherListener.h
+++ b/widget/TextEventDispatcherListener.h
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_textinputdispatcherlistener_h_
 #define mozilla_textinputdispatcherlistener_h_
 
 #include "nsWeakReference.h"
 
+struct nsIMEUpdatePreference;
+
 namespace mozilla {
 namespace widget {
 
 class TextEventDispatcher;
 struct IMENotification;
 
 #define NS_TEXT_INPUT_PROXY_LISTENER_IID \
 { 0xf2226f55, 0x6ddb, 0x40d5, \
@@ -26,16 +28,21 @@ public:
    * NotifyIME() is called by TextEventDispatcher::NotifyIME().  This is a
    * notification or request to IME.  See document of nsIWidget::NotifyIME()
    * for the detail.
    */
   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                        const IMENotification& aNotification) = 0;
 
   /**
+   * Returns preference for which IME notification are received by NotifyIME().
+   */
+  NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() = 0;
+
+  /**
    * OnRemovedFrom() is called when the TextEventDispatcher stops working and
    * is releasing the listener.
    */
   NS_IMETHOD_(void) OnRemovedFrom(
                       TextEventDispatcher* aTextEventDispatcher) = 0;
 
   /**
    * WillDispatchKeyboardEvent() may be called immediately before
--- a/widget/android/GeckoEditableSupport.cpp
+++ b/widget/android/GeckoEditableSupport.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GeckoEditableSupport.h"
 
 #include "AndroidRect.h"
 #include "KeyEvent.h"
+#include "PuppetWidget.h"
 #include "android_npapi.h"
 #include "nsIContent.h"
 #include "nsISelection.h"
 
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "mozilla/TextEvents.h"
@@ -386,17 +387,17 @@ void
 GeckoEditableSupport::RemoveComposition(RemoveCompositionFlag aFlag)
 {
     if (!mDispatcher || !mDispatcher->IsComposing()) {
         return;
     }
 
     nsEventStatus status = nsEventStatus_eIgnore;
 
-    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
     mDispatcher->CommitComposition(
             status, aFlag == CANCEL_IME_COMPOSITION ? &EmptyString() : nullptr);
 }
 
 void
 GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
         int32_t aScanCode, int32_t aMetaState, int32_t aKeyPressMetaState,
         int64_t aTime, int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
@@ -437,17 +438,17 @@ GeckoEditableSupport::OnKeyEvent(int32_t
 
     if (aIsSynthesizedImeKey) {
         // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
         // array until the next IME_REPLACE_TEXT event, at which point
         // these keys are dispatched in sequence.
         mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(event.Duplicate()));
     } else {
         RemoveComposition();
-        NS_ENSURE_SUCCESS_VOID(dispatcher->BeginNativeInputTransaction());
+        NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(dispatcher));
         dispatcher->DispatchKeyboardEvent(msg, event, status);
         if (widget->Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
             // Skip default processing.
             return;
         }
         mEditable->OnDefaultKeyEvent(aOriginalEvent);
     }
 
@@ -458,16 +459,22 @@ GeckoEditableSupport::OnKeyEvent(int32_t
 
     WidgetKeyboardEvent pressEvent(true, eKeyPress, widget);
     InitKeyEvent(pressEvent, aAction, aKeyCode, aScanCode, aKeyPressMetaState,
                  aTime, aDomPrintableKeyValue, aRepeatCount, aFlags);
 
     if (aIsSynthesizedImeKey) {
         mIMEKeyEvents.AppendElement(
                 UniquePtr<WidgetEvent>(pressEvent.Duplicate()));
+    } else if (nsIWidget::UsePuppetWidgets()) {
+        AutoCacheNativeKeyCommands autoCache(
+                static_cast<PuppetWidget*>(widget.get()));
+        // Don't use native key bindings.
+        autoCache.CacheNoCommands();
+        dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
     } else {
         dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
     }
 }
 
 /*
  * Send dummy key events for pages that are unaware of input events,
  * to provide web compatibility for pages that depend on key events.
@@ -477,17 +484,17 @@ void
 GeckoEditableSupport::SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg)
 {
     nsEventStatus status = nsEventStatus_eIgnore;
     MOZ_ASSERT(mDispatcher);
 
     WidgetKeyboardEvent event(true, msg, aWidget);
     event.mTime = PR_Now() / 1000;
     MOZ_ASSERT(event.mKeyCode == 0);
-    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
     mDispatcher->DispatchKeyboardEvent(msg, event, status);
 }
 
 void
 GeckoEditableSupport::AddIMETextChange(const IMETextChange& aChange)
 {
     mIMETextChanges.AppendElement(aChange);
 
@@ -752,17 +759,17 @@ GeckoEditableSupport::OnImeReplaceText(i
         mWindow->UserActivity();
     }
 
     /*
         Replace text in Gecko thread from aStart to aEnd with the string text.
     */
     nsCOMPtr<nsIWidget> widget = GetWidget();
     NS_ENSURE_TRUE_VOID(mDispatcher && widget);
-    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
 
     RefPtr<TextComposition> composition(GetComposition());
     MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
 
     nsString string(aText->ToString());
     const bool composing = !mIMERanges->IsEmpty();
     nsEventStatus status = nsEventStatus_eIgnore;
 
@@ -787,21 +794,27 @@ GeckoEditableSupport::OnImeReplaceText(i
         }
 
         if (!mIMEKeyEvents.IsEmpty()) {
             for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
                 const auto event = mIMEKeyEvents[i]->AsKeyboardEvent();
                 // widget for duplicated events is initially nullptr.
                 event->mWidget = widget;
 
-                if (event->mMessage == eKeyPress) {
+                if (event->mMessage != eKeyPress) {
+                    mDispatcher->DispatchKeyboardEvent(
+                            event->mMessage, *event, status);
+                } else if (nsIWidget::UsePuppetWidgets()) {
+                    AutoCacheNativeKeyCommands autoCache(
+                            static_cast<PuppetWidget*>(widget.get()));
+                    // Don't use native key bindings.
+                    autoCache.CacheNoCommands();
                     mDispatcher->MaybeDispatchKeypressEvents(*event, status);
                 } else {
-                    mDispatcher->DispatchKeyboardEvent(
-                            event->mMessage, *event, status);
+                    mDispatcher->MaybeDispatchKeypressEvents(*event, status);
                 }
                 if (widget->Destroyed()) {
                     break;
                 }
             }
             mIMEKeyEvents.Clear();
             return;
         }
@@ -947,17 +960,17 @@ GeckoEditableSupport::OnImeUpdateComposi
 
 #ifdef DEBUG_ANDROID_IME
     const NS_ConvertUTF16toUTF8 data(event.mData);
     const char* text = data.get();
     ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u",
             text, event.mData.Length(), event.mRanges->Length());
 #endif // DEBUG_ANDROID_IME
 
-    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
     mDispatcher->SetPendingComposition(string, mIMERanges);
     mDispatcher->FlushPendingComposition(status);
     mIMERanges->Clear();
 }
 
 void
 GeckoEditableSupport::OnImeRequestCursorUpdates(int aRequestMode)
 {
@@ -1019,16 +1032,31 @@ GeckoEditableSupport::NotifyIME(TextEven
             nsAppShell::PostEvent([this, self, dispatcher] {
                 nsCOMPtr<nsIWidget> widget = dispatcher->GetWidget();
 
                 --mIMEMaskEventsCount;
                 if (mIMEMaskEventsCount || !widget || widget->Destroyed()) {
                     return;
                 }
 
+                mEditable->NotifyIME(
+                        GeckoEditableListener::NOTIFY_IME_OF_TOKEN);
+
+                if (mIsRemote) {
+                    if (!mEditableAttached) {
+                        // Re-attach on focus; see OnRemovedFrom().
+                        AttachNative(mEditable, this);
+                        mEditableAttached = true;
+                    }
+                    // Because GeckoEditableSupport in content process doesn't
+                    // manage the active input context, we need to retrieve the
+                    // input context from the widget, for use by
+                    // OnImeReplaceText.
+                    mInputContext = widget->GetInputContext();
+                }
                 mDispatcher = dispatcher;
                 mIMEKeyEvents.Clear();
                 FlushIMEText();
 
                 // IME will call requestCursorUpdates after getting context.
                 // So reset cursor update mode before getting context.
                 mIMEMonitorCursor = false;
 
@@ -1038,17 +1066,17 @@ GeckoEditableSupport::NotifyIME(TextEven
             break;
         }
 
         case NOTIFY_IME_OF_BLUR: {
             ALOGIME("IME: NOTIFY_IME_OF_BLUR");
 
             if (!mIMEMaskEventsCount) {
                 mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OF_BLUR);
-                mDispatcher = nullptr;
+                OnRemovedFrom(mDispatcher);
             }
 
             // Mask events because we lost focus. Unmask on the next focus.
             mIMEMaskEventsCount++;
             break;
         }
 
         case NOTIFY_IME_OF_SELECTION_CHANGE: {
@@ -1086,27 +1114,33 @@ GeckoEditableSupport::NotifyIME(TextEven
             break;
     }
     return NS_OK;
 }
 
 void
 GeckoEditableSupport::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
 {
+    mDispatcher = nullptr;
+
+    if (mIsRemote) {
+        // When we're remote, detach every time.
+        OnDetach();
+    }
 }
 
 void
 GeckoEditableSupport::WillDispatchKeyboardEvent(
         TextEventDispatcher* aTextEventDispatcher,
         WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
         void* aData)
 {
 }
 
-nsIMEUpdatePreference
+NS_IMETHODIMP_(nsIMEUpdatePreference)
 GeckoEditableSupport::GetIMEUpdatePreference()
 {
     // While a plugin has focus, Listener doesn't need any notifications.
     if (GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
       return nsIMEUpdatePreference();
     }
     return nsIMEUpdatePreference(nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
 }
--- a/widget/android/GeckoEditableSupport.h
+++ b/widget/android/GeckoEditableSupport.h
@@ -84,34 +84,45 @@ class GeckoEditableSupport final
     };
 
     enum RemoveCompositionFlag
     {
         CANCEL_IME_COMPOSITION,
         COMMIT_IME_COMPOSITION
     };
 
+    const bool mIsRemote;
     nsWindow::WindowPtr<GeckoEditableSupport> mWindow; // Parent only
     RefPtr<TextEventDispatcher> mDispatcher;
     java::GeckoEditableChild::GlobalRef mEditable;
+    bool mEditableAttached;
     InputContext mInputContext;
     AutoTArray<UniquePtr<mozilla::WidgetEvent>, 4> mIMEKeyEvents;
     AutoTArray<IMETextChange, 4> mIMETextChanges;
     RefPtr<TextRangeArray> mIMERanges;
     int32_t mIMEMaskEventsCount; // Mask events when > 0.
     bool mIMEUpdatingContext;
     bool mIMESelectionChanged;
     bool mIMETextChangedDuringFlush;
     bool mIMEMonitorCursor;
 
     nsIWidget* GetWidget() const
     {
         return mDispatcher ? mDispatcher->GetWidget() : mWindow;
     }
 
+    nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher)
+    {
+        if (mIsRemote) {
+            return aDispatcher->BeginInputTransaction(this);
+        } else {
+            return aDispatcher->BeginNativeInputTransaction();
+        }
+    }
+
     virtual ~GeckoEditableSupport() {}
 
     RefPtr<TextComposition> GetComposition() const;
     void RemoveComposition(
             RemoveCompositionFlag aFlag = COMMIT_IME_COMPOSITION);
     void SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg);
     void AddIMETextChange(const IMETextChange& aChange);
     void PostFlushIMEChanges();
@@ -149,58 +160,67 @@ public:
                 }
                 Base::Run();
             }
         };
         nsAppShell::PostEvent(mozilla::MakeUnique<IMEEvent>(
                 mozilla::Move(aCall)));
     }
 
+    // Constructor for main process GeckoEditableChild.
     GeckoEditableSupport(nsWindow::NativePtr<GeckoEditableSupport>* aPtr,
                          nsWindow* aWindow,
                          java::GeckoEditableChild::Param aEditableChild)
-        : mWindow(aPtr, aWindow)
+        : mIsRemote(!aWindow)
+        , mWindow(aPtr, aWindow)
         , mEditable(aEditableChild)
+        , mEditableAttached(!mIsRemote)
         , mIMERanges(new TextRangeArray())
         , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
         , mIMEUpdatingContext(false)
         , mIMESelectionChanged(false)
         , mIMETextChangedDuringFlush(false)
         , mIMEMonitorCursor(false)
     {}
 
+    // Constructor for content process GeckoEditableChild.
+    GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
+        : GeckoEditableSupport(nullptr, nullptr, aEditableChild)
+    {}
+
     NS_DECL_ISUPPORTS
 
     // TextEventDispatcherListener methods
     NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                          const IMENotification& aNotification) override;
 
+    NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
+
     NS_IMETHOD_(void) OnRemovedFrom(
             TextEventDispatcher* aTextEventDispatcher) override;
 
     NS_IMETHOD_(void) WillDispatchKeyboardEvent(
             TextEventDispatcher* aTextEventDispatcher,
             WidgetKeyboardEvent& aKeyboardEvent,
             uint32_t aIndexOfKeypress,
             void* aData) override;
 
-    nsIMEUpdatePreference GetIMEUpdatePreference();
-
     void SetInputContext(const InputContext& aContext,
                          const InputContextAction& aAction);
 
     InputContext GetInputContext();
 
     // GeckoEditableChild methods
     using EditableBase::AttachNative;
 
     void OnDetach() {
         RefPtr<GeckoEditableSupport> self(this);
-        nsAppShell::PostEvent([self] {
-            DisposeNative(self->mEditable);
+        nsAppShell::PostEvent([this, self] {
+            mEditableAttached = false;
+            DisposeNative(mEditable);
         });
     }
 
     // Handle an Android KeyEvent.
     void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
                     int32_t aMetaState, int32_t aKeyPressMetaState,
                     int64_t aTime, int32_t aDomPrintableKeyValue,
                     int32_t aRepeatCount, int32_t aFlags,
new file mode 100644
--- /dev/null
+++ b/widget/android/GeckoProcessManager.h
@@ -0,0 +1,62 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GeckoProcessManager_h
+#define GeckoProcessManager_h
+
+#include "GeneratedJNINatives.h"
+#include "WidgetUtils.h"
+#include "nsAppShell.h"
+#include "nsWindow.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/ContentProcessManager.h"
+
+namespace mozilla {
+
+class GeckoProcessManager final
+    : public java::GeckoProcessManager::Natives<GeckoProcessManager>
+{
+    GeckoProcessManager() = delete;
+
+    static already_AddRefed<nsIWidget>
+    GetWidget(int64_t aContentId, int64_t aTabId)
+    {
+        using namespace dom;
+        MOZ_ASSERT(NS_IsMainThread());
+
+        ContentProcessManager* const cpm =
+            ContentProcessManager::GetSingleton();
+        NS_ENSURE_TRUE(cpm, nullptr);
+
+        RefPtr<TabParent> tab = cpm->GetTopLevelTabParentByProcessAndTabId(
+                ContentParentId(aContentId), TabId(aTabId));
+        NS_ENSURE_TRUE(tab, nullptr);
+
+        nsCOMPtr<nsPIDOMWindowOuter> domWin = tab->GetParentWindowOuter();
+        NS_ENSURE_TRUE(domWin, nullptr);
+
+        return WidgetUtils::DOMWindowToWidget(domWin);
+    }
+
+public:
+    static jni::Object::LocalRef
+    GetEditableParent(int64_t aContentId, int64_t aTabId)
+    {
+        // On binder thread.
+        jni::Object::GlobalRef ret;
+        nsAppShell::SyncRunEvent([aContentId, aTabId, &ret] {
+            nsCOMPtr<nsIWidget> widget = GetWidget(aContentId, aTabId);
+            if (widget) {
+                ret = static_cast<nsWindow*>(widget.get())->GetEditableParent();
+            }
+        });
+        return ret;
+    }
+};
+
+} // namespace mozilla
+
+#endif // GeckoProcessManager_h
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -454,11 +454,26 @@ public:
 template<class Impl>
 const JNINativeMethod VsyncSource::Natives<Impl>::methods[] = {
 
     mozilla::jni::MakeNativeMethod<VsyncSource::NotifyVsync_t>(
             mozilla::jni::NativeStub<VsyncSource::NotifyVsync_t, Impl>
             ::template Wrap<&Impl::NotifyVsync>)
 };
 
+template<class Impl>
+class GeckoProcessManager::Natives : public mozilla::jni::NativeImpl<GeckoProcessManager, Impl>
+{
+public:
+    static const JNINativeMethod methods[1];
+};
+
+template<class Impl>
+const JNINativeMethod GeckoProcessManager::Natives<Impl>::methods[] = {
+
+    mozilla::jni::MakeNativeMethod<GeckoProcessManager::GetEditableParent_t>(
+            mozilla::jni::NativeStub<GeckoProcessManager::GetEditableParent_t, Impl>
+            ::template Wrap<&Impl::GetEditableParent>)
+};
+
 } /* java */
 } /* mozilla */
 #endif // GeneratedJNINatives_h
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -854,16 +854,24 @@ const char GeckoThread::name[] =
 constexpr char GeckoThread::CheckAndSetState_t::name[];
 constexpr char GeckoThread::CheckAndSetState_t::signature[];
 
 auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> bool
 {
     return mozilla::jni::Method<CheckAndSetState_t>::Call(GeckoThread::Context(), nullptr, a0, a1);
 }
 
+constexpr char GeckoThread::IsChildProcess_t::name[];
+constexpr char GeckoThread::IsChildProcess_t::signature[];
+
+auto GeckoThread::IsChildProcess() -> bool
+{
+    return mozilla::jni::Method<IsChildProcess_t>::Call(GeckoThread::Context(), nullptr);
+}
+
 constexpr char GeckoThread::CreateServices_t::name[];
 constexpr char GeckoThread::CreateServices_t::signature[];
 
 constexpr char GeckoThread::OnPause_t::name[];
 constexpr char GeckoThread::OnPause_t::signature[];
 
 constexpr char GeckoThread::OnResume_t::name[];
 constexpr char GeckoThread::OnResume_t::signature[];
@@ -1745,16 +1753,33 @@ auto VsyncSource::ObserveVsync(bool a0) 
 constexpr char VsyncSource::INSTANCE_t::name[];
 constexpr char VsyncSource::INSTANCE_t::signature[];
 
 auto VsyncSource::INSTANCE() -> VsyncSource::LocalRef
 {
     return mozilla::jni::Field<INSTANCE_t>::Get(VsyncSource::Context(), nullptr);
 }
 
+const char GeckoProcessManager::name[] =
+        "org/mozilla/gecko/process/GeckoProcessManager";
+
+constexpr char GeckoProcessManager::GetEditableParent_t::name[];
+constexpr char GeckoProcessManager::GetEditableParent_t::signature[];
+
+const char GeckoServiceChildProcess::name[] =
+        "org/mozilla/gecko/process/GeckoServiceChildProcess";
+
+constexpr char GeckoServiceChildProcess::GetEditableParent_t::name[];
+constexpr char GeckoServiceChildProcess::GetEditableParent_t::signature[];
+
+auto GeckoServiceChildProcess::GetEditableParent(int64_t a0, int64_t a1) -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<GetEditableParent_t>::Call(GeckoServiceChildProcess::Context(), nullptr, a0, a1);
+}
+
 const char Clipboard::name[] =
         "org/mozilla/gecko/util/Clipboard";
 
 constexpr char Clipboard::ClearText_t::name[];
 constexpr char Clipboard::ClearText_t::signature[];
 
 auto Clipboard::ClearText() -> void
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2455,16 +2455,18 @@ public:
     static const char name[];
 
     explicit GeckoEditableListener(const Context& ctx) : ObjectBase<GeckoEditableListener>(ctx) {}
 
     static const int32_t NOTIFY_IME_OF_BLUR = 2;
 
     static const int32_t NOTIFY_IME_OF_FOCUS = 1;
 
+    static const int32_t NOTIFY_IME_OF_TOKEN = -3;
+
     static const int32_t NOTIFY_IME_OPEN_VKB = -2;
 
     static const int32_t NOTIFY_IME_REPLY_EVENT = -1;
 
     static const int32_t NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
 
     static const int32_t NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
 
@@ -2582,16 +2584,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto CheckAndSetState(mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> bool;
 
+    struct IsChildProcess_t {
+        typedef GeckoThread Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "isChildProcess";
+        static constexpr char signature[] =
+                "()Z";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto IsChildProcess() -> bool;
+
     struct CreateServices_t {
         typedef GeckoThread Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param,
                 mozilla::jni::String::Param> Args;
         static constexpr char name[] = "nativeCreateServices";
@@ -4998,16 +5019,81 @@ public:
     static auto INSTANCE() -> VsyncSource::LocalRef;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
+class GeckoProcessManager : public mozilla::jni::ObjectBase<GeckoProcessManager>
+{
+public:
+    static const char name[];
+
+    explicit GeckoProcessManager(const Context& ctx) : ObjectBase<GeckoProcessManager>(ctx) {}
+
+    struct GetEditableParent_t {
+        typedef GeckoProcessManager Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<
+                int64_t,
+                int64_t> Args;
+        static constexpr char name[] = "nativeGetEditableParent";
+        static constexpr char signature[] =
+                "(JJ)Lorg/mozilla/gecko/IGeckoEditableParent;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+    template<class Impl> class Natives;
+};
+
+class GeckoServiceChildProcess : public mozilla::jni::ObjectBase<GeckoServiceChildProcess>
+{
+public:
+    static const char name[];
+
+    explicit GeckoServiceChildProcess(const Context& ctx) : ObjectBase<GeckoServiceChildProcess>(ctx) {}
+
+    struct GetEditableParent_t {
+        typedef GeckoServiceChildProcess Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<
+                int64_t,
+                int64_t> Args;
+        static constexpr char name[] = "getEditableParent";
+        static constexpr char signature[] =
+                "(JJ)Lorg/mozilla/gecko/IGeckoEditableParent;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto GetEditableParent(int64_t, int64_t) -> mozilla::jni::Object::LocalRef;
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::GECKO;
+
+};
+
 class Clipboard : public mozilla::jni::ObjectBase<Clipboard>
 {
 public:
     static const char name[];
 
     explicit Clipboard(const Context& ctx) : ObjectBase<Clipboard>(ctx) {}
 
     struct ClearText_t {
--- a/widget/android/jni/Utils.cpp
+++ b/widget/android/jni/Utils.cpp
@@ -119,16 +119,22 @@ void SetGeckoThreadEnv(JNIEnv* aEnv)
     aEnv->ExceptionClear();
 
     sClassLoader = Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
     sClassLoaderLoadClass = aEnv->GetMethodID(
             Class::LocalRef::Adopt(aEnv->GetObjectClass(sClassLoader)).Get(),
             "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
     MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
 
+    if (java::GeckoThread::IsChildProcess()) {
+        // Disallow Fennec-only classes from being used in child processes.
+        sIsFennec = false;
+        return;
+    }
+
     auto geckoAppClass = Class::LocalRef::Adopt(
             aEnv->FindClass("org/mozilla/gecko/GeckoApp"));
     aEnv->ExceptionClear();
     sIsFennec = !!geckoAppClass;
 }
 
 JNIEnv* GetEnvForThread()
 {
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -19,27 +19,29 @@
 #include "nsIGeolocationProvider.h"
 #include "nsCacheService.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMClientRectList.h"
 #include "nsIDOMClientRect.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "nsISpeculativeConnect.h"
+#include "nsITabChild.h"
 #include "nsIURIFixup.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsToolkitCompsCID.h"
 #include "nsGeoPosition.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Hal.h"
+#include "mozilla/dom/TabChild.h"
 #include "prenv.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "GeneratedJNINatives.h"
 #include <android/log.h>
 #include <pthread.h>
 #include <wchar.h>
@@ -60,16 +62,17 @@
 #include "nsExceptionHandler.h"
 #endif
 
 #include "AndroidAlerts.h"
 #include "AndroidUiThread.h"
 #include "ANRReporter.h"
 #include "GeckoBatteryManager.h"
 #include "GeckoNetworkManager.h"
+#include "GeckoProcessManager.h"
 #include "GeckoScreenOrientation.h"
 #include "PrefsHelper.h"
 #include "fennec/MemoryMonitor.h"
 #include "fennec/Telemetry.h"
 #include "fennec/ThumbnailHelper.h"
 
 #ifdef DEBUG_ANDROID_EVENTS
 #define EVLOG(args...)  ALOG(args)
@@ -379,26 +382,31 @@ nsAppShell::nsAppShell()
     , mSyncRunQuit(false)
 {
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = this;
     }
 
     if (!XRE_IsParentProcess()) {
+        if (jni::IsAvailable()) {
+            // Set the corresponding state in GeckoThread.
+            java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
+        }
         return;
     }
 
     if (jni::IsAvailable()) {
         // Initialize JNI and Set the corresponding state in GeckoThread.
         AndroidBridge::ConstructBridge();
         GeckoAppShellSupport::Init();
         GeckoThreadSupport::Init();
         mozilla::GeckoBatteryManager::Init();
         mozilla::GeckoNetworkManager::Init();
+        mozilla::GeckoProcessManager::Init();
         mozilla::GeckoScreenOrientation::Init();
         mozilla::PrefsHelper::Init();
         nsWindow::InitNatives();
 
         if (jni::IsFennec()) {
             mozilla::ANRReporter::Init();
             mozilla::MemoryMonitor::Init();
             mozilla::widget::Telemetry::Init();
@@ -432,17 +440,17 @@ nsAppShell::~nsAppShell()
 
     if (sPowerManagerService) {
         sPowerManagerService->RemoveWakeLockListener(sWakeLockListener);
 
         sPowerManagerService = nullptr;
         sWakeLockListener = nullptr;
     }
 
-    if (jni::IsAvailable()) {
+    if (jni::IsAvailable() && XRE_IsParentProcess()) {
         DestroyAndroidUiThread();
         AndroidBridge::DeconstructBridge();
     }
 }
 
 void
 nsAppShell::NotifyNativeEvent()
 {
@@ -497,16 +505,17 @@ nsAppShell::Init()
 {
     nsresult rv = nsBaseAppShell::Init();
     nsCOMPtr<nsIObserverService> obsServ =
         mozilla::services::GetObserverService();
     if (obsServ) {
         obsServ->AddObserver(this, "browser-delayed-startup-finished", false);
         obsServ->AddObserver(this, "profile-after-change", false);
         obsServ->AddObserver(this, "chrome-document-loaded", false);
+        obsServ->AddObserver(this, "tab-child-created", false);
         obsServ->AddObserver(this, "quit-application-granted", false);
         obsServ->AddObserver(this, "xpcom-shutdown", false);
     }
 
     if (sPowerManagerService)
         sPowerManagerService->AddWakeLockListener(sWakeLockListener);
 
     Preferences::AddStrongObservers(this, kObservedPrefs);
@@ -590,16 +599,47 @@ nsAppShell::Observe(nsISupports* aSubjec
             }
         }
         removeObserver = true;
 
     } else if (!strcmp(aTopic, "nsPref:changed")) {
         if (jni::IsAvailable()) {
             mozilla::PrefsHelper::OnPrefChange(aData);
         }
+
+    } else if (!strcmp(aTopic, "tab-child-created")) {
+        // Associate the PuppetWidget of the newly-created TabChild with a
+        // GeckoEditableChild instance.
+        MOZ_ASSERT(!XRE_IsParentProcess());
+
+        dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+        nsCOMPtr<nsITabChild> ptabChild = do_QueryInterface(aSubject);
+        NS_ENSURE_TRUE(contentChild && ptabChild, NS_OK);
+
+        // Get the content/tab ID in order to get the correct
+        // IGeckoEditableParent object, which GeckoEditableChild uses to
+        // communicate with the parent process.
+        const auto tabChild = static_cast<dom::TabChild*>(ptabChild.get());
+        const uint64_t contentId = contentChild->GetID();
+        const uint64_t tabId = tabChild->GetTabId();
+        NS_ENSURE_TRUE(contentId && tabId, NS_OK);
+
+        auto editableParent = java::GeckoServiceChildProcess::GetEditableParent(
+                contentId, tabId);
+        NS_ENSURE_TRUE(editableParent, NS_OK);
+
+        RefPtr<widget::PuppetWidget> widget(tabChild->WebWidget());
+        auto editableChild = java::GeckoEditableChild::New(editableParent);
+        NS_ENSURE_TRUE(widget && editableChild, NS_OK);
+
+        RefPtr<GeckoEditableSupport> editableSupport =
+                new GeckoEditableSupport(editableChild);
+
+        // Tell PuppetWidget to use our listener for IME operations.
+        widget->SetNativeTextEventDispatcherListener(editableSupport);
     }
 
     if (removeObserver) {
         nsCOMPtr<nsIObserverService> obsServ =
             mozilla::services::GetObserverService();
         if (obsServ) {
             obsServ->RemoveObserver(this, aTopic);
         }
@@ -621,17 +661,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
         curEvent = mEventQueue.Pop(/* mayWait */ false);
 
         if (!curEvent && mayWait) {
             // This processes messages in the Android Looper. Note that we only
             // get here if the normal Gecko event loop has been awoken
             // (bug 750713). Looper messages effectively have the lowest
             // priority because we only process them before we're about to
             // wait for new events.
-            if (jni::IsAvailable() &&
+            if (jni::IsAvailable() && XRE_IsParentProcess() &&
                     AndroidBridge::Bridge()->PumpMessageLoop()) {
                 return true;
             }
 
             PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent::Wait",
                 js::ProfileEntry::Category::EVENTS);
             mozilla::HangMonitor::Suspend();
 
--- a/widget/android/nsAppShell.h
+++ b/widget/android/nsAppShell.h
@@ -8,16 +8,17 @@
 
 #include <time.h>
 
 #include "mozilla/HangMonitor.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Move.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/jni/Natives.h"
 #include "nsBaseAppShell.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIAndroidBridge.h"
@@ -142,16 +143,23 @@ public:
                 mozilla::Move(lambda)));
     }
 
     // Post a event and wait for it to finish running on the Gecko thread.
     static void SyncRunEvent(Event&& event,
                              mozilla::UniquePtr<Event>(*eventFactory)(
                                     mozilla::UniquePtr<Event>&&) = nullptr);
 
+    template<typename T> static
+    typename mozilla::EnableIf<!mozilla::IsBaseOf<Event, T>::value, void>::Type
+    SyncRunEvent(T&& lambda)
+    {
+        SyncRunEvent(LambdaEvent<T>(mozilla::Forward<T>(lambda)));
+    }
+
     static already_AddRefed<nsIURI> ResolveURI(const nsCString& aUriStr);
 
     void SetBrowserApp(nsIAndroidBrowserApp* aBrowserApp) {
         mBrowserApp = aBrowserApp;
     }
 
     nsIAndroidBrowserApp* GetBrowserApp() {
         return mBrowserApp;
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2116,30 +2116,16 @@ nsWindow::GetInputContext()
         return InputContext();
     }
 
     // We let the top window process SetInputContext,
     // so we should let it process GetInputContext as well.
     return top->mEditableSupport->GetInputContext();
 }
 
-nsIMEUpdatePreference
-nsWindow::GetIMEUpdatePreference()
-{
-    nsWindow* top = FindTopLevel();
-    MOZ_ASSERT(top);
-
-    if (!top->mEditableSupport) {
-        // Non-GeckoView windows don't support IME operations.
-        return nsIMEUpdatePreference();
-    }
-
-    return top->mEditableSupport->GetIMEUpdatePreference();
-}
-
 nsresult
 nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                      TouchPointerState aPointerState,
                                      LayoutDeviceIntPoint aPoint,
                                      double aPointerPressure,
                                      uint32_t aPointerOrientation,
                                      nsIObserver* aObserver)
 {
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -125,17 +125,19 @@ public:
         };
 
         WindowPtr(NativePtr<Impl>* aPtr, nsWindow* aWindow)
             : mPtr(aPtr)
             , mWindow(aWindow)
             , mWindowLock(NativePtr<Impl>::sName)
         {
             MOZ_ASSERT(NS_IsMainThread());
-            mPtr->mPtr = this;
+            if (mPtr) {
+                mPtr->mPtr = this;
+            }
         }
 
         ~WindowPtr()
         {
             MOZ_ASSERT(NS_IsMainThread());
             if (!mPtr) {
                 return;
             }
@@ -267,17 +269,16 @@ public:
     virtual nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; }
     virtual MOZ_MUST_USE nsresult GetAttention(int32_t aCycleCount) override { return NS_ERROR_NOT_IMPLEMENTED; }
 
 
     TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override;
     virtual void SetInputContext(const InputContext& aContext,
                                  const InputContextAction& aAction) override;
     virtual InputContext GetInputContext() override;
-    virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
 
     void SetSelectionDragState(bool aState);
     LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                   LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                   LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override;
 
     virtual bool NeedsPaint() override;
     virtual bool PreRender(mozilla::widget::WidgetRenderingContext* aContext) override;
@@ -310,16 +311,18 @@ public:
     CompositorBridgeChild* GetCompositorBridgeChild() const;
 
     mozilla::jni::DependentRef<mozilla::java::GeckoLayerClient> GetLayerClient();
 
     // Call this function when the users activity is the direct cause of an
     // event (like a keypress or mouse click).
     void UserActivity();
 
+    mozilla::java::GeckoEditable::Ref& GetEditableParent() { return mEditable; }
+
 protected:
     void BringToFront();
     nsWindow *FindTopLevel();
     bool IsTopLevel();
 
     void ConfigureAPZControllerThread() override;
     void DispatchHitTest(const mozilla::WidgetTouchEvent& aEvent);
 
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -769,16 +769,17 @@ private:
  */
 
 class IMEInputHandler : public TextInputHandlerBase
 {
 public:
   // TextEventDispatcherListener methods
   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                        const IMENotification& aNotification) override;
+  NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
   NS_IMETHOD_(void) OnRemovedFrom(
                       TextEventDispatcher* aTextEventDispatcher) override;
   NS_IMETHOD_(void) WillDispatchKeyboardEvent(
                       TextEventDispatcher* aTextEventDispatcher,
                       WidgetKeyboardEvent& aKeyboardEvent,
                       uint32_t aIndexOfKeypress,
                       void* aData) override;
 
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -2622,16 +2622,25 @@ IMEInputHandler::NotifyIME(TextEventDisp
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       OnSelectionChange(aNotification);
       return NS_OK;
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
+NS_IMETHODIMP_(nsIMEUpdatePreference)
+IMEInputHandler::GetIMEUpdatePreference()
+{
+  // XXX Shouldn't we move floating window which shows composition string
+  //     when plugin has focus and its parent is scrolled or the window is
+  //     moved?
+  return nsIMEUpdatePreference();
+}
+
 NS_IMETHODIMP_(void)
 IMEInputHandler::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
 {
   // XXX When input transaction is being stolen by add-on, what should we do?
 }
 
 NS_IMETHODIMP_(void)
 IMEInputHandler::WillDispatchKeyboardEvent(
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -393,17 +393,16 @@ public:
                       void* aCallbackData) override;
   bool ExecuteNativeKeyBindingRemapped(
                       NativeKeyBindingsType aType,
                       const mozilla::WidgetKeyboardEvent& aEvent,
                       DoCommandCallback aCallback,
                       void* aCallbackData,
                       uint32_t aGeckoKeyCode,
                       uint32_t aCocoaKeyCode);
-  virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
 
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void                SetTransparencyMode(nsTransparencyMode aMode) override;
 
   virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                             int32_t aNativeKeyCode,
                                             uint32_t aModifierFlags,
                                             const nsAString& aCharacters,
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1888,25 +1888,16 @@ nsChildView::ExecuteNativeKeyBinding(Nat
                                              geckoKey, cocoaKey);
     }
   }
 
   NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
   return keyBindings->Execute(aEvent, aCallback, aCallbackData);
 }
 
-nsIMEUpdatePreference
-nsChildView::GetIMEUpdatePreference()
-{
-  // XXX Shouldn't we move floating window which shows composition string
-  //     when plugin has focus and its parent is scrolled or the window is
-  //     moved?
-  return nsIMEUpdatePreference();
-}
-
 NSView<mozView>* nsChildView::GetEditorView()
 {
   NSView<mozView>* editorView = mView;
   // We need to get editor's view. E.g., when the focus is in the bookmark
   // dialog, the view is <panel> element of the dialog.  At this time, the key
   // events are processed the parent window's view that has native focus.
   WidgetQueryContentEvent textContent(true, eQueryTextContent, this);
   textContent.InitForQueryTextContent(0, 0);
--- a/widget/gtk/IMContextWrapper.cpp
+++ b/widget/gtk/IMContextWrapper.cpp
@@ -319,18 +319,18 @@ IMContextWrapper::GetTextEventDispatcher
   }
   TextEventDispatcher* dispatcher =
     mLastFocusedWindow->GetTextEventDispatcher();
   // nsIWidget::GetTextEventDispatcher() shouldn't return nullptr.
   MOZ_RELEASE_ASSERT(dispatcher);
   return dispatcher;
 }
 
-nsIMEUpdatePreference
-IMContextWrapper::GetIMEUpdatePreference() const
+NS_IMETHODIMP_(nsIMEUpdatePreference)
+IMContextWrapper::GetIMEUpdatePreference()
 {
     // While a plugin has focus, IMContextWrapper doesn't need any
     // notifications.
     if (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
       return nsIMEUpdatePreference();
     }
 
     nsIMEUpdatePreference::Notifications notifications =
--- a/widget/gtk/IMContextWrapper.h
+++ b/widget/gtk/IMContextWrapper.h
@@ -28,16 +28,17 @@ namespace widget {
 class IMContextWrapper final : public TextEventDispatcherListener
 {
 public:
     // TextEventDispatcherListener implementation
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                          const IMENotification& aNotification) override;
+    NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
     NS_IMETHOD_(void) OnRemovedFrom(
                           TextEventDispatcher* aTextEventDispatcher) override;
     NS_IMETHOD_(void) WillDispatchKeyboardEvent(
                           TextEventDispatcher* aTextEventDispatcher,
                           WidgetKeyboardEvent& aKeyboardEvent,
                           uint32_t aIndexOfKeypress,
                           void* aData) override;
 
@@ -46,18 +47,16 @@ public:
     // destroyed, the related IME contexts are released (i.e., IME cannot be
     // used with the instance after that).
     explicit IMContextWrapper(nsWindow* aOwnerWindow);
 
     // "Enabled" means the users can use all IMEs.
     // I.e., the focus is in the normal editors.
     bool IsEnabled() const;
 
-    nsIMEUpdatePreference GetIMEUpdatePreference() const;
-
     // OnFocusWindow is a notification that aWindow is going to be focused.
     void OnFocusWindow(nsWindow* aWindow);
     // OnBlurWindow is a notification that aWindow is going to be unfocused.
     void OnBlurWindow(nsWindow* aWindow);
     // OnDestroyWindow is a notification that aWindow is going to be destroyed.
     void OnDestroyWindow(nsWindow* aWindow);
     // OnFocusChangeInGecko is a notification that an editor gets focus.
     void OnFocusChangeInGecko(bool aFocus);
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6094,25 +6094,16 @@ nsWindow::GetInputContext()
       context.mIMEState.mEnabled = IMEState::DISABLED;
       context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
   } else {
       context = mIMContext->GetInputContext();
   }
   return context;
 }
 
-nsIMEUpdatePreference
-nsWindow::GetIMEUpdatePreference()
-{
-    if (!mIMContext) {
-        return nsIMEUpdatePreference();
-    }
-    return mIMContext->GetIMEUpdatePreference();
-}
-
 TextEventDispatcherListener*
 nsWindow::GetNativeTextEventDispatcherListener()
 {
     if (NS_WARN_IF(!mIMContext)) {
         return nullptr;
     }
     return mIMContext;
 }
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -269,17 +269,16 @@ public:
                                             bool *aIsCancelled);
     WidgetEventTime    GetWidgetEventTime(guint32 aEventTime);
     mozilla::TimeStamp GetEventTimeStamp(guint32 aEventTime);
     mozilla::CurrentX11TimeGetter* GetCurrentTimeGetter();
 
     virtual void SetInputContext(const InputContext& aContext,
                                  const InputContextAction& aAction) override;
     virtual InputContext GetInputContext() override;
-    virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
     virtual TextEventDispatcherListener*
         GetNativeTextEventDispatcherListener() override;
     bool ExecuteNativeKeyBindingRemapped(
                         NativeKeyBindingsType aType,
                         const mozilla::WidgetKeyboardEvent& aEvent,
                         DoCommandCallback aCallback,
                         void* aCallbackData,
                         uint32_t aGeckoKeyCode,
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1808,16 +1808,28 @@ nsBaseWidget::NotifyIME(const IMENotific
       if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
         mIMEHasFocus = false;
       }
       return rv2 == NS_ERROR_NOT_IMPLEMENTED ? rv : rv2;
     }
   }
 }
 
+nsIMEUpdatePreference
+nsBaseWidget::GetIMEUpdatePreference()
+{
+  RefPtr<TextEventDispatcherListener> listener =
+    GetNativeTextEventDispatcherListener();
+  if (!listener) {
+    // Default is to not send additional change notifications to NotifyIME.
+    return nsIMEUpdatePreference();
+  }
+  return listener->GetIMEUpdatePreference();
+}
+
 void
 nsBaseWidget::EnsureTextEventDispatcher()
 {
   if (mTextEventDispatcher) {
     return;
   }
   mTextEventDispatcher = new TextEventDispatcher(this);
 }
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -283,17 +283,17 @@ public:
   virtual MOZ_MUST_USE nsresult AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) override { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual bool            ExecuteNativeKeyBinding(
                             NativeKeyBindingsType aType,
                             const mozilla::WidgetKeyboardEvent& aEvent,
                             DoCommandCallback aCallback,
                             void* aCallbackData) override { return false; }
   bool                    ComputeShouldAccelerate();
   virtual bool            WidgetTypeSupportsAcceleration() { return true; }
-  virtual nsIMEUpdatePreference GetIMEUpdatePreference() override { return nsIMEUpdatePreference(); }
+  virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
   virtual MOZ_MUST_USE nsresult OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect) override { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual already_AddRefed<nsIWidget>
   CreateChild(const LayoutDeviceIntRect& aRect,
               nsWidgetInitData* aInitData = nullptr,
               bool aForceUseIWidgetParent = false) override;
   virtual void            AttachViewToTopLevel(bool aUseAttachedEvents) override;
   virtual nsIWidgetListener* GetAttachedWidgetListener() override;
   virtual void               SetAttachedWidgetListener(nsIWidgetListener* aListener) override;
--- a/widget/windows/WinTextEventDispatcherListener.cpp
+++ b/widget/windows/WinTextEventDispatcherListener.cpp
@@ -51,16 +51,22 @@ WinTextEventDispatcherListener::NotifyIM
 {
   nsWindow* window = static_cast<nsWindow*>(aTextEventDispatcher->GetWidget());
   if (NS_WARN_IF(!window)) {
     return NS_ERROR_FAILURE;
   }
   return IMEHandler::NotifyIME(window, aNotification);
 }
 
+NS_IMETHODIMP_(nsIMEUpdatePreference)
+WinTextEventDispatcherListener::GetIMEUpdatePreference()
+{
+  return IMEHandler::GetUpdatePreference();
+}
+
 NS_IMETHODIMP_(void)
 WinTextEventDispatcherListener::OnRemovedFrom(
                                   TextEventDispatcher* aTextEventDispatcher)
 {
   // XXX When input transaction is being stolen by add-on, what should we do?
 }
 
 NS_IMETHODIMP_(void)
--- a/widget/windows/WinTextEventDispatcherListener.h
+++ b/widget/windows/WinTextEventDispatcherListener.h
@@ -24,16 +24,17 @@ class WinTextEventDispatcherListener fin
 public:
   static WinTextEventDispatcherListener* GetInstance();
   static void Shutdown();
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                        const IMENotification& aNotification) override;
+  NS_IMETHOD_(nsIMEUpdatePreference) GetIMEUpdatePreference() override;
   NS_IMETHOD_(void) OnRemovedFrom(
                       TextEventDispatcher* aTextEventDispatcher) override;
   NS_IMETHOD_(void) WillDispatchKeyboardEvent(
                       TextEventDispatcher* aTextEventDispatcher,
                       WidgetKeyboardEvent& aKeyboardEvent,
                       uint32_t aIndexOfKeypress,
                       void* aData) override;
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -7220,22 +7220,16 @@ nsWindow::GetInputContext()
   if (WinUtils::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
     mInputContext.mIMEState.mOpen = IMEState::OPEN;
   } else {
     mInputContext.mIMEState.mOpen = IMEState::CLOSED;
   }
   return mInputContext;
 }
 
-nsIMEUpdatePreference
-nsWindow::GetIMEUpdatePreference()
-{
-  return IMEHandler::GetUpdatePreference();
-}
-
 TextEventDispatcherListener*
 nsWindow::GetNativeTextEventDispatcherListener()
 {
   return IMEHandler::GetNativeTextEventDispatcherListener();
 }
 
 #ifdef ACCESSIBILITY
 #ifdef DEBUG
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -205,17 +205,16 @@ public:
   virtual InputContext    GetInputContext() override;
   virtual TextEventDispatcherListener*
     GetNativeTextEventDispatcherListener() override;
 #ifdef MOZ_XUL
   virtual void            SetTransparencyMode(nsTransparencyMode aMode) override;
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void            UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) override;
 #endif // MOZ_XUL
-  virtual nsIMEUpdatePreference GetIMEUpdatePreference() override;
   virtual nsresult        SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override;
   void                    SetDrawsInTitlebar(bool aState) override;
   virtual void            UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) override;
 
   virtual void            UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override;
   virtual uint32_t        GetMaxTouchPoints() const override;
 
   /**
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -17,16 +17,20 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Vector.h"
 #include "mozilla/TimeStamp.h"
 #include "XREChildData.h"
 #include "XREShellData.h"
 
+#if defined(MOZ_WIDGET_ANDROID)
+#include <jni.h>
+#endif
+
 /**
  * A directory service key which provides the platform-correct "application
  * data" directory as follows, where $name and $vendor are as defined above and
  * $vendor is optional:
  *
  * Windows:
  *   HOME = Documents and Settings\$USER\Application Data
  *   UAppData = $HOME[\$vendor]\$name
@@ -402,17 +406,17 @@ static_assert(MOZ_ARRAY_LENGTH(kGeckoPro
               GeckoProcessType_End,
               "Array length mismatch");
 
 XRE_API(const char*,
         XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
 
 #if defined(MOZ_WIDGET_ANDROID)
 XRE_API(void,
-        XRE_SetAndroidChildFds, (int crashFd, int ipcFd))
+        XRE_SetAndroidChildFds, (JNIEnv* env, int crashFd, int ipcFd))
 #endif // defined(MOZ_WIDGET_ANDROID)
 
 XRE_API(void,
         XRE_SetProcessType, (const char* aProcessTypeString))
 
 #if defined(MOZ_CRASHREPORTER)
 // Used in the "master" parent process hosting the crash server
 XRE_API(bool,