Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: handles render crash and navigation Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ebfa85722be9e0d8afd1bc3b9226b5f791faf69
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
@@ -0,0 +1,168 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser.input;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import org.chromium.base.Log;
+import org.chromium.content.browser.input.ChromiumBaseInputConnection.ThreadManager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Default factory for ChromiumBaseInputConnection classes.
+ */
+public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnection.Factory {
+ private static final String TAG = "cr_Ime";
+
+ private ThreadedInputConnection mChromiumInputConnection;
+ private final Handler mHandler;
+ private final ThreadedInputConnection.ThreadManager mThreadManager;
+
+ ThreadedInputConnectionFactory() {
+ HandlerThread thread =
+ new HandlerThread("InputConnectionHandlerThread", HandlerThread.NORM_PRIORITY);
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ mThreadManager = new ThreadManager(mHandler);
+ }
+
+ @Override
+ public ChromiumBaseInputConnection initializeAndGet(View view, ImeAdapter imeAdapter,
+ int inputType, int inputFlags, EditorInfo outAttrs) {
+ ImeUtils.assertOnUiThread();
+ if (mChromiumInputConnection == null) {
+ Log.d(TAG, "Creating ChromiumInputConnection...");
+ mChromiumInputConnection = new ThreadedInputConnection(imeAdapter, mThreadManager);
+ }
+ mChromiumInputConnection.initializeOutAttrsOnUiThread(inputType, inputFlags, outAttrs);
+ switchInputConnectionLooper(view.getContext(), view.getHandler());
+ return mChromiumInputConnection;
+ }
+
+ @Override
+ public ThreadManager getThreadManager() {
+ return mThreadManager;
+ }
+
+ /**
+ * Pump messages from a handler and add them to another handler.
+ *
+ * @param sourceHandler The source handler.
+ * @param targetHandler The target handler.
+ * @throws NoSuchFieldException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ * @throws NoSuchMethodException
+ * @throws InvocationTargetException
+ */
+ private void pumpHandlerMessages(Handler sourceHandler, Handler targetHandler)
Ted C 2016/02/02 23:14:43 yikes...this method is...terrifying. I know your
aelias_OOO_until_Jul13 2016/02/02 23:51:56 I chatted offline with Ted about this. Here's wha
Changwan Ryu 2016/02/03 00:02:47 Thanks for the feedback and new idea. I'll investi
Changwan Ryu 2016/02/11 16:21:08 I've explored many different ideas, and switched t
+ throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException,
+ NoSuchMethodException, InvocationTargetException {
+ Looper looper = sourceHandler.getLooper();
+ Method getQueueMethod = Looper.class.getDeclaredMethod("getQueue");
+ getQueueMethod.setAccessible(true);
+ MessageQueue queue = (MessageQueue) getQueueMethod.invoke(looper);
+ getQueueMethod.setAccessible(false);
+
+ Field messagesField = MessageQueue.class.getDeclaredField("mMessages");
+ Field nextField = Message.class.getDeclaredField("next");
+
+ synchronized (queue) {
+ // Copy messages first.
+ messagesField.setAccessible(true);
+ nextField.setAccessible(true);
+ Message msg = (Message) messagesField.get(queue);
+ while (msg != null) {
+ if (msg.getTarget() == sourceHandler) {
+ Log.d(TAG, "Copying a message to the new handler...");
+ Message newMsg = targetHandler.obtainMessage();
+ newMsg.copyFrom(msg);
+ targetHandler.sendMessage(newMsg);
Ted C 2016/02/02 23:14:43 This looks like it would clobber any delayed-ness
Changwan Ryu 2016/02/11 16:21:08 I've switched to another approach. I'll fix this o
+ }
+ msg = (Message) nextField.get(msg);
+ }
+ messagesField.setAccessible(false);
+ nextField.setAccessible(false);
+
+ // Now remove messages from the first handler.
+ sourceHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ /**
+ * Switch the looper in InputMethodManager so that future calls to InputConnection
+ * can run in IME thread.
+ *
+ * @param context The context
+ * @param viewHandler The current handler for the View that this InputConnection will be
+ * attached to.
+ */
+ private void switchInputConnectionLooper(Context context, Handler viewHandler) {
+ Log.d(TAG, "switchInputConnectionLooper");
+ // Retrieve the same singleton instance of InputMethodManager that called
+ // onCreateInputConnection().
+ final InputMethodManager inputMethodManager =
+ (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ final Handler icHandler = mHandler;
+ viewHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "switchInputConnectionLooper: replacing looper and handler");
+ try {
+ // inputMethodManager.mServedInputConnectionWrapper.mMainLooper =
+ // icHandler.getLooper();
+ Field inputConnectionWrapperField = InputMethodManager.class.getDeclaredField(
+ "mServedInputConnectionWrapper");
+ inputConnectionWrapperField.setAccessible(true);
+ Object inputConnectionWrapper =
+ inputConnectionWrapperField.get(inputMethodManager);
+ Class<?> iinputConnectionWrapperClass =
+ Class.forName("com.android.internal.view.IInputConnectionWrapper");
+ Field looperField =
+ iinputConnectionWrapperClass.getDeclaredField("mMainLooper");
+ looperField.setAccessible(true);
+ looperField.set(inputConnectionWrapper, icHandler.getLooper());
+ looperField.setAccessible(false);
+
+ // inputMethodManager.mServedInputConnectionWrapper.mH =
+ // new IInputConnectionWrapper.MyHandler(icHandler.getLooper());
+ Class<?> myHandlerClass = Class.forName(
+ "com.android.internal.view.IInputConnectionWrapper$MyHandler");
+ Constructor<?> ctor = myHandlerClass.getDeclaredConstructor(
+ iinputConnectionWrapperClass, Looper.class);
+ ctor.setAccessible(true);
+ Handler myHandler = (Handler) ctor.newInstance(
+ inputConnectionWrapper, icHandler.getLooper());
+ Field handlerField = iinputConnectionWrapperClass.getDeclaredField("mH");
+ ctor.setAccessible(false);
+ handlerField.setAccessible(true);
+ Handler oldHandler = (Handler) handlerField.get(inputConnectionWrapper);
+ handlerField.set(inputConnectionWrapper, myHandler);
+ handlerField.setAccessible(false);
+
+ pumpHandlerMessages(oldHandler, myHandler);
+ } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException
+ | ClassNotFoundException | NoSuchMethodException | InstantiationException
+ | InvocationTargetException | NullPointerException e) {
+ e.printStackTrace();
Ted C 2016/02/02 23:14:43 I would use Log.wtf here. This is unrecoverable r
Changwan Ryu 2016/02/11 16:21:08 I've added verification & crashing logic in Thread
+ return;
+ }
+ Log.d(TAG, "switchInputConnectionLooper: replacing done.");
+ }
+ });
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698