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

Side by Side 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, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.input;
6
7 import android.content.Context;
8 import android.os.Handler;
9 import android.os.HandlerThread;
10 import android.os.Looper;
11 import android.os.Message;
12 import android.os.MessageQueue;
13 import android.view.View;
14 import android.view.inputmethod.EditorInfo;
15 import android.view.inputmethod.InputMethodManager;
16
17 import org.chromium.base.Log;
18 import org.chromium.content.browser.input.ChromiumBaseInputConnection.ThreadMana ger;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24
25 /**
26 * Default factory for ChromiumBaseInputConnection classes.
27 */
28 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti on.Factory {
29 private static final String TAG = "cr_Ime";
30
31 private ThreadedInputConnection mChromiumInputConnection;
32 private final Handler mHandler;
33 private final ThreadedInputConnection.ThreadManager mThreadManager;
34
35 ThreadedInputConnectionFactory() {
36 HandlerThread thread =
37 new HandlerThread("InputConnectionHandlerThread", HandlerThread. NORM_PRIORITY);
38 thread.start();
39 mHandler = new Handler(thread.getLooper());
40 mThreadManager = new ThreadManager(mHandler);
41 }
42
43 @Override
44 public ChromiumBaseInputConnection initializeAndGet(View view, ImeAdapter im eAdapter,
45 int inputType, int inputFlags, EditorInfo outAttrs) {
46 ImeUtils.assertOnUiThread();
47 if (mChromiumInputConnection == null) {
48 Log.d(TAG, "Creating ChromiumInputConnection...");
49 mChromiumInputConnection = new ThreadedInputConnection(imeAdapter, m ThreadManager);
50 }
51 mChromiumInputConnection.initializeOutAttrsOnUiThread(inputType, inputFl ags, outAttrs);
52 switchInputConnectionLooper(view.getContext(), view.getHandler());
53 return mChromiumInputConnection;
54 }
55
56 @Override
57 public ThreadManager getThreadManager() {
58 return mThreadManager;
59 }
60
61 /**
62 * Pump messages from a handler and add them to another handler.
63 *
64 * @param sourceHandler The source handler.
65 * @param targetHandler The target handler.
66 * @throws NoSuchFieldException
67 * @throws IllegalAccessException
68 * @throws IllegalArgumentException
69 * @throws NoSuchMethodException
70 * @throws InvocationTargetException
71 */
72 private void pumpHandlerMessages(Handler sourceHandler, Handler targetHandle r)
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
73 throws NoSuchFieldException, IllegalAccessException, IllegalArgument Exception,
74 NoSuchMethodException, InvocationTargetException {
75 Looper looper = sourceHandler.getLooper();
76 Method getQueueMethod = Looper.class.getDeclaredMethod("getQueue");
77 getQueueMethod.setAccessible(true);
78 MessageQueue queue = (MessageQueue) getQueueMethod.invoke(looper);
79 getQueueMethod.setAccessible(false);
80
81 Field messagesField = MessageQueue.class.getDeclaredField("mMessages");
82 Field nextField = Message.class.getDeclaredField("next");
83
84 synchronized (queue) {
85 // Copy messages first.
86 messagesField.setAccessible(true);
87 nextField.setAccessible(true);
88 Message msg = (Message) messagesField.get(queue);
89 while (msg != null) {
90 if (msg.getTarget() == sourceHandler) {
91 Log.d(TAG, "Copying a message to the new handler...");
92 Message newMsg = targetHandler.obtainMessage();
93 newMsg.copyFrom(msg);
94 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
95 }
96 msg = (Message) nextField.get(msg);
97 }
98 messagesField.setAccessible(false);
99 nextField.setAccessible(false);
100
101 // Now remove messages from the first handler.
102 sourceHandler.removeCallbacksAndMessages(null);
103 }
104 }
105
106 /**
107 * Switch the looper in InputMethodManager so that future calls to InputConn ection
108 * can run in IME thread.
109 *
110 * @param context The context
111 * @param viewHandler The current handler for the View that this InputConnec tion will be
112 * attached to.
113 */
114 private void switchInputConnectionLooper(Context context, Handler viewHandle r) {
115 Log.d(TAG, "switchInputConnectionLooper");
116 // Retrieve the same singleton instance of InputMethodManager that calle d
117 // onCreateInputConnection().
118 final InputMethodManager inputMethodManager =
119 (InputMethodManager) context.getSystemService(Context.INPUT_METH OD_SERVICE);
120 final Handler icHandler = mHandler;
121 viewHandler.post(new Runnable() {
122 @Override
123 public void run() {
124 Log.d(TAG, "switchInputConnectionLooper: replacing looper and ha ndler");
125 try {
126 // inputMethodManager.mServedInputConnectionWrapper.mMainLoo per =
127 // icHandler.getLooper();
128 Field inputConnectionWrapperField = InputMethodManager.class .getDeclaredField(
129 "mServedInputConnectionWrapper");
130 inputConnectionWrapperField.setAccessible(true);
131 Object inputConnectionWrapper =
132 inputConnectionWrapperField.get(inputMethodManager);
133 Class<?> iinputConnectionWrapperClass =
134 Class.forName("com.android.internal.view.IInputConne ctionWrapper");
135 Field looperField =
136 iinputConnectionWrapperClass.getDeclaredField("mMain Looper");
137 looperField.setAccessible(true);
138 looperField.set(inputConnectionWrapper, icHandler.getLooper( ));
139 looperField.setAccessible(false);
140
141 // inputMethodManager.mServedInputConnectionWrapper.mH =
142 // new IInputConnectionWrapper.MyHandler(icHandler.g etLooper());
143 Class<?> myHandlerClass = Class.forName(
144 "com.android.internal.view.IInputConnectionWrapper$M yHandler");
145 Constructor<?> ctor = myHandlerClass.getDeclaredConstructor(
146 iinputConnectionWrapperClass, Looper.class);
147 ctor.setAccessible(true);
148 Handler myHandler = (Handler) ctor.newInstance(
149 inputConnectionWrapper, icHandler.getLooper());
150 Field handlerField = iinputConnectionWrapperClass.getDeclare dField("mH");
151 ctor.setAccessible(false);
152 handlerField.setAccessible(true);
153 Handler oldHandler = (Handler) handlerField.get(inputConnect ionWrapper);
154 handlerField.set(inputConnectionWrapper, myHandler);
155 handlerField.setAccessible(false);
156
157 pumpHandlerMessages(oldHandler, myHandler);
158 } catch (IllegalAccessException | IllegalArgumentException | NoS uchFieldException
159 | ClassNotFoundException | NoSuchMethodException | Insta ntiationException
160 | InvocationTargetException | NullPointerException e) {
161 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
162 return;
163 }
164 Log.d(TAG, "switchInputConnectionLooper: replacing done.");
165 }
166 });
167 }
168 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698