| Index: content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnectionFactory.java | 
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnectionFactory.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..ef1149693d38b4ce57860bf3142f7f7edbc50f3c | 
| --- /dev/null | 
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnectionFactory.java | 
| @@ -0,0 +1,162 @@ | 
| +// 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 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 ChromiumInputConnectionFactory implements ChromiumBaseInputConnection.Factory { | 
| +    private static final String TAG = "cr_Ime"; | 
| + | 
| +    private ChromiumInputConnection mChromiumInputConnection; | 
| +    private final Handler mHandler; | 
| +    private final ChromiumInputConnection.ThreadManager mThreadManager; | 
| + | 
| +    ChromiumInputConnectionFactory() { | 
| +        HandlerThread thread = | 
| +                new HandlerThread("InputConnectionHandlerThread", HandlerThread.NORM_PRIORITY); | 
| +        thread.start(); | 
| +        mHandler = new Handler(thread.getLooper()); | 
| +        mThreadManager = new ChromiumInputConnection.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 ChromiumInputConnection(imeAdapter, mThreadManager); | 
| +        } | 
| +        mChromiumInputConnection.initializeOutAttrsOnUiThread(inputType, inputFlags, outAttrs); | 
| +        switchInputConnectionLooper(view.getContext(), view.getHandler()); | 
| +        return mChromiumInputConnection; | 
| +    } | 
| + | 
| +    /** | 
| +     * 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) | 
| +            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); | 
| +                } | 
| +                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(); | 
| +                    return; | 
| +                } | 
| +                Log.d(TAG, "switchInputConnectionLooper: replacing done."); | 
| +            } | 
| +        }); | 
| +    } | 
| +} | 
|  |