diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index dfbdda773deb..41b7bcdf89a2 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -458,10 +458,17 @@ final class GeckoEditable // GeckoEditableClient interface @Override - public void sendEvent(GeckoEvent event) { - if (DEBUG) { - // GeckoEditableClient methods should all be called from the IC thread - assertOnIcThread(); + public void sendEvent(final GeckoEvent event) { + if (!onIcThread()) { + // Events may get dispatched to the main thread; + // reroute to our IC thread instead + mIcRunHandler.post(new Runnable() { + @Override + public void run() { + sendEvent(event); + } + }); + return; } /* We are actually sending two events to Gecko here, @@ -477,18 +484,24 @@ final class GeckoEditable @Override public Editable getEditable() { - if (DEBUG) { - // GeckoEditableClient methods should all be called from the IC thread - assertOnIcThread(); + if (!onIcThread()) { + // Android may be holding an old InputConnection; ignore + if (DEBUG) { + Log.i(LOGTAG, "getEditable() called on non-IC thread"); + } + return null; } return mProxy; } @Override public void setUpdateGecko(boolean update) { - if (DEBUG) { - // GeckoEditableClient methods should all be called from the IC thread - assertOnIcThread(); + if (!onIcThread()) { + // Android may be holding an old InputConnection; ignore + if (DEBUG) { + Log.i(LOGTAG, "setUpdateGecko() called on non-IC thread"); + } + return; } if (update) { icUpdateGecko(false); diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index 3e8a108531fb..ae403fed35e2 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -481,6 +481,40 @@ class GeckoInputConnection return this; } + @Override + public boolean sendKeyEvent(KeyEvent event) { + // BaseInputConnection.sendKeyEvent() dispatches the key event to the main thread. + // In order to ensure events are processed in the proper order, we must block the + // IC thread until the main thread finishes processing the key event + super.sendKeyEvent(event); + final View v = getView(); + if (v == null) { + return false; + } + final Handler icHandler = mEditableClient.getInputConnectionHandler(); + final Handler mainHandler = v.getRootView().getHandler(); + if (icHandler.getLooper() != mainHandler.getLooper()) { + // We are on separate IC thread but the event is queued on the main thread; + // wait on IC thread until the main thread processes our posted Runnable. At + // that point the key event has already been processed. + synchronized (icHandler) { + mainHandler.post(new Runnable() { + @Override + public void run() { + synchronized (icHandler) { + icHandler.notify(); + } + } + }); + try { + icHandler.wait(); + } catch (InterruptedException e) { + } + } + } + return false; // seems to always return false + } + public boolean onKeyPreIme(int keyCode, KeyEvent event) { return false; }