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

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/ChromiumInputConnection.java

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed release test failures 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 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.os.Bundle;
8 import android.os.Handler;
9 import android.os.Looper;
10 import android.view.KeyEvent;
11 import android.view.inputmethod.CompletionInfo;
12 import android.view.inputmethod.CorrectionInfo;
13 import android.view.inputmethod.EditorInfo;
14 import android.view.inputmethod.ExtractedText;
15 import android.view.inputmethod.ExtractedTextRequest;
16 import android.view.inputmethod.InputConnection;
17
18 import org.chromium.base.Log;
19 import org.chromium.base.ThreadUtils;
20
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.Callable;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.LinkedBlockingQueue;
25
26 /**
27 * Chromium's implementation of {@link InputConnection} to communicate with exte rnal input method
28 * apps.
29 */
30 public class ChromiumInputConnection implements ChromiumBaseInputConnection {
31 private static final String TAG = "cr_Ime";
32
33 static class ThreadManager implements ChromiumBaseInputConnection.ThreadMana ger {
34 private final Handler mHandler;
35
36 public ThreadManager(Handler handler) {
37 mHandler = handler;
38 }
39
40 public Handler getHandler() {
41 return mHandler;
42 }
43
44 @Override
45 public boolean runningOnThisThread() {
46 return getHandler().getLooper() == Looper.myLooper();
47 }
48
49 @Override
50 public void post(Runnable runnable) {
51 getHandler().post(runnable);
52 }
53 }
54
55 private final ImeAdapter mImeAdapter;
56 private final ThreadManager mThreadManager;
57
58 private int mNumNestedBatchEdits = 0;
59
60 private final BlockingQueue<TextInputState> mQueue = new LinkedBlockingQueue <>();
61 private TextInputState mUpdatePendingTextInputState;
aelias_OOO_until_Jul13 2016/01/21 07:43:04 Please rename to mBatchEditPendingState.
Changwan Ryu 2016/01/22 10:22:16 Done.
62 // Only accessible on UI thread.
63 private TextInputState mLastUpdatedTextInputState;
64
65 ChromiumInputConnection(ImeAdapter imeAdapter, ThreadManager threadManager) {
66 Log.d(TAG, "constructor");
67 ImeUtils.assertOnUiThread();
68 mImeAdapter = imeAdapter;
69 mThreadManager = threadManager;
70 }
71
72 void initializeOnUiThread(int inputType, int inputFlags, EditorInfo outAttrs ) {
aelias_OOO_until_Jul13 2016/01/21 07:43:04 Please rename to initializeOutAttrsOnUiThread.
Changwan Ryu 2016/01/22 10:22:16 Done.
73 Log.d(TAG, "initialize");
74 ImeUtils.assertOnUiThread();
75 int initialSelStart = 0;
76 int initialSelEnd = 0;
77 if (mLastUpdatedTextInputState != null) {
78 initialSelStart = mLastUpdatedTextInputState.selection().start();
79 initialSelEnd = mLastUpdatedTextInputState.selection().end();
80 }
81 ImeUtils.computeEditorInfo(inputType, inputFlags, initialSelStart, initi alSelEnd, outAttrs);
82 }
83
84 @Override
85 public void updateStateOnUiThread(final String text, final int selectionStar t,
86 final int selectionEnd, final int compositionStart, final int compos itionEnd,
87 boolean singleLine, final boolean isNonImeChange) {
88 Log.d(TAG, "updateState [%s] [%s %s] [%s %s] [%b] [%b]", text, selection Start, selectionEnd,
89 compositionStart, compositionEnd, singleLine, isNonImeChange);
90 ImeUtils.assertOnUiThread();
91
92 // TODO(changwan): this should be called at the end of each call, not af ter it.
93 final TextInputState newState =
94 new TextInputState(text, new Range(selectionStart, selectionEnd) ,
95 new Range(compositionStart, compositionEnd), singleLine) ;
96 mLastUpdatedTextInputState = newState;
97
98 if (!isNonImeChange) {
99 onImeOriginatingStateUpdateOnUiThread(newState);
100 } else {
101 mThreadManager.post(new Runnable() {
102 @Override
103 public void run() {
104 updateToInputMethodManager(newState);
105 }
106 });
107 }
108 Log.d(TAG, "updateState finished");
109 }
110
111 @Override
112 public ThreadManager getThreadManager() {
113 return mThreadManager;
114 }
115
116 @Override
117 public void onRestartInputOnUiThread() {}
118
119 private void updateToInputMethodManager(TextInputState textInputState) {
120 assertOnImeThread();
121 if (mNumNestedBatchEdits != 0) {
122 mUpdatePendingTextInputState = textInputState;
123 return;
124 }
125 ImeUtils.assertReally(textInputState != null);
126 Range selection = textInputState.selection();
127 Range composition = textInputState.composition();
128 mImeAdapter.updateSelection(
aelias_OOO_until_Jul13 2016/01/21 07:43:04 This call back into ImeAdapter from deep in the IM
Changwan Ryu 2016/01/22 10:22:16 Hmm... I've done this refactoring with AdapterInpu
129 selection.start(), selection.end(), composition.start(), composi tion.end());
130 }
131
132 private void updatePendingTextInputState() {
aelias_OOO_until_Jul13 2016/01/21 07:43:04 As I mentioned in endBatchEdit(), this can be dele
Changwan Ryu 2016/01/22 10:22:16 Done.
133 if (mUpdatePendingTextInputState == null) return;
134 Range selection = mUpdatePendingTextInputState.selection();
135 Range composition = mUpdatePendingTextInputState.composition();
136 mImeAdapter.updateSelection(
137 selection.start(), selection.end(), composition.start(), composi tion.end());
138 mUpdatePendingTextInputState = null;
139 }
140
141 private TextInputState updateTextInputState() {
aelias_OOO_until_Jul13 2016/01/21 07:43:04 Please rename to requestTextInputState().
Changwan Ryu 2016/01/22 10:22:16 Done.
142 ThreadUtils.postOnUiThread(new Runnable() {
143 @Override
144 public void run() {
145 boolean result = mImeAdapter.requestTextInputStateUpdate();
146 if (!result) unblock();
147 }
148 });
149 return blockAndGetStateUpdate();
150 }
151
152 private void onImeOriginatingStateUpdateOnUiThread(TextInputState textInputS tate) {
153 ImeUtils.assertOnUiThread();
154 try {
155 mQueue.put(textInputState);
156 } catch (InterruptedException e) {
157 Log.e(TAG, "onImeOriginatingStateUpdateOnUiThread interrupted", e);
158 }
159 Log.d(TAG, "onImeOriginatingStateUpdateOnUiThread finished: %d", mQueue. size());
160 }
161
162 /**
163 * Unblock the blockAndGetStateUpdate() function if we found that we will
164 * never get state update.
165 */
166 private void unblock() {
167 ImeUtils.assertOnUiThread();
168 onImeOriginatingStateUpdateOnUiThread(new TextInputState.Unblocker());
169 }
170
171 private void postConsumeStateUpdate() {
172 mThreadManager.post(new Runnable() {
173 @Override
174 public void run() {
175 blockAndGetStateUpdate();
176 }
177 });
178 }
179
180 private void assertOnImeThread() {
181 ImeUtils.assertReally(mThreadManager.runningOnThisThread());
182 }
183
184 /**
185 * Block until we get the expected state update.
186 * @return TextInputState if we get it successfully. null otherwise.
187 */
188 private TextInputState blockAndGetStateUpdate() {
189 assertOnImeThread();
190 while (true) {
191 TextInputState result = mQueue.poll();
192 if (result != null) {
193 if (result.shouldUnblock()) {
194 Log.d(TAG, "blockAndGetStateUpdate: unblocked");
195 return null;
196 }
197 updateToInputMethodManager(result);
198 return result;
199 }
200 }
201 }
202
203 /**
204 * @see InputConnection#setComposingText(java.lang.CharSequence, int)
205 */
206 @Override
207 public boolean setComposingText(final CharSequence text, final int newCursor Position) {
208 Log.d(TAG, "setComposingText [%s] [%d]", text, newCursorPosition);
209 assertOnImeThread();
210 ThreadUtils.postOnUiThread(new Runnable() {
211 @Override
212 public void run() {
213 boolean result = mImeAdapter.setComposingText(text, newCursorPos ition);
214 if (!result) unblock();
aelias_OOO_until_Jul13 2016/01/21 07:43:04 If you apply my comment in render_widget.cpp, than
Changwan Ryu 2016/01/22 10:22:16 Done.
215 }
216 });
217 return blockAndGetStateUpdate() != null;
aelias_OOO_until_Jul13 2016/01/21 07:43:04 If you apply my comment in render_widget.cpp, than
Changwan Ryu 2016/01/22 10:22:16 Done.
218 }
219
220 /**
221 * @see InputConnection#commitText(java.lang.CharSequence, int)
222 */
223 @Override
224 public boolean commitText(final CharSequence text, final int newCursorPositi on) {
225 Log.d(TAG, "commitText [%s] [%d]", text, newCursorPosition);
226 // TODO(changwan): for some unknown reason, setComposingText() -> commit Text()
227 // does not delete the original composition. Consider using commitText() after fixing it.
228 beginBatchEdit();
229 boolean result = setComposingText(text, newCursorPosition) && finishComp osingText();
230 endBatchEdit();
231 return result;
232 }
233
234 /**
235 * @see InputConnection#performEditorAction(int)
236 */
237 @Override
238 public boolean performEditorAction(final int actionCode) {
239 Log.d(TAG, "performEditorAction [%d]", actionCode);
240 assertOnImeThread();
241 try {
242 return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
243 @Override
244 public Boolean call() {
245 return mImeAdapter.performEditorAction(actionCode);
246 }
247 });
248 } catch (ExecutionException e) {
249 e.printStackTrace();
250 return false;
251 }
252 }
253
254 /**
255 * @see InputConnection#performContextMenuAction(int)
256 */
257 @Override
258 public boolean performContextMenuAction(final int id) {
259 Log.d(TAG, "performContextMenuAction [%d]", id);
260 assertOnImeThread();
261 try {
262 return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
263 @Override
264 public Boolean call() {
265 return mImeAdapter.performContextMenuAction(id);
266 }
267 });
268 } catch (ExecutionException e) {
269 e.printStackTrace();
270 return false;
271 }
272 }
273
274 /**
275 * @see InputConnection#getExtractedText(android.view.inputmethod.ExtractedT extRequest,
276 * int)
277 */
278 @Override
279 public ExtractedText getExtractedText(ExtractedTextRequest request, int flag s) {
280 Log.d(TAG, "getExtractedText");
281 assertOnImeThread();
282 TextInputState textInputState = updateTextInputState();
283 if (textInputState == null) return null;
284 ExtractedText extractedText = new ExtractedText();
285 extractedText.text = textInputState.text();
286 extractedText.partialEndOffset = textInputState.text().length();
287 extractedText.selectionStart = textInputState.selection().start();
288 extractedText.selectionEnd = textInputState.selection().end();
289 extractedText.flags = textInputState.singleLine() ? ExtractedText.FLAG_S INGLE_LINE : 0;
290 return extractedText;
291 }
292
293 /**
294 * @see InputConnection#beginBatchEdit()
295 */
296 @Override
297 public boolean beginBatchEdit() {
298 Log.d(TAG, "beginBatchEdit [%b]", (mNumNestedBatchEdits == 0));
299 assertOnImeThread();
300 mNumNestedBatchEdits++;
301 return true;
302 }
303
304 /**
305 * @see InputConnection#endBatchEdit()
306 */
307 @Override
308 public boolean endBatchEdit() {
309 assertOnImeThread();
310 if (mNumNestedBatchEdits == 0) return false;
311 --mNumNestedBatchEdits;
312 Log.d(TAG, "endBatchEdit [%b]", (mNumNestedBatchEdits == 0));
313 if (mNumNestedBatchEdits == 0) {
314 updatePendingTextInputState();
aelias_OOO_until_Jul13 2016/01/21 07:43:04 Please delete that method and just do: updateToIn
Changwan Ryu 2016/01/22 10:22:16 Done.
315 }
316 return mNumNestedBatchEdits != 0;
317 }
318
319 /**
320 * @see InputConnection#deleteSurroundingText(int, int)
321 */
322 @Override
323 public boolean deleteSurroundingText(final int beforeLength, final int after Length) {
324 Log.d(TAG, "deleteSurroundingText [%d %d]", beforeLength, afterLength);
325 assertOnImeThread();
326 ThreadUtils.postOnUiThread(new Runnable() {
327 @Override
328 public void run() {
329 boolean result = mImeAdapter.deleteSurroundingText(beforeLength, afterLength);
330 if (!result) unblock();
331 }
332 });
333 return blockAndGetStateUpdate() != null;
334 }
335
336 /**
337 * @see ChromiumBaseInputConnection#sendKeyEventOnUiThread(KeyEvent)
338 */
339 @Override
340 public boolean sendKeyEventOnUiThread(final KeyEvent event) {
341 ImeUtils.assertOnUiThread();
342 mThreadManager.post(new Runnable() {
343 @Override
344 public void run() {
345 sendKeyEvent(event);
346 }
347 });
348 return true;
349 }
350
351 /**
352 * @see InputConnection#sendKeyEvent(android.view.KeyEvent)
353 */
354 @Override
355 public boolean sendKeyEvent(final KeyEvent event) {
356 Log.d(TAG, "sendKeyEvent [%d %d]", event.getAction(), event.getKeyCode() );
357 assertOnImeThread();
358 ThreadUtils.postOnUiThread(new Runnable() {
359 @Override
360 public void run() {
361 boolean result = mImeAdapter.sendKeyEvent(event);
362 if (!result) unblock();
363 }
364 });
365 return blockAndGetStateUpdate() != null;
366 }
367
368 /**
369 * @see InputConnection#finishComposingText()
370 */
371 @Override
372 public boolean finishComposingText() {
373 Log.d(TAG, "finishComposingText");
374 // This is the only function that may be called on UI thread because
375 // of direct calls from InputMethodManager.
376
377 ThreadUtils.postOnUiThread(new Runnable() {
378 @Override
379 public void run() {
380 boolean result = mImeAdapter.finishComposingText();
381 if (!result) unblock();
382 }
383 });
384
385 if (ThreadUtils.runningOnUiThread()) {
386 postConsumeStateUpdate();
aelias_OOO_until_Jul13 2016/01/21 07:43:04 If you apply my comment in render_widget.cpp, than
Changwan Ryu 2016/01/22 10:22:16 Removed.
387 return true;
388 } else {
389 return blockAndGetStateUpdate() != null;
390 }
391 }
392
393 /**
394 * @see InputConnection#setSelection(int, int)
395 */
396 @Override
397 public boolean setSelection(final int start, final int end) {
398 Log.d(TAG, "setSelection [%d %d]", start, end);
399 assertOnImeThread();
400 ThreadUtils.postOnUiThread(new Runnable() {
401 @Override
402 public void run() {
403 boolean result = mImeAdapter.setEditableSelectionOffsets(start, end);
404 if (!result) unblock();
405 }
406 });
407 return blockAndGetStateUpdate() != null;
408 }
409
410 /**
411 * @see InputConnection#setComposingRegion(int, int)
412 */
413 @Override
414 public boolean setComposingRegion(final int start, final int end) {
415 Log.d(TAG, "setComposingRegion [%d %d]", start, end);
416 assertOnImeThread();
417 ThreadUtils.postOnUiThread(new Runnable() {
418 @Override
419 public void run() {
420 boolean result = mImeAdapter.setComposingRegion(start, end);
421 if (!result) unblock();
422 }
423 });
424 return blockAndGetStateUpdate() != null;
425 }
426
427 /**
428 * @see InputConnection#getTextBeforeCursor(int, int)
429 */
430 @Override
431 public CharSequence getTextBeforeCursor(int maxChars, int flags) {
432 Log.d(TAG, "getTextBeforeCursor [%d %x]", maxChars, flags);
433 assertOnImeThread();
434 TextInputState textInputState = updateTextInputState();
435 if (textInputState == null) return null;
436 return textInputState.getTextBeforeSelection(maxChars);
437 }
438
439 /**
440 * @see InputConnection#getTextAfterCursor(int, int)
441 */
442 @Override
443 public CharSequence getTextAfterCursor(int maxChars, int flags) {
444 Log.d(TAG, "getTextAfterCursor [%d %x]", maxChars, flags);
445 assertOnImeThread();
446 TextInputState textInputState = updateTextInputState();
447 if (textInputState == null) return null;
448 return textInputState.getTextAfterSelection(maxChars);
449 }
450
451 /**
452 * @see InputConnection#getSelectedText(int)
453 */
454 @Override
455 public CharSequence getSelectedText(int flags) {
456 Log.d(TAG, "getSelectedText [%x]", flags);
457 assertOnImeThread();
458 TextInputState textInputState = updateTextInputState();
459 if (textInputState == null) return null;
460 return textInputState.getSelectedText();
461 }
462
463 @Override
464 public void moveCursorToSelectionEndOnUiThread() {
465 mThreadManager.post(new Runnable() {
466 @Override
467 public void run() {
468 TextInputState textInputState = updateTextInputState();
469 if (textInputState == null) return;
470 Range selection = textInputState.selection();
471 setSelection(selection.end(), selection.end());
472 }
473 });
474 }
475
476 /**
477 * @see InputConnection#getCursorCapsMode(int)
478 */
479 @Override
480 public int getCursorCapsMode(int reqModes) {
481 Log.d(TAG, "getCursorCapsMode [%x]", reqModes);
482 assertOnImeThread();
483 // TODO(changwan): Auto-generated method stub
484 return 0;
485 }
486
487 /**
488 * @see InputConnection#commitCompletion(android.view.inputmethod.Completion Info)
489 */
490 @Override
491 public boolean commitCompletion(CompletionInfo text) {
492 Log.d(TAG, "commitCompletion [%s]", text);
493 assertOnImeThread();
494 // TODO(changwan): Auto-generated method stub
495 return false;
496 }
497
498 /**
499 * @see InputConnection#commitCorrection(android.view.inputmethod.Correction Info)
500 */
501 @Override
502 public boolean commitCorrection(CorrectionInfo correctionInfo) {
503 Log.d(TAG, "commitCorrection [%s]", ImeUtils.dumpCorrectionInfo(correcti onInfo));
504 assertOnImeThread();
505 // TODO(changwan): Auto-generated method stub
506 return false;
507 }
508
509 /**
510 * @see InputConnection#clearMetaKeyStates(int)
511 */
512 @Override
513 public boolean clearMetaKeyStates(int states) {
514 Log.d(TAG, "clearMetaKeyStates [%x]", states);
515 assertOnImeThread();
516 // TODO(changwan): Auto-generated method stub
517 return false;
518 }
519
520 /**
521 * @see InputConnection#reportFullscreenMode(boolean)
522 */
523 @Override
524 public boolean reportFullscreenMode(boolean enabled) {
525 Log.d(TAG, "reportFullscreenMode [%b]", enabled);
526 assertOnImeThread();
527 // We ignore fullscreen mode for now. That's why we set
528 // EditorInfo.IME_FLAG_NO_FULLSCREEN in constructor.
529 return false;
530 }
531
532 /**
533 * @see InputConnection#performPrivateCommand(java.lang.String, android.os.B undle)
534 */
535 @Override
536 public boolean performPrivateCommand(String action, Bundle data) {
537 Log.d(TAG, "performPrivateCommand [%s]", action);
538 assertOnImeThread();
539 // TODO(changwan): Auto-generated method stub
540 return false;
541 }
542
543 /**
544 * @see InputConnection#requestCursorUpdates(int)
545 */
546 @Override
547 public boolean requestCursorUpdates(int cursorUpdateMode) {
548 Log.d(TAG, "requestCursorUpdates [%x]", cursorUpdateMode);
549 assertOnImeThread();
550 // TODO(changwan): Auto-generated method stub
551 return false;
552 }
553 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698