| 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..29de9f1b1abb112c8979af8e35adb1fe76a28775 | 
| --- /dev/null | 
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java | 
| @@ -0,0 +1,101 @@ | 
| +// 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.os.Handler; | 
| +import android.os.HandlerThread; | 
| +import android.view.View; | 
| +import android.view.inputmethod.EditorInfo; | 
| + | 
| +import org.chromium.base.Log; | 
| + | 
| +/** | 
| + * Default factory for ChromiumBaseInputConnection classes. | 
| + */ | 
| +public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnection.Factory { | 
| +    private static final String TAG = "cr_Ime"; | 
| +    private static final boolean DEBUG_LOGS = false; | 
| + | 
| +    private ThreadedInputConnection mThreadedInputConnection; | 
| +    private final Handler mHandler; | 
| +    private final InputMethodManagerWrapper mInputMethodManagerWrapper; | 
| +    private ThreadedInputConnectionProxyView mProxyView; | 
| + | 
| +    ThreadedInputConnectionFactory(InputMethodManagerWrapper inputMethodManagerWrapper) { | 
| +        HandlerThread thread = | 
| +                new HandlerThread("InputConnectionHandlerThread", HandlerThread.NORM_PRIORITY); | 
| +        thread.start(); | 
| +        mHandler = new Handler(thread.getLooper()); | 
| +        mInputMethodManagerWrapper = inputMethodManagerWrapper; | 
| +    } | 
| + | 
| +    private boolean shouldTriggerDelayedOnCreateInputConnection() { | 
| +        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { | 
| +            String className = ste.getClassName(); | 
| +            if (className != null | 
| +                    && (className.contains(ThreadedInputConnectionProxyView.class.getName()) | 
| +                    || className.contains("TestInputMethodManagerWrapper"))) { | 
| +                return false; | 
| +            } | 
| +        } | 
| +        return true; | 
| +    } | 
| + | 
| +    @Override | 
| +    public ChromiumBaseInputConnection initializeAndGet( | 
| +            View view, ImeAdapter imeAdapter, int inputType, int inputFlags, int selectionStart, | 
| +            int selectionEnd, EditorInfo outAttrs) { | 
| +        ImeUtils.checkOnUiThread(); | 
| +        if (shouldTriggerDelayedOnCreateInputConnection()) { | 
| +            triggerDelayedOnCreateInputConnection(view); | 
| +            return null; | 
| +        } | 
| +        if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view"); | 
| +        if (mThreadedInputConnection == null) { | 
| +            if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection..."); | 
| +            mThreadedInputConnection = new ThreadedInputConnection(imeAdapter, mHandler); | 
| +        } | 
| +        mThreadedInputConnection.initializeOutAttrsOnUiThread(inputType, inputFlags, | 
| +                selectionStart, selectionEnd, outAttrs); | 
| +        return mThreadedInputConnection; | 
| +    } | 
| + | 
| +    private void triggerDelayedOnCreateInputConnection(final View view) { | 
| +        if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection"); | 
| +        if (mProxyView == null) { | 
| +            mProxyView = new ThreadedInputConnectionProxyView(view.getContext(), mHandler, view); | 
| +        } | 
| +        mProxyView.requestFocus(); | 
| +        view.getHandler().post(new Runnable() { | 
| +            @Override | 
| +            public void run() { | 
| +                // This is a hack to make InputMethodManager believe that the proxy view | 
| +                // now has a focus. As a result, InputMethodManager will think that mProxyView | 
| +                // is focused, and will call getHandler() of the view when creating input | 
| +                // connection. | 
| + | 
| +                // Step 1: Set mProxyView as InputMethodManager#mNextServedView. | 
| +                mProxyView.onWindowFocusChanged(true); | 
| +                // Step 2: Have InputMethodManager focus in on mNextServedView. | 
| +                mInputMethodManagerWrapper.isActive(view); | 
| +                // Step 3: Verify that the above hack worked. | 
| +                mHandler.post(new Runnable() { | 
| +                    @Override | 
| +                    public void run() { | 
| +                        ImeUtils.checkCondition(mInputMethodManagerWrapper.isActive(mProxyView)); | 
| +                        ImeUtils.checkCondition(mInputMethodManagerWrapper.isActive(view)); | 
| +                    } | 
| +                }); | 
| + | 
| +                // TODO(changwan): come up with fallback plan or crash when the above hack fails. | 
| +            } | 
| +        }); | 
| +    } | 
| + | 
| +    @Override | 
| +    public Handler getHandler() { | 
| +        return mHandler; | 
| +    } | 
| +} | 
|  |