author | Jim Chen <nchen@mozilla.com> |
Wed, 13 Dec 2017 22:57:21 -0500 | |
changeset 448178 | 29a3c1e94980304151b3b9b58f03d83b4a70ad5b |
parent 448177 | 3e18c58d0320ef834bcf6b9f76dad2f8f1397315 |
child 448179 | 39f1117294d19db393e644f30fc6c1c0f98ef3e0 |
push id | 8527 |
push user | Callek@gmail.com |
push date | Thu, 11 Jan 2018 21:05:50 +0000 |
treeherder | mozilla-beta@95342d212a7a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | esawin |
bugs | 1416918 |
milestone | 59.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java @@ -32,23 +32,26 @@ import android.text.SpannableStringBuild import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; -/* - GeckoEditable implements only some functions of Editable - The field mText contains the actual underlying - SpannableStringBuilder/Editable that contains our text. -*/ -final class GeckoEditable extends IGeckoEditableParent.Stub - implements InvocationHandler, Editable, GeckoEditableClient { +/** + * GeckoEditable implements only some functions of Editable + * The field mText contains the actual underlying + * SpannableStringBuilder/Editable that contains our text. + */ +/* package */ final class GeckoEditable + extends IGeckoEditableParent.Stub + implements InvocationHandler, + Editable, + GeckoEditableClient { private static final boolean DEBUG = false; private static final String LOGTAG = "GeckoEditable"; // Filters to implement Editable's filtering functionality private InputFilter[] mFilters; private final AsyncText mText; @@ -63,17 +66,16 @@ final class GeckoEditable extends IGecko private Handler mIcPostHandler; // Parent process child used as a default for key events. /* package */ IGeckoEditableChild mDefaultChild; // Used by IC thread. // Parent or content process child that has the focus. /* package */ IGeckoEditableChild mFocusedChild; // Used by IC thread. /* package */ IBinder mFocusedToken; // Used by Gecko/binder thread. /* package */ GeckoEditableListener mListener; - /* package */ GeckoView mView; /* package */ boolean mInBatchMode; // Used by IC thread /* package */ boolean mNeedSync; // Used by IC thread // Gecko side needs an updated composition from Java; private boolean mNeedUpdateComposition; // Used by IC thread private boolean mSuppressKeyUp; // Used by IC thread private boolean mIgnoreSelectionChange; // Used by Gecko thread @@ -592,84 +594,57 @@ final class GeckoEditable extends IGecko if (DEBUG) { Log.d(LOGTAG, "sending: " + event); } onKeyEvent(mFocusedChild, event, event.getAction(), /* metaState */ 0, /* isSynthesizedImeKey */ true); } } - @WrapForJNI(calledFrom = "gecko") - private GeckoEditable() { + /* package */ GeckoEditable() { if (DEBUG) { - // Called by nsWindow. - ThreadUtils.assertOnGeckoThread(); + // Called by TextInputController. + ThreadUtils.assertOnUiThread(); } mText = new AsyncText(); mActions = new ConcurrentLinkedQueue<Action>(); final Class<?>[] PROXY_INTERFACES = { Editable.class }; - mProxy = (Editable)Proxy.newProxyInstance( - Editable.class.getClassLoader(), - PROXY_INTERFACES, this); + mProxy = (Editable) Proxy.newProxyInstance(Editable.class.getClassLoader(), + PROXY_INTERFACES, this); mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler(); } - @WrapForJNI(calledFrom = "gecko") - private void setDefaultEditableChild(final IGeckoEditableChild child) { + /* package */ void setDefaultEditableChild(final IGeckoEditableChild child) { + if (DEBUG) { + // Called by TextInputController. + ThreadUtils.assertOnUiThread(); + Log.d(LOGTAG, "setDefaultEditableChild " + child); + } mDefaultChild = child; } - @WrapForJNI(calledFrom = "gecko") - private void onViewChange(final GeckoView v) { + /* package */ void setListener(final GeckoEditableListener newListener) { if (DEBUG) { - // Called by nsWindow. - ThreadUtils.assertOnGeckoThread(); - Log.d(LOGTAG, "onViewChange(" + v + ")"); + // Called by TextInputController. + ThreadUtils.assertOnUiThread(); + Log.d(LOGTAG, "setListener " + newListener); } - final GeckoEditableListener newListener = - v != null ? GeckoInputConnection.create(v, this) : null; - - final Runnable setListenerRunnable = new Runnable() { + mIcPostHandler.post(new Runnable() { @Override public void run() { if (DEBUG) { Log.d(LOGTAG, "onViewChange (set listener)"); } mListener = newListener; } - }; - - // Post to UI thread first to make sure any code that is using the old input - // connection has finished running, before we switch to a new input connection or - // before we clear the input connection on destruction. - final Handler icHandler = mIcPostHandler; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (DEBUG) { - Log.d(LOGTAG, "onViewChange (set IC)"); - } - - if (mView != null) { - // Detach the previous view. - mView.setInputConnectionListener(null); - } - if (v != null) { - // And attach the new view. - v.setInputConnectionListener((InputConnectionListener) newListener); - } - - mView = v; - icHandler.post(setListenerRunnable); - } }); } private boolean onIcThread() { return mIcRunHandler.getLooper() == Looper.myLooper(); } private void assertOnIcThread() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java @@ -39,19 +39,20 @@ import android.view.View; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -class GeckoInputConnection +/* package */ class GeckoInputConnection extends BaseInputConnection - implements InputConnectionListener, GeckoEditableListener { + implements TextInputController.Delegate, + GeckoEditableListener { private static final boolean DEBUG = false; protected static final String LOGTAG = "GeckoInputConnection"; private static final String CUSTOM_HANDLER_TEST_METHOD = "testInputConnection"; private static final String CUSTOM_HANDLER_TEST_CLASS = "org.mozilla.gecko.tests.components.GeckoViewComponent$TextInput"; @@ -65,38 +66,43 @@ class GeckoInputConnection private String mIMEModeHint = ""; private String mIMEActionHint = ""; private boolean mInPrivateBrowsing; private boolean mIsUserAction; private boolean mFocused; private String mCurrentInputMethod = ""; - private final GeckoView mView; + private final GeckoSession mSession; + private final View mView; private final GeckoEditableClient mEditableClient; protected int mBatchEditCount; private ExtractedTextRequest mUpdateRequest; private final ExtractedText mUpdateExtract = new ExtractedText(); private final InputConnection mKeyInputConnection; private CursorAnchorInfo.Builder mCursorAnchorInfoBuilder; // Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices. private volatile boolean mSoftInputReentrancyGuard; - public static GeckoEditableListener create(GeckoView targetView, - GeckoEditableClient editable) { - if (DEBUG) - return DebugGeckoInputConnection.create(targetView, editable); - else - return new GeckoInputConnection(targetView, editable); + public static TextInputController.Delegate create(GeckoSession session, + View targetView, + GeckoEditableClient editable) { + if (DEBUG) { + return DebugGeckoInputConnection.create(session, targetView, editable); + } else { + return new GeckoInputConnection(session, targetView, editable); + } } - protected GeckoInputConnection(GeckoView targetView, + protected GeckoInputConnection(GeckoSession session, + View targetView, GeckoEditableClient editable) { super(targetView, true); + mSession = session; mView = targetView; mEditableClient = editable; mIMEState = IME_STATE_DISABLED; // InputConnection that sends keys for plugins, which don't have full editors mKeyInputConnection = new BaseInputConnection(targetView, false); } @Override @@ -197,17 +203,18 @@ class GeckoInputConnection if ((req.flags & GET_TEXT_WITH_STYLES) != 0) { extract.text = new SpannableString(editable); } else { extract.text = editable.toString(); } return extract; } - private GeckoView getView() { + @Override // TextInputController.Delegate + public View getView() { return mView; } private InputMethodManager getInputMethodManager() { View view = getView(); if (view == null) { return null; } @@ -229,23 +236,22 @@ class GeckoInputConnection @Override public void run() { if (v.hasFocus() && !imm.isActive(v)) { // Marshmallow workaround: The view has focus but it is not the active // view for the input method. (Bug 1211848) v.clearFocus(); v.requestFocus(); } - final GeckoView view = getView(); - if (view != null && view.getSession() != null) { - if (showToolbar) { - view.getDynamicToolbarAnimator().showToolbar(/*immediately*/ true); - } - view.getEventDispatcher().dispatch("GeckoView:ZoomToInput", null); + + if (showToolbar) { + mSession.getDynamicToolbarAnimator().showToolbar(/* immediately */ true); } + mSession.getEventDispatcher().dispatch("GeckoView:ZoomToInput", null); + mSoftInputReentrancyGuard = true; imm.showSoftInput(v, 0); mSoftInputReentrancyGuard = false; } }); } private void hideSoftInput() { @@ -352,17 +358,17 @@ class GeckoInputConnection @TargetApi(21) @Override public void updateCompositionRects(final RectF[] rects) { if (!(Build.VERSION.SDK_INT >= 21)) { return; } - final GeckoView view = getView(); + final View view = getView(); if (view == null) { return; } final Editable content = getEditable(); if (content == null) { return; } @@ -382,30 +388,26 @@ class GeckoInputConnection @Override public void run() { updateCompositionRectsOnUi(view, rects, composition); } }); } @TargetApi(21) - /* package */ void updateCompositionRectsOnUi(final GeckoView view, + /* package */ void updateCompositionRectsOnUi(final View view, final RectF[] rects, final CharSequence composition) { - if (view.getSession() == null) { - return; - } - if (mCursorAnchorInfoBuilder == null) { mCursorAnchorInfoBuilder = new CursorAnchorInfo.Builder(); } mCursorAnchorInfoBuilder.reset(); final Matrix matrix = new Matrix(); - view.getSession().getClientToScreenMatrix(matrix); + mSession.getClientToScreenMatrix(matrix); mCursorAnchorInfoBuilder.setMatrix(matrix); for (int i = 0; i < rects.length; i++) { mCursorAnchorInfoBuilder.addCharacterBounds( i, rects[i].left, rects[i].top, rects[i].right, rects[i].bottom, CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION); } @@ -472,31 +474,31 @@ class GeckoInputConnection // wait for new thread to set sBackgroundHandler GeckoInputConnection.class.wait(); } catch (InterruptedException e) { } } return sBackgroundHandler; } - private boolean canReturnCustomHandler() { + private synchronized boolean canReturnCustomHandler() { if (mIMEState == IME_STATE_DISABLED) { return false; } for (StackTraceElement frame : Thread.currentThread().getStackTrace()) { // We only return our custom Handler to InputMethodManager's InputConnection // proxy. For all other purposes, we return the regular Handler. // InputMethodManager retrieves the Handler for its InputConnection proxy // inside its method startInputInner(), so we check for that here. This is // valid from Android 2.2 to at least Android 4.2. If this situation ever // changes, we gracefully fall back to using the regular Handler. if ("startInputInner".equals(frame.getMethodName()) && "android.view.inputmethod.InputMethodManager".equals(frame.getClassName())) { - // only return our own Handler to InputMethodManager - return true; + // Only return our own Handler to InputMethodManager and only prior to 24. + return Build.VERSION.SDK_INT < 24; } if (CUSTOM_HANDLER_TEST_METHOD.equals(frame.getMethodName()) && CUSTOM_HANDLER_TEST_CLASS.equals(frame.getClassName())) { // InputConnection tests should also run on the custom handler return true; } } return false; @@ -512,34 +514,36 @@ class GeckoInputConnection } // Android N: @Override // InputConnection // We need to suppress lint complaining about the lack override here in the meantime: it wants us to build // against sdk 24, even though we're using 23, and therefore complains about the lack of override. // Once we update to 24, we can use the actual override annotation and remove the lint suppression. @SuppressLint("Override") public Handler getHandler() { + final Handler handler; if (isPhysicalKeyboardPresent()) { - return ThreadUtils.getUiHandler(); + handler = ThreadUtils.getUiHandler(); + } else { + handler = getBackgroundHandler(); } - - return getBackgroundHandler(); + return mEditableClient.setInputConnectionHandler(handler); } - @Override // InputConnectionListener + @Override // TextInputController.Delegate public Handler getHandler(Handler defHandler) { if (!canReturnCustomHandler()) { return defHandler; } - return mEditableClient.setInputConnectionHandler(getHandler()); + return getHandler(); } - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + @Override // TextInputController.Delegate + public synchronized InputConnection onCreateInputConnection(EditorInfo outAttrs) { // Some keyboards require us to fill out outAttrs even if we return null. outAttrs.inputType = InputType.TYPE_CLASS_TEXT; outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; outAttrs.actionLabel = null; if (mIMEState == IME_STATE_DISABLED) { hideSoftInput(); return null; @@ -741,17 +745,17 @@ class GeckoInputConnection } @Override public boolean sendKeyEvent(KeyEvent event) { sendKeyEvent(event.getAction(), event); return false; // seems to always return false } - @Override + @Override // TextInputController.Delegate public boolean onKeyPreIme(int keyCode, KeyEvent event) { return false; } private boolean shouldProcessKey(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_BACK: @@ -842,22 +846,22 @@ class GeckoInputConnection @Override public void run() { sendKeyEvent(action, event); } }); return true; } - @Override + @Override // TextInputController.Delegate public boolean onKeyDown(int keyCode, KeyEvent event) { return processKey(KeyEvent.ACTION_DOWN, keyCode, event); } - @Override + @Override // TextInputController.Delegate public boolean onKeyUp(int keyCode, KeyEvent event) { return processKey(KeyEvent.ACTION_UP, keyCode, event); } /** * Get a key that represents a given character. */ private KeyEvent getCharKeyEvent(final char c) { @@ -871,17 +875,17 @@ class GeckoInputConnection @Override public int getUnicodeChar(int metaState) { return c; } }; } - @Override + @Override // TextInputController.Delegate public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { // KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters() final String str = event.getCharacters(); for (int i = 0; i < str.length(); i++) { final KeyEvent charEvent = getCharKeyEvent(str.charAt(i)); if (!processKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN, charEvent) || !processKey(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_UNKNOWN, charEvent)) { @@ -895,33 +899,33 @@ class GeckoInputConnection if (!processKey(KeyEvent.ACTION_DOWN, keyCode, event) || !processKey(KeyEvent.ACTION_UP, keyCode, event)) { return false; } } return true; } - @Override + @Override // TextInputController.Delegate public boolean onKeyLongPress(int keyCode, KeyEvent event) { View v = getView(); switch (keyCode) { case KeyEvent.KEYCODE_MENU: InputMethodManager imm = getInputMethodManager(); imm.toggleSoftInputFromWindow(v.getWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; default: break; } return false; } - @Override - public boolean isIMEEnabled() { + @Override // TextInputController.Delegate + public synchronized boolean isInputActive() { // make sure this picks up PASSWORD and PLUGIN states as well return mIMEState != IME_STATE_DISABLED; } @Override public void notifyIME(int type) { switch (type) { @@ -968,18 +972,19 @@ class GeckoInputConnection if (DEBUG) { throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type); } break; } } @Override - public void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint, - boolean inPrivateBrowsing, boolean isUserAction) { + public synchronized void notifyIMEContext(int state, String typeHint, String modeHint, + String actionHint, boolean inPrivateBrowsing, + boolean isUserAction) { // For some input type we will use a widget to display the ui, for those we must not // display the ime. We can display a widget for date and time types and, if the sdk version // is 11 or greater, for datetime/month/week as well. if (typeHint != null && (typeHint.equalsIgnoreCase("date") || typeHint.equalsIgnoreCase("time") || typeHint.equalsIgnoreCase("datetime") || typeHint.equalsIgnoreCase("month") || @@ -1030,33 +1035,35 @@ class GeckoInputConnection final class DebugGeckoInputConnection extends GeckoInputConnection implements InvocationHandler { private InputConnection mProxy; private final StringBuilder mCallLevel; - private DebugGeckoInputConnection(GeckoView targetView, + private DebugGeckoInputConnection(GeckoSession session, + View targetView, GeckoEditableClient editable) { - super(targetView, editable); + super(session, targetView, editable); mCallLevel = new StringBuilder(); } - public static GeckoEditableListener create(GeckoView targetView, - GeckoEditableClient editable) { + public static TextInputController.Delegate create(GeckoSession session, + View targetView, + GeckoEditableClient editable) { final Class<?>[] PROXY_INTERFACES = { InputConnection.class, - InputConnectionListener.class, + TextInputController.Delegate.class, GeckoEditableListener.class }; DebugGeckoInputConnection dgic = - new DebugGeckoInputConnection(targetView, editable); - dgic.mProxy = (InputConnection)Proxy.newProxyInstance( + new DebugGeckoInputConnection(session, targetView, editable); + dgic.mProxy = (InputConnection) Proxy.newProxyInstance( GeckoInputConnection.class.getClassLoader(), PROXY_INTERFACES, dgic); - return (GeckoEditableListener)dgic.mProxy; + return (TextInputController.Delegate) dgic.mProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { StringBuilder log = new StringBuilder(mCallLevel); log.append("> ").append(method.getName()).append("(");