Commit 001d21a7 authored by Changwan Ryu's avatar Changwan Ryu Committed by Commit Bot

Fix focus to scroll input into view consistently

Focus gain causes virtual keyboard to show. Then virtual keyboard takes
the bottom inset and causes the window and view size to shrink, on
chrome and sometimes on webview.
At this point, the bottom inset may occlude the input form,
i.e. the input form is behind the keyboard and is not visible.

We have the following auto-scrolling logic to mitigate the inconvenience
here:
1) Check the size of visible display size after showing keyboard.
2) Wait for the view height change after showing keyboard.
3) Change the viewport size
4) Scroll focused editable node into view.

In the browser, this is in order. However, in the renderer, the order
of 3) and 4) sometimes gets reversed.
I suspect that this happens because the two messages are going through
different IPC channels.

Now we do the following:
1) Wait until SynchronizeVisualProperties() changes visual viewport.
2) Detect that blink's scrollable viewport size has changed
3) Comfirm this signal against the visible display size
4) Scroll focused editable node into view

This also adds some test and refactors ImeAdapterImpl.

Bug: 920061

Change-Id: Id237fa76c304c92b5459bda4fe6591c96cb804d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1562585Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Commit-Queue: Changwan Ryu <changwan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682991}
parent 1d673a61
...@@ -10,6 +10,7 @@ import android.content.Context; ...@@ -10,6 +10,7 @@ import android.content.Context;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule; import android.support.test.rule.ActivityTestRule;
import android.util.AndroidRuntimeException; import android.util.AndroidRuntimeException;
import android.util.Base64;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.junit.Assert; import org.junit.Assert;
...@@ -236,6 +237,13 @@ public class AwActivityTestRule extends ActivityTestRule<AwTestRunnerActivity> { ...@@ -236,6 +237,13 @@ public class AwActivityTestRule extends ActivityTestRule<AwTestRunnerActivity> {
currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} }
public void loadHtmlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
final String html) throws Throwable {
int currentCallCount = onPageFinishedHelper.getCallCount();
final String encodedData = Base64.encodeToString(html.getBytes(), Base64.NO_PADDING);
loadDataSync(awContents, onPageFinishedHelper, encodedData, "text/html", true);
}
public void loadDataSyncWithCharset(final AwContents awContents, public void loadDataSyncWithCharset(final AwContents awContents,
CallbackHelper onPageFinishedHelper, final String data, final String mimeType, CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
final boolean isBase64Encoded, final String charset) throws Exception { final boolean isBase64Encoded, final String charset) throws Exception {
......
...@@ -4,13 +4,20 @@ ...@@ -4,13 +4,20 @@
package org.chromium.android_webview.test; package org.chromium.android_webview.test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.ViewGroup;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.JavascriptInterface; import android.webkit.JavascriptInterface;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
...@@ -21,8 +28,10 @@ import org.chromium.base.test.util.CallbackHelper; ...@@ -21,8 +28,10 @@ import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.ImeAdapter; import org.chromium.content_public.browser.ImeAdapter;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.test.util.TestInputMethodManagerWrapper; import org.chromium.content_public.browser.test.util.TestInputMethodManagerWrapper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
...@@ -60,9 +69,21 @@ public class AwImeTest { ...@@ -60,9 +69,21 @@ public class AwImeTest {
// Use detached container view to avoid focus request. // Use detached container view to avoid focus request.
mTestContainerView = mTestContainerView =
mActivityTestRule.createDetachedAwTestContainerView(mContentsClient); mActivityTestRule.createDetachedAwTestContainerView(mContentsClient);
mTestContainerView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mEditText = new EditText(mActivityTestRule.getActivity()); mEditText = new EditText(mActivityTestRule.getActivity());
mActivityTestRule.getActivity().addView(mEditText); LinearLayout linearLayout = new LinearLayout(mActivityTestRule.getActivity());
mActivityTestRule.getActivity().addView(mTestContainerView); linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// Ensures that we don't autofocus EditText.
linearLayout.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
linearLayout.setFocusableInTouchMode(true);
mActivityTestRule.getActivity().addView(linearLayout);
linearLayout.addView(mEditText);
linearLayout.addView(mTestContainerView);
mTestContainerView.getAwContents().addJavascriptInterface( mTestContainerView.getAwContents().addJavascriptInterface(
mTestJavascriptInterface, "test"); mTestJavascriptInterface, "test");
// Let's not test against real input method. // Let's not test against real input method.
...@@ -70,6 +91,7 @@ public class AwImeTest { ...@@ -70,6 +91,7 @@ public class AwImeTest {
imeAdapter.setInputMethodManagerWrapper( imeAdapter.setInputMethodManagerWrapper(
TestInputMethodManagerWrapper.create(imeAdapter)); TestInputMethodManagerWrapper.create(imeAdapter));
}); });
AwActivityTestRule.enableJavaScriptOnUiThread(mTestContainerView.getAwContents());
} }
private void loadContentEditableBody() throws Exception { private void loadContentEditableBody() throws Exception {
...@@ -81,6 +103,18 @@ public class AwImeTest { ...@@ -81,6 +103,18 @@ public class AwImeTest {
mTestContainerView.getAwContents(), loadHelper, htmlDocument, mime, false); mTestContainerView.getAwContents(), loadHelper, htmlDocument, mime, false);
} }
private void loadBottomInputHtml() throws Throwable {
// Shows an input at the bottom of the screen.
final String htmlDocument = "<html><head>"
+ "<style>html, body{background-color:beige} "
+ "div{position:absolute;top:10000px;}</style></head>"
+ "<body>Test<div id='footer'><input id='input_text'><br/></div></body></html>";
final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper();
mActivityTestRule.loadHtmlSync(
mTestContainerView.getAwContents(), loadHelper, htmlDocument);
}
private void focusOnEditTextAndShowKeyboard() { private void focusOnEditTextAndShowKeyboard() {
TestThreadUtils.runOnUiThreadBlocking(() -> { TestThreadUtils.runOnUiThreadBlocking(() -> {
mEditText.requestFocus(); mEditText.requestFocus();
...@@ -94,7 +128,6 @@ public class AwImeTest { ...@@ -94,7 +128,6 @@ public class AwImeTest {
private void focusOnWebViewAndEnableEditing() throws Exception { private void focusOnWebViewAndEnableEditing() throws Exception {
TestThreadUtils.runOnUiThreadBlocking((Runnable) () -> mTestContainerView.requestFocus()); TestThreadUtils.runOnUiThreadBlocking((Runnable) () -> mTestContainerView.requestFocus());
AwActivityTestRule.enableJavaScriptOnUiThread(mTestContainerView.getAwContents());
// View focus may not have been propagated to the renderer process yet. If document is not // View focus may not have been propagated to the renderer process yet. If document is not
// yet focused, and focusing on an element is an invalid operation. See crbug.com/622151 // yet focused, and focusing on an element is an invalid operation. See crbug.com/622151
// for details. // for details.
...@@ -207,4 +240,60 @@ public class AwImeTest { ...@@ -207,4 +240,60 @@ public class AwImeTest {
} }
}); });
} }
private void scrollBottomOfNodeIntoView(String nodeId) throws Exception {
mActivityTestRule.executeJavaScriptAndWaitForResult(mTestContainerView.getAwContents(),
mContentsClient,
"document.getElementById('" + nodeId + "').scrollIntoView(false);");
}
private float getScrollY() throws Exception {
float res = Float.parseFloat(mActivityTestRule.executeJavaScriptAndWaitForResult(
mTestContainerView.getAwContents(), mContentsClient, "window.scrollY"));
return res;
}
// https://crbug.com/920061
@Test
@SmallTest
public void testFocusAndViewSizeChangeCausesScroll() throws Throwable {
loadBottomInputHtml();
Rect currentRect = new Rect();
TestThreadUtils.runOnUiThreadBlocking(
() -> { mTestContainerView.getWindowVisibleDisplayFrame(currentRect); });
WebContents webContents = mTestContainerView.getAwContents().getWebContents();
DOMUtils.waitForNonZeroNodeBounds(webContents, "input_text");
float initialScrollY = getScrollY();
scrollBottomOfNodeIntoView("footer");
// footer's offset is 10000 px from top. Scrolling this node into view should definitely
// change the scroll.
float scrollYAtBottom = getScrollY();
assertThat(scrollYAtBottom, greaterThan(initialScrollY));
DOMUtils.clickNode(webContents, "input_text", true /* goThroughAndroidRootView */,
false /* shouldScrollIntoView */);
TestThreadUtils.runOnUiThreadBlocking(() -> {
// Expect that we may have a size change.
ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mTestContainerView.getWebContents());
imeAdapter.onShowKeyboardReceiveResult(InputMethodManager.RESULT_SHOWN);
// When a virtual keyboard shows up, the window and view size shrink. Note that we are
// not depend on the real virtual keyboard behavior here. We only emulate the behavior
// by shrinking the view height.
currentRect.bottom = currentRect.centerY();
mTestContainerView.setWindowVisibleDisplayFrameOverride(currentRect);
int width = mTestContainerView.getWidth();
int height = mTestContainerView.getHeight();
mTestContainerView.onSizeChanged(width, height / 2, width, height);
});
// Scrolling may take some time. Ensures that scrolling happened.
CriteriaHelper.pollInstrumentationThread(
() -> (getScrollY() > scrollYAtBottom), "Scrolling should happen.");
}
} }
...@@ -48,6 +48,8 @@ public class AwTestContainerView extends FrameLayout { ...@@ -48,6 +48,8 @@ public class AwTestContainerView extends FrameLayout {
private HardwareView mHardwareView; private HardwareView mHardwareView;
private boolean mAttachedContents; private boolean mAttachedContents;
private Rect mWindowVisibleDisplayFrameOverride;
private class HardwareView extends GLSurfaceView { private class HardwareView extends GLSurfaceView {
private static final int MODE_DRAW = 0; private static final int MODE_DRAW = 0;
private static final int MODE_PROCESS = 1; private static final int MODE_PROCESS = 1;
...@@ -281,6 +283,19 @@ public class AwTestContainerView extends FrameLayout { ...@@ -281,6 +283,19 @@ public class AwTestContainerView extends FrameLayout {
} }
} }
public void setWindowVisibleDisplayFrameOverride(Rect rect) {
mWindowVisibleDisplayFrameOverride = rect;
}
@Override
public void getWindowVisibleDisplayFrame(Rect outRect) {
if (mWindowVisibleDisplayFrameOverride != null) {
outRect.set(mWindowVisibleDisplayFrameOverride);
} else {
super.getWindowVisibleDisplayFrame(outRect);
}
}
public boolean isBackedByHardwareView() { public boolean isBackedByHardwareView() {
return mHardwareView != null; return mHardwareView != null;
} }
......
...@@ -189,14 +189,6 @@ void ImeAdapterAndroid::UpdateState(const TextInputState& state) { ...@@ -189,14 +189,6 @@ void ImeAdapterAndroid::UpdateState(const TextInputState& state) {
state.composition_end, state.reply_to_request); state.composition_end, state.reply_to_request);
} }
void ImeAdapterAndroid::UpdateAfterViewSizeChanged() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
if (obj.is_null())
return;
Java_ImeAdapterImpl_updateAfterViewSizeChanged(env, obj);
}
void ImeAdapterAndroid::UpdateOnTouchDown() { void ImeAdapterAndroid::UpdateOnTouchDown() {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env); ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
...@@ -232,6 +224,24 @@ void ImeAdapterAndroid::UpdateFrameInfo( ...@@ -232,6 +224,24 @@ void ImeAdapterAndroid::UpdateFrameInfo(
insertion_marker_top, insertion_marker_bottom); insertion_marker_top, insertion_marker_bottom);
} }
void ImeAdapterAndroid::OnRenderFrameMetadataChangedAfterActivation(
const gfx::SizeF& new_viewport_size) {
if (old_viewport_size_ == new_viewport_size)
return;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ime_adapter_.get(env);
if (obj.is_null())
return;
const jboolean surface_height_reduced =
new_viewport_size.width() == old_viewport_size_.width() &&
new_viewport_size.height() < old_viewport_size_.height();
old_viewport_size_ = new_viewport_size;
Java_ImeAdapterImpl_onResizeScrollableViewport(env, obj,
surface_height_reduced);
}
bool ImeAdapterAndroid::SendKeyEvent( bool ImeAdapterAndroid::SendKeyEvent(
JNIEnv* env, JNIEnv* env,
const JavaParamRef<jobject>&, const JavaParamRef<jobject>&,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "content/browser/android/render_widget_host_connector.h" #include "content/browser/android/render_widget_host_connector.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
namespace ui { namespace ui {
...@@ -99,6 +100,7 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector { ...@@ -99,6 +100,7 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector {
void UpdateFrameInfo(const gfx::SelectionBound& selection_start, void UpdateFrameInfo(const gfx::SelectionBound& selection_start,
float dip_scale, float dip_scale,
float content_offset_ypix); float content_offset_ypix);
void OnRenderFrameMetadataChangedAfterActivation(const gfx::SizeF& new_size);
// Called from native -> java // Called from native -> java
void CancelComposition(); void CancelComposition();
...@@ -111,7 +113,6 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector { ...@@ -111,7 +113,6 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector {
} }
void UpdateState(const TextInputState& state); void UpdateState(const TextInputState& state);
void UpdateAfterViewSizeChanged();
void UpdateOnTouchDown(); void UpdateOnTouchDown();
void AdvanceFocusInForm(JNIEnv*, void AdvanceFocusInForm(JNIEnv*,
...@@ -128,6 +129,8 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector { ...@@ -128,6 +129,8 @@ class CONTENT_EXPORT ImeAdapterAndroid : public RenderWidgetHostConnector {
const base::android::JavaParamRef<jobject>& text, const base::android::JavaParamRef<jobject>& text,
const base::string16& text16); const base::string16& text16);
gfx::SizeF old_viewport_size_;
// Current RenderWidgetHostView connected to this instance. Can be null. // Current RenderWidgetHostView connected to this instance. Can be null.
RenderWidgetHostViewAndroid* rwhva_; RenderWidgetHostViewAndroid* rwhva_;
JavaObjectWeakGlobalRef java_ime_adapter_; JavaObjectWeakGlobalRef java_ime_adapter_;
......
...@@ -559,6 +559,23 @@ void RenderWidgetHostViewAndroid::WriteContentBitmapToDiskAsync( ...@@ -559,6 +559,23 @@ void RenderWidgetHostViewAndroid::WriteContentBitmapToDiskAsync(
std::move(result_callback)); std::move(result_callback));
} }
void RenderWidgetHostViewAndroid::
OnRenderFrameMetadataChangedAfterActivation() {
if (ime_adapter_android_) {
const cc::RenderFrameMetadata& metadata =
host()->render_frame_metadata_provider()->LastRenderFrameMetadata();
// We need to first wait for Blink's viewport size to change such that we
// can correctly scroll to the currently focused input.
// On Clank, only visible viewport size changes and device viewport size or
// viewport_size_in_pixels do not change according to the window/view size
/// change. Only scrollable viewport size changes both for Chrome and
// WebView.
ime_adapter_android_->OnRenderFrameMetadataChangedAfterActivation(
metadata.scrollable_viewport_size);
}
RenderWidgetHostViewBase::OnRenderFrameMetadataChangedAfterActivation();
}
void RenderWidgetHostViewAndroid::Focus() { void RenderWidgetHostViewAndroid::Focus() {
if (view_.HasFocus()) if (view_.HasFocus())
GotFocus(); GotFocus();
...@@ -2221,11 +2238,6 @@ bool RenderWidgetHostViewAndroid::RequiresDoubleTapGestureEvents() const { ...@@ -2221,11 +2238,6 @@ bool RenderWidgetHostViewAndroid::RequiresDoubleTapGestureEvents() const {
return true; return true;
} }
void RenderWidgetHostViewAndroid::OnSizeChanged() {
if (ime_adapter_android_)
ime_adapter_android_->UpdateAfterViewSizeChanged();
}
void RenderWidgetHostViewAndroid::OnPhysicalBackingSizeChanged() { void RenderWidgetHostViewAndroid::OnPhysicalBackingSizeChanged() {
EvictFrameIfNecessary(); EvictFrameIfNecessary();
// We may need to update the background color to match pre-surface-sync // We may need to update the background color to match pre-surface-sync
......
...@@ -202,7 +202,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid ...@@ -202,7 +202,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid
bool OnMouseEvent(const ui::MotionEventAndroid& m) override; bool OnMouseEvent(const ui::MotionEventAndroid& m) override;
bool OnMouseWheelEvent(const ui::MotionEventAndroid& event) override; bool OnMouseWheelEvent(const ui::MotionEventAndroid& event) override;
bool OnGestureEvent(const ui::GestureEventAndroid& event) override; bool OnGestureEvent(const ui::GestureEventAndroid& event) override;
void OnSizeChanged() override;
void OnPhysicalBackingSizeChanged() override; void OnPhysicalBackingSizeChanged() override;
// ui::ViewAndroidObserver implementation: // ui::ViewAndroidObserver implementation:
...@@ -346,6 +345,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid ...@@ -346,6 +345,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid
// RenderFrameMetadataProvider::Observer // RenderFrameMetadataProvider::Observer
void OnRenderFrameMetadataChangedBeforeActivation( void OnRenderFrameMetadataChangedBeforeActivation(
const cc::RenderFrameMetadata& metadata) override; const cc::RenderFrameMetadata& metadata) override;
void OnRenderFrameMetadataChangedAfterActivation() override;
void WasEvicted(); void WasEvicted();
......
...@@ -512,10 +512,7 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData ...@@ -512,10 +512,7 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData
} }
} }
/** @Override
* Call this when we get result from ResultReceiver passed in calling showSoftInput().
* @param resultCode The result of showSoftInput() as defined in InputMethodManager.
*/
public void onShowKeyboardReceiveResult(int resultCode) { public void onShowKeyboardReceiveResult(int resultCode) {
View containerView = getContainerView(); View containerView = getContainerView();
if (resultCode == InputMethodManager.RESULT_SHOWN) { if (resultCode == InputMethodManager.RESULT_SHOWN) {
...@@ -534,25 +531,6 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData ...@@ -534,25 +531,6 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData
} }
} }
@CalledByNative
private void updateAfterViewSizeChanged() {
// Execute a delayed form focus operation because the OSK was brought up earlier.
if (!mFocusPreOSKViewportRect.isEmpty()) {
Rect rect = new Rect();
getContainerView().getWindowVisibleDisplayFrame(rect);
if (!rect.equals(mFocusPreOSKViewportRect)) {
// Only assume the OSK triggered the onSizeChanged if width was preserved.
if (rect.width() == mFocusPreOSKViewportRect.width()) {
assert mWebContents != null;
mWebContents.scrollFocusedEditableNodeIntoView();
}
// Zero the rect to prevent the above operation from issuing the delayed
// form focus event.
cancelRequestToScrollFocusedEditableNodeIntoView();
}
}
}
@CalledByNative @CalledByNative
private void updateOnTouchDown() { private void updateOnTouchDown() {
cancelRequestToScrollFocusedEditableNodeIntoView(); cancelRequestToScrollFocusedEditableNodeIntoView();
...@@ -996,6 +974,30 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData ...@@ -996,6 +974,30 @@ public class ImeAdapterImpl implements ImeAdapter, WindowEventObserver, UserData
insertionMarkerTop, insertionMarkerBottom, getContainerView()); insertionMarkerTop, insertionMarkerBottom, getContainerView());
} }
@CalledByNative
private void onResizeScrollableViewport(boolean contentsHeightReduced) {
if (!contentsHeightReduced) {
cancelRequestToScrollFocusedEditableNodeIntoView();
return;
}
// Execute a delayed form focus operation because the OSK was brought up earlier.
if (!mFocusPreOSKViewportRect.isEmpty()) {
Rect rect = new Rect();
getContainerView().getWindowVisibleDisplayFrame(rect);
if (!rect.equals(mFocusPreOSKViewportRect)) {
// Only assume the OSK triggered the onSizeChanged if width was preserved.
if (rect.width() == mFocusPreOSKViewportRect.width()) {
assert mWebContents != null;
mWebContents.scrollFocusedEditableNodeIntoView();
}
// Zero the rect to prevent the above operation from issuing the delayed
// form focus event.
cancelRequestToScrollFocusedEditableNodeIntoView();
}
}
}
private int getUnderlineColorForSuggestionSpan(SuggestionSpan suggestionSpan) { private int getUnderlineColorForSuggestionSpan(SuggestionSpan suggestionSpan) {
try { try {
Method getUnderlineColorMethod = SuggestionSpan.class.getMethod("getUnderlineColor"); Method getUnderlineColorMethod = SuggestionSpan.class.getMethod("getUnderlineColor");
......
...@@ -93,4 +93,11 @@ public interface ImeAdapter { ...@@ -93,4 +93,11 @@ public interface ImeAdapter {
*/ */
@VisibleForTesting @VisibleForTesting
void setComposingTextForTest(final CharSequence text, final int newCursorPosition); void setComposingTextForTest(final CharSequence text, final int newCursorPosition);
/**
* Call this when we get result from ResultReceiver passed in calling showSoftInput().
* @param resultCode The result of showSoftInput() as defined in InputMethodManager.
*/
@VisibleForTesting
void onShowKeyboardReceiveResult(int resultCode);
} }
...@@ -253,7 +253,22 @@ public class DOMUtils { ...@@ -253,7 +253,22 @@ public class DOMUtils {
*/ */
public static boolean clickNode(final WebContents webContents, String nodeId, public static boolean clickNode(final WebContents webContents, String nodeId,
boolean goThroughRootAndroidView) throws InterruptedException, TimeoutException { boolean goThroughRootAndroidView) throws InterruptedException, TimeoutException {
scrollNodeIntoView(webContents, nodeId); return clickNode(
webContents, nodeId, goThroughRootAndroidView, true /* shouldScrollIntoView */);
}
/**
* Click a DOM node by its id.
* @param webContents The WebContents in which the node lives.
* @param nodeId The id of the node.
* @param goThroughRootAndroidView Whether the input should be routed through the Root View for
* the CVC.
* @param shouldScrollIntoView Whether to scroll the node into view first.
*/
public static boolean clickNode(final WebContents webContents, String nodeId,
boolean goThroughRootAndroidView, boolean shouldScrollIntoView)
throws InterruptedException, TimeoutException {
if (shouldScrollIntoView) scrollNodeIntoView(webContents, nodeId);
int[] clickTarget = getClickTargetForNode(webContents, nodeId); int[] clickTarget = getClickTargetForNode(webContents, nodeId);
if (goThroughRootAndroidView) { if (goThroughRootAndroidView) {
return TouchCommon.singleClickView( return TouchCommon.singleClickView(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment