summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-20 22:40:30 +0000
committermkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-20 22:40:30 +0000
commit36a2df7239f2fb104d77981f7edb0ecff3d8c84d (patch)
tree616812c3f5398caf0af122e838a268bab6d00671
parent4e40d83a011b8651f7f7f1da33dff5a37af93238 (diff)
downloadchromium_src-36a2df7239f2fb104d77981f7edb0ecff3d8c84d.zip
chromium_src-36a2df7239f2fb104d77981f7edb0ecff3d8c84d.tar.gz
chromium_src-36a2df7239f2fb104d77981f7edb0ecff3d8c84d.tar.bz2
Hookup android_webview scroll offset delegation to Java side.
This connects the scroll offset that is delegated from the CC all the way up to the Java android.view.View class. BUG=b/6029133 TEST=AndroidWebViewTest Review URL: https://chromiumcodereview.appspot.com/16255010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207614 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--android_webview/browser/browser_view_renderer.h16
-rw-r--r--android_webview/browser/in_process_view_renderer.cc47
-rw-r--r--android_webview/browser/in_process_view_renderer.h14
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwContents.java49
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java50
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java349
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java19
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java27
-rw-r--r--android_webview/native/aw_contents.cc20
-rw-r--r--android_webview/native/aw_contents.h4
-rw-r--r--android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java16
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/ContentView.java6
-rw-r--r--content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java13
-rw-r--r--content/renderer/gpu/input_handler_manager_client.h5
-rw-r--r--ui/gfx/vector2d_f.h4
15 files changed, 576 insertions, 63 deletions
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index 52f917e..dad70ed 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -8,6 +8,7 @@
#include "base/android/scoped_java_ref.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
+#include "ui/gfx/vector2d_f.h"
struct AwDrawGLInfo;
struct AwDrawSWFunctionTable;
@@ -16,10 +17,6 @@ namespace content {
class ContentViewCore;
}
-namespace gfx {
-class Rect;
-}
-
namespace android_webview {
// Interface for all the WebView-specific content rendering operations.
@@ -43,6 +40,9 @@ class BrowserViewRenderer {
// Called to get view's absolute location on the screen.
virtual gfx::Point GetLocationOnScreen() = 0;
+ // Try to set the view's scroll offset to |new_value|.
+ virtual void ScrollContainerViewTo(gfx::Vector2d new_value) = 0;
+
protected:
virtual ~Client() {}
};
@@ -88,7 +88,7 @@ class BrowserViewRenderer {
// scroll offset. |clip| is the canvas's clip bounds.
virtual bool OnDraw(jobject java_canvas,
bool is_hardware_canvas,
- const gfx::Point& scroll,
+ const gfx::Vector2d& scroll,
const gfx::Rect& clip) = 0;
// Called in response to a prior Client::RequestDrawGL() call. See
// AwDrawGLInfo documentation for more details of the contract.
@@ -104,6 +104,12 @@ class BrowserViewRenderer {
virtual void OnAttachedToWindow(int width, int height) = 0;
virtual void OnDetachedFromWindow() = 0;
+ // Sets the scale for logical<->physical pixel conversions.
+ virtual void SetDipScale(float dip_scale) = 0;
+
+ // Set the root layer scroll offset to |new_value|.
+ virtual void ScrollTo(gfx::Vector2d new_value) = 0;
+
// Android views hierarchy gluing.
virtual bool IsAttachedToWindow() = 0;
virtual bool IsViewVisible() = 0;
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc
index 28810e2..acb6db8 100644
--- a/android_webview/browser/in_process_view_renderer.cc
+++ b/android_webview/browser/in_process_view_renderer.cc
@@ -25,6 +25,7 @@
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/transform.h"
+#include "ui/gfx/vector2d_conversions.h"
#include "ui/gfx/vector2d_f.h"
#include "ui/gl/gl_bindings.h"
@@ -306,6 +307,7 @@ InProcessViewRenderer::InProcessViewRenderer(
web_contents_(web_contents),
compositor_(NULL),
view_visible_(false),
+ dip_scale_(0.0),
continuous_invalidate_(false),
block_invalidates_(false),
width_(0),
@@ -341,7 +343,7 @@ void InProcessViewRenderer::WebContentsGone() {
bool InProcessViewRenderer::OnDraw(jobject java_canvas,
bool is_hardware_canvas,
- const gfx::Point& scroll,
+ const gfx::Vector2d& scroll,
const gfx::Rect& clip) {
scroll_at_start_of_frame_ = scroll;
if (is_hardware_canvas && attached_to_window_ && compositor_ &&
@@ -643,14 +645,49 @@ void InProcessViewRenderer::SetContinuousInvalidate(bool invalidate) {
EnsureContinuousInvalidation(NULL);
}
+void InProcessViewRenderer::SetDipScale(float dip_scale) {
+ dip_scale_ = dip_scale;
+ CHECK(dip_scale_ > 0);
+}
+
+void InProcessViewRenderer::ScrollTo(gfx::Vector2d new_value) {
+ DCHECK(dip_scale_ > 0);
+ // In general we don't guarantee that the scroll offset transforms are
+ // symmetrical. That is if scrolling from JS to offset1 results in a native
+ // offset2 then scrolling from UI to offset2 results in JS being scrolled to
+ // offset1 again.
+ // The reason we explicitly do rounding here is that it seems to yeld the
+ // most stabile transformation.
+ gfx::Vector2dF new_value_css = gfx::ToRoundedVector2d(
+ gfx::ScaleVector2d(new_value, 1.0f / dip_scale_));
+
+ DCHECK(scroll_offset_css_ != new_value_css);
+
+ scroll_offset_css_ = new_value_css;
+
+ if (compositor_)
+ compositor_->DidChangeRootLayerScrollOffset();
+}
+
void InProcessViewRenderer::SetTotalRootLayerScrollOffset(
- gfx::Vector2dF new_value) {
- // TODO(mkosiba): Plumb this all the way through to the view.
- scroll_offset_ = new_value;
+ gfx::Vector2dF new_value_css) {
+ // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during
+ // DrawGl when http://crbug.com/249972 is fixed.
+ if (scroll_offset_css_ == new_value_css)
+ return;
+
+ scroll_offset_css_ = new_value_css;
+
+ DCHECK(dip_scale_ > 0);
+
+ gfx::Vector2d scroll_offset =
+ gfx::ToRoundedVector2d(gfx::ScaleVector2d(new_value_css, dip_scale_));
+
+ client_->ScrollContainerViewTo(scroll_offset);
}
gfx::Vector2dF InProcessViewRenderer::GetTotalRootLayerScrollOffset() {
- return scroll_offset_;
+ return scroll_offset_css_;
}
void InProcessViewRenderer::EnsureContinuousInvalidation(
diff --git a/android_webview/browser/in_process_view_renderer.h b/android_webview/browser/in_process_view_renderer.h
index 73262bf..db85926 100644
--- a/android_webview/browser/in_process_view_renderer.h
+++ b/android_webview/browser/in_process_view_renderer.h
@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "content/public/browser/android/synchronous_compositor_client.h"
+#include "ui/gfx/vector2d_f.h"
namespace content {
class SynchronousCompositor;
@@ -36,7 +37,7 @@ class InProcessViewRenderer : public BrowserViewRenderer,
// BrowserViewRenderer overrides
virtual bool OnDraw(jobject java_canvas,
bool is_hardware_canvas,
- const gfx::Point& scroll,
+ const gfx::Vector2d& scroll_,
const gfx::Rect& clip) OVERRIDE;
virtual void DrawGL(AwDrawGLInfo* draw_info) OVERRIDE;
virtual base::android::ScopedJavaLocalRef<jobject> CapturePicture() OVERRIDE;
@@ -44,8 +45,10 @@ class InProcessViewRenderer : public BrowserViewRenderer,
virtual void OnVisibilityChanged(
bool view_visible, bool window_visible) OVERRIDE;
virtual void OnSizeChanged(int width, int height) OVERRIDE;
+ virtual void ScrollTo(gfx::Vector2d new_value) OVERRIDE;
virtual void OnAttachedToWindow(int width, int height) OVERRIDE;
virtual void OnDetachedFromWindow() OVERRIDE;
+ virtual void SetDipScale(float dip_scale) OVERRIDE;
virtual bool IsAttachedToWindow() OVERRIDE;
virtual bool IsViewVisible() OVERRIDE;
virtual gfx::Rect GetScreenRect() OVERRIDE;
@@ -56,7 +59,8 @@ class InProcessViewRenderer : public BrowserViewRenderer,
virtual void DidDestroyCompositor(
content::SynchronousCompositor* compositor) OVERRIDE;
virtual void SetContinuousInvalidate(bool invalidate) OVERRIDE;
- virtual void SetTotalRootLayerScrollOffset(gfx::Vector2dF new_value) OVERRIDE;
+ virtual void SetTotalRootLayerScrollOffset(
+ gfx::Vector2dF new_value_css) OVERRIDE;
virtual gfx::Vector2dF GetTotalRootLayerScrollOffset() OVERRIDE;
void WebContentsGone();
@@ -74,6 +78,7 @@ class InProcessViewRenderer : public BrowserViewRenderer,
content::SynchronousCompositor* compositor_;
bool view_visible_;
+ float dip_scale_;
// When true, we should continuously invalidate and keep drawing, for example
// to drive animation.
@@ -95,9 +100,10 @@ class InProcessViewRenderer : public BrowserViewRenderer,
EGLContext last_egl_context_;
// Last View scroll when View.onDraw() was called.
- gfx::Point scroll_at_start_of_frame_;
+ gfx::Vector2d scroll_at_start_of_frame_;
- gfx::Vector2dF scroll_offset_;
+ // Current scroll offset in CSS pixels.
+ gfx::Vector2dF scroll_offset_css_;
DISALLOW_COPY_AND_ASSIGN(InProcessViewRenderer);
};
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index cec63c2..4f8b72b 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -92,6 +92,14 @@ public class AwContents {
*/
public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate {
/**
+ * @see View#onScrollChanged(int, int, int, int)
+ *
+ * TODO(mkosiba): WebViewClassic calls this, AwContents doesn't. Check if there
+ * are any cases we're missing, if not - remove.
+ */
+ void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
+
+ /**
* @see View#setMeasuredDimension(int, int)
*/
void setMeasuredDimension(int measuredWidth, int measuredHeight);
@@ -119,6 +127,7 @@ public class AwContents {
private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
private final InternalAccessDelegate mInternalAccessAdapter;
private final AwLayoutSizer mLayoutSizer;
+ private final AwScrollOffsetManager mScrollOffsetManager;
private final AwZoomControls mZoomControls;
// This can be accessed on any thread after construction. See AwContentsIoThreadClient.
private final AwSettings mSettings;
@@ -280,6 +289,30 @@ public class AwContents {
}
//--------------------------------------------------------------------------------------------
+ private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
+ @Override
+ public boolean scrollContainerViewTo(int x, int y) {
+ mContainerView.scrollTo(x, y);
+ return (x == mContainerView.getScrollX() && y == mContainerView.getScrollY());
+ }
+
+ @Override
+ public void scrollNativeTo(int x, int y) {
+ nativeScrollTo(mNativeAwContents, x, y);
+ }
+
+ @Override
+ public int getContainerViewScrollX() {
+ return mContainerView.getScrollX();
+ }
+
+ @Override
+ public int getContainerViewScrollY() {
+ return mContainerView.getScrollY();
+ }
+ }
+
+ //--------------------------------------------------------------------------------------------
private class AwPinchGestureStateListener implements ContentViewCore.PinchGestureStateListener {
@Override
public void onPinchGestureStart() {
@@ -384,6 +417,7 @@ public class AwContents {
mSettings.setDefaultVideoPosterURL(
mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL());
mContentsClient.setDIPScale(mDIPScale);
+ mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate());
setNewAwContents(nativeInit(browserContext));
}
@@ -421,6 +455,7 @@ public class AwContents {
mIoThreadClient, mInterceptNavigationDelegate);
mContentsClient.installWebContentsObserver(mContentViewCore);
mSettings.setWebContents(nativeWebContents);
+ nativeSetDipScale(mNativeAwContents, (float) mDIPScale);
}
/**
@@ -556,6 +591,13 @@ public class AwContents {
return (int) Math.ceil(mContentViewCore.getContentWidthCss());
}
+ /**
+ * Called by the embedder when the scroll offset of the containing view has changed.
+ */
+ public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) {
+ mScrollOffsetManager.onContainerViewScrollChanged(l, t);
+ }
+
public Picture capturePicture() {
return nativeCapturePicture(mNativeAwContents);
}
@@ -1398,6 +1440,11 @@ public class AwContents {
mLayoutSizer.onPageScaleChanged(pageScaleFactor);
}
+ @CalledByNative
+ private void scrollContainerViewTo(int x, int y) {
+ mScrollOffsetManager.scrollContainerViewTo(x, y);
+ }
+
// -------------------------------------------------------------------------------------------
// Helper methods
// -------------------------------------------------------------------------------------------
@@ -1485,10 +1532,12 @@ public class AwContents {
private native void nativeUpdateLastHitTestData(int nativeAwContents);
private native void nativeOnSizeChanged(int nativeAwContents, int w, int h, int ow, int oh);
+ private native void nativeScrollTo(int nativeAwContents, int x, int y);
private native void nativeSetWindowViewVisibility(int nativeAwContents, boolean windowVisible,
boolean viewVisible);
private native void nativeOnAttachedToWindow(int nativeAwContents, int w, int h);
private native void nativeOnDetachedFromWindow(int nativeAwContents);
+ private native void nativeSetDipScale(int nativeAwContents, float dipScale);
// Returns null if save state fails.
private native byte[] nativeGetOpaqueState(int nativeAwContents);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java b/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java
new file mode 100644
index 0000000..c85b079
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java
@@ -0,0 +1,50 @@
+// Copyright 2013 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.android_webview;
+
+import org.chromium.base.CalledByNative;
+
+class AwScrollOffsetManager {
+ // The unit of all the values in this delegate are physical pixels
+ public interface Delegate {
+ // Returns whether the update succeeded
+ boolean scrollContainerViewTo(int x, int y);
+ void scrollNativeTo(int x, int y);
+ int getContainerViewScrollX();
+ int getContainerViewScrollY();
+ }
+
+ final Delegate mDelegate;
+ int mNativeScrollX;
+ int mNativeScrollY;
+
+ public AwScrollOffsetManager(Delegate delegate) {
+ mDelegate = delegate;
+ }
+
+ public void scrollContainerViewTo(int x, int y) {
+ mNativeScrollX = x;
+ mNativeScrollY = y;
+
+ if (!mDelegate.scrollContainerViewTo(x, y)) {
+ scrollNativeTo(mDelegate.getContainerViewScrollX(),
+ mDelegate.getContainerViewScrollY());
+ }
+ }
+
+ public void onContainerViewScrollChanged(int x, int y) {
+ scrollNativeTo(x, y);
+ }
+
+ private void scrollNativeTo(int x, int y) {
+ if (x == mNativeScrollX && y == mNativeScrollY)
+ return;
+
+ mNativeScrollX = x;
+ mNativeScrollY = y;
+
+ mDelegate.scrollNativeTo(x, y);
+ }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
new file mode 100644
index 0000000..f57acc0
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -0,0 +1,349 @@
+// Copyright 2013 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.android_webview.test;
+
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsClient;
+import org.chromium.android_webview.test.util.CommonResources;
+import org.chromium.android_webview.test.util.JavascriptEventObserver;
+import org.chromium.base.test.util.Feature;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.util.CallbackHelper;
+import org.chromium.content.browser.test.util.CallbackHelper;
+import org.chromium.content.browser.test.util.TestTouchUtils;
+import org.chromium.ui.gfx.DeviceDisplayInfo;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Integration tests for synchronous scrolling.
+ */
+public class AndroidScrollIntegrationTest extends AwTestBase {
+
+ public static final int SCROLL_OFFSET_PROPAGATION_TIMEOUT_MS = 6 * 1000;
+
+ private static class ScrollTestContainerView extends AwTestContainerView {
+ private int mMaxScrollXPix = -1;
+ private int mMaxScrollYPix = -1;
+
+ private CallbackHelper mOnScrollToCallbackHelper = new CallbackHelper();
+
+ public ScrollTestContainerView(Context context) {
+ super(context);
+ }
+
+ public CallbackHelper getOnScrollToCallbackHelper() {
+ return mOnScrollToCallbackHelper;
+ }
+
+ public void setMaxScrollX(int maxScrollXPix) {
+ mMaxScrollXPix = maxScrollXPix;
+ }
+
+ public void setMaxScrollY(int maxScrollYPix) {
+ mMaxScrollYPix = maxScrollYPix;
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ if (mMaxScrollXPix != -1)
+ x = Math.min(mMaxScrollXPix, x);
+ if (mMaxScrollYPix != -1)
+ y = Math.min(mMaxScrollYPix, y);
+ super.scrollTo(x, y);
+ mOnScrollToCallbackHelper.notifyCalled();
+ }
+ }
+
+ @Override
+ protected TestDependencyFactory createTestDependencyFactory() {
+ return new TestDependencyFactory() {
+ @Override
+ public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
+ return new ScrollTestContainerView(activity);
+ }
+ };
+ }
+
+ private String makeTestPage(String onscrollObserver, String firstFrameObserver) {
+ String headers =
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> " +
+ "<style type=\"text/css\"> " +
+ " div { " +
+ " width:1000px; " +
+ " height:10000px; " +
+ " background-color: blue; " +
+ " } " +
+ "</style> ";
+ String content = "<div>test div</div> ";
+ if (onscrollObserver != null) {
+ content +=
+ "<script> " +
+ " window.onscroll = function(oEvent) { " +
+ " " + onscrollObserver + ".notifyJava(); " +
+ " } " +
+ "</script>";
+ }
+ if (firstFrameObserver != null) {
+ content +=
+ "<script> " +
+ " window.framesToIgnore = 10; " +
+ " window.onAnimationFrame = function(timestamp) { " +
+ " if (window.framesToIgnore == 0) { " +
+ " " + firstFrameObserver + ".notifyJava(); " +
+ " } else {" +
+ " window.framesToIgnore -= 1; " +
+ " window.requestAnimationFrame(window.onAnimationFrame); " +
+ " } " +
+ " }; " +
+ " window.requestAnimationFrame(window.onAnimationFrame); " +
+ "</script>";
+ }
+ return CommonResources.makeHtmlPageFrom(headers, content);
+ }
+
+ private void scrollToOnMainSync(final View view, final int xPix, final int yPix) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ view.scrollTo(xPix, yPix);
+ }
+ });
+ }
+
+ private void setMaxScrollOnMainSync(final ScrollTestContainerView testContainerView,
+ final int maxScrollXPix, final int maxScrollYPix) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ testContainerView.setMaxScrollX(maxScrollXPix);
+ testContainerView.setMaxScrollY(maxScrollYPix);
+ }
+ });
+ }
+
+ private boolean checkScrollOnMainSync(final ScrollTestContainerView testContainerView,
+ final int scrollXPix, final int scrollYPix) {
+ final AtomicBoolean equal = new AtomicBoolean(false);
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ equal.set((scrollXPix == testContainerView.getScrollX()) &&
+ (scrollYPix == testContainerView.getScrollY()));
+ }
+ });
+ return equal.get();
+ }
+
+ private void assertScrollOnMainSync(final ScrollTestContainerView testContainerView,
+ final int scrollXPix, final int scrollYPix) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(scrollXPix, testContainerView.getScrollX());
+ assertEquals(scrollYPix, testContainerView.getScrollY());
+ }
+ });
+ }
+
+ private void assertScrollInJs(AwContents awContents, TestAwContentsClient contentsClient,
+ int xCss, int yCss) throws Exception {
+ String x = executeJavaScriptAndWaitForResult(awContents, contentsClient, "window.scrollX");
+ String y = executeJavaScriptAndWaitForResult(awContents, contentsClient, "window.scrollY");
+ assertEquals(Integer.toString(xCss), x);
+ assertEquals(Integer.toString(yCss), y);
+ }
+
+ private void loadTestPageAndWaitForFirstFrame(final ScrollTestContainerView testContainerView,
+ final TestAwContentsClient contentsClient,
+ final String onscrollObserverName) throws Exception {
+ final JavascriptEventObserver firstFrameObserver = new JavascriptEventObserver();
+ final String firstFrameObserverName = "firstFrameObserver";
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ firstFrameObserver.register(testContainerView.getContentViewCore(),
+ firstFrameObserverName);
+ }
+ });
+
+ // After page load the view's scroll offset will be reset back to (0, 0) at least once. We
+ // set the initial scroll offset to something different so we can observe that.
+ scrollToOnMainSync(testContainerView, 10, 10);
+
+ loadDataSync(testContainerView.getAwContents(), contentsClient.getOnPageFinishedHelper(),
+ makeTestPage(onscrollObserverName, firstFrameObserverName), "text/html", false);
+
+ // We wait for "a couple" of frames for the active tree in CC to stabilize and for pending
+ // tree activations to stop clobbering the root scroll layer's scroll offset. This wait
+ // doesn't strictly guarantee that but there isn't a good alternative and this seems to
+ // work fine.
+ firstFrameObserver.waitForEvent(WAIT_TIMEOUT_SECONDS * 1000);
+ // This is just to double-check that the animation frame waiting code didn't exit too soon.
+ assertScrollOnMainSync(testContainerView, 0, 0);
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView"})
+ public void testUiScrollReflectedInJs() throws Throwable {
+ final TestAwContentsClient contentsClient = new TestAwContentsClient();
+ final ScrollTestContainerView testContainerView =
+ (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient);
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+ final double deviceDIPScale =
+ DeviceDisplayInfo.create(testContainerView.getContext()).getDIPScale();
+ final int targetScrollXCss = 233;
+ final int targetScrollYCss = 322;
+ final int targetScrollXPix = (int) Math.round(targetScrollXCss * deviceDIPScale);
+ final int targetScrollYPix = (int) Math.round(targetScrollYCss * deviceDIPScale);
+ final JavascriptEventObserver onscrollObserver = new JavascriptEventObserver();
+
+ Log.w("AndroidScrollIntegrationTest", String.format("scroll in Js (%d, %d) -> (%d, %d)",
+ targetScrollXCss, targetScrollYCss, targetScrollXPix, targetScrollYPix));
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ onscrollObserver.register(testContainerView.getContentViewCore(),
+ "onscrollObserver");
+ }
+ });
+
+ loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, "onscrollObserver");
+
+ scrollToOnMainSync(testContainerView, targetScrollXPix, targetScrollYPix);
+
+ onscrollObserver.waitForEvent(SCROLL_OFFSET_PROPAGATION_TIMEOUT_MS);
+ assertScrollInJs(testContainerView.getAwContents(), contentsClient,
+ targetScrollXCss, targetScrollYCss);
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView"})
+ public void testJsScrollReflectedInUi() throws Throwable {
+ final TestAwContentsClient contentsClient = new TestAwContentsClient();
+ final ScrollTestContainerView testContainerView =
+ (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient);
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+ final double deviceDIPScale =
+ DeviceDisplayInfo.create(testContainerView.getContext()).getDIPScale();
+ final int targetScrollXCss = 132;
+ final int targetScrollYCss = 243;
+ final int targetScrollXPix = (int) Math.round(targetScrollXCss * deviceDIPScale);
+ final int targetScrollYPix = (int) Math.round(targetScrollYCss * deviceDIPScale);
+
+ loadDataSync(testContainerView.getAwContents(), contentsClient.getOnPageFinishedHelper(),
+ makeTestPage(null, null), "text/html", false);
+
+ final CallbackHelper onScrollToCallbackHelper =
+ testContainerView.getOnScrollToCallbackHelper();
+ final int scrollToCallCount = onScrollToCallbackHelper.getCallCount();
+ executeJavaScriptAndWaitForResult(testContainerView.getAwContents(), contentsClient,
+ String.format("window.scrollTo(%d, %d);", targetScrollXCss, targetScrollYCss));
+ onScrollToCallbackHelper.waitForCallback(scrollToCallCount);
+
+ assertScrollOnMainSync(testContainerView, targetScrollXPix, targetScrollYPix);
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView"})
+ public void testJsScrollCanBeAlteredByUi() throws Throwable {
+ final TestAwContentsClient contentsClient = new TestAwContentsClient();
+ final ScrollTestContainerView testContainerView =
+ (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient);
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+ final double deviceDIPScale =
+ DeviceDisplayInfo.create(testContainerView.getContext()).getDIPScale();
+ final int targetScrollXCss = 132;
+ final int targetScrollYCss = 243;
+ final int targetScrollXPix = (int) Math.round(targetScrollXCss * deviceDIPScale);
+ final int targetScrollYPix = (int) Math.round(targetScrollYCss * deviceDIPScale);
+
+ final int maxScrollXCss = 101;
+ final int maxScrollYCss = 201;
+ final int maxScrollXPix = (int) Math.round(maxScrollXCss * deviceDIPScale);
+ final int maxScrollYPix = (int) Math.round(maxScrollYCss * deviceDIPScale);
+
+ loadDataSync(testContainerView.getAwContents(), contentsClient.getOnPageFinishedHelper(),
+ makeTestPage(null, null), "text/html", false);
+
+ setMaxScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix);
+
+ final CallbackHelper onScrollToCallbackHelper =
+ testContainerView.getOnScrollToCallbackHelper();
+ final int scrollToCallCount = onScrollToCallbackHelper.getCallCount();
+ executeJavaScriptAndWaitForResult(testContainerView.getAwContents(), contentsClient,
+ "window.scrollTo(" + targetScrollXCss + "," + targetScrollYCss + ")");
+ onScrollToCallbackHelper.waitForCallback(scrollToCallCount);
+
+ assertScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix);
+ }
+
+ private void dragViewBy(AwTestContainerView testContainerView, int dxPix, int dyPix,
+ int steps) {
+ int gestureStart[] = new int[2];
+ testContainerView.getLocationOnScreen(gestureStart);
+ int gestureEnd[] = new int[] { gestureStart[0] - dxPix, gestureStart[1] - dyPix };
+
+ TestTouchUtils.drag(this, gestureStart[0], gestureEnd[0], gestureStart[1], gestureEnd[1],
+ steps);
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView"})
+ public void testTouchScrollCanBeAlteredByUi() throws Throwable {
+ final TestAwContentsClient contentsClient = new TestAwContentsClient();
+ final ScrollTestContainerView testContainerView =
+ (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient);
+ enableJavaScriptOnUiThread(testContainerView.getAwContents());
+
+ final int dragSteps = 10;
+ final int dragStepSize = 24;
+ // Watch out when modifying - if the y or x delta aren't big enough vertical or horizontal
+ // scroll snapping will kick in.
+ final int targetScrollXPix = dragStepSize * dragSteps;
+ final int targetScrollYPix = dragStepSize * dragSteps;
+
+ final double deviceDIPScale =
+ DeviceDisplayInfo.create(testContainerView.getContext()).getDIPScale();
+ final int maxScrollXPix = 101;
+ final int maxScrollYPix = 211;
+ // Make sure we can't hit these values simply as a result of scrolling.
+ assert (maxScrollXPix % dragStepSize) != 0;
+ assert (maxScrollYPix % dragStepSize) != 0;
+ final int maxScrollXCss = (int) Math.round(maxScrollXPix / deviceDIPScale);
+ final int maxScrollYCss = (int) Math.round(maxScrollYPix / deviceDIPScale);
+
+ setMaxScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix);
+
+ loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null);
+
+ final CallbackHelper onScrollToCallbackHelper =
+ testContainerView.getOnScrollToCallbackHelper();
+ final int scrollToCallCount = onScrollToCallbackHelper.getCallCount();
+ dragViewBy(testContainerView, targetScrollXPix, targetScrollYPix, dragSteps);
+
+ for (int i = 1; i <= dragSteps; ++i) {
+ onScrollToCallbackHelper.waitForCallback(scrollToCallCount, i);
+ if (checkScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix))
+ break;
+ }
+
+ assertScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix);
+ assertScrollInJs(testContainerView.getAwContents(), contentsClient,
+ maxScrollXCss, maxScrollYCss);
+ }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
index 72e62a6..9ba9419 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
@@ -65,11 +65,14 @@ public class AndroidViewIntegrationTest extends AwTestBase {
}
}
- private class DependencyFactory extends TestDependencyFactory {
- @Override
- public AwLayoutSizer createLayoutSizer() {
- return new TestAwLayoutSizer();
- }
+ @Override
+ protected TestDependencyFactory createTestDependencyFactory() {
+ return new TestDependencyFactory() {
+ @Override
+ public AwLayoutSizer createLayoutSizer() {
+ return new TestAwLayoutSizer();
+ }
+ };
}
final LinearLayout.LayoutParams wrapContentLayoutParams =
@@ -82,8 +85,7 @@ public class AndroidViewIntegrationTest extends AwTestBase {
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- testContainerView.set(createAwTestContainerView(awContentsClient,
- new DependencyFactory()));
+ testContainerView.set(createAwTestContainerView(awContentsClient));
testContainerView.get().setLayoutParams(wrapContentLayoutParams);
testContainerView.get().setVisibility(visibility);
}
@@ -98,8 +100,7 @@ public class AndroidViewIntegrationTest extends AwTestBase {
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- testContainerView.set(createDetachedAwTestContainerView(awContentsClient,
- new DependencyFactory()));
+ testContainerView.set(createDetachedAwTestContainerView(awContentsClient));
}
});
return testContainerView.get();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
index 52957e0..da078d2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
@@ -200,18 +200,18 @@ public class AwTestBase
public AwLayoutSizer createLayoutSizer() {
return new AwLayoutSizer();
}
+ public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity) {
+ return new AwTestContainerView(activity);
+ }
}
- protected AwTestContainerView createAwTestContainerView(
- final AwContentsClient awContentsClient) {
- return createAwTestContainerView(awContentsClient, new TestDependencyFactory());
+ protected TestDependencyFactory createTestDependencyFactory() {
+ return new TestDependencyFactory();
}
protected AwTestContainerView createAwTestContainerView(
- final AwContentsClient awContentsClient,
- final TestDependencyFactory testDependencyFactory) {
- AwTestContainerView testContainerView =
- createDetachedAwTestContainerView(awContentsClient, testDependencyFactory);
+ final AwContentsClient awContentsClient) {
+ AwTestContainerView testContainerView = createDetachedAwTestContainerView(awContentsClient);
getActivity().addView(testContainerView);
testContainerView.requestFocus();
return testContainerView;
@@ -222,9 +222,10 @@ public class AwTestBase
new AwBrowserContext(new InMemorySharedPreferences());
protected AwTestContainerView createDetachedAwTestContainerView(
- final AwContentsClient awContentsClient,
- final TestDependencyFactory testDependencyFactory) {
- final AwTestContainerView testContainerView = new AwTestContainerView(getActivity());
+ final AwContentsClient awContentsClient) {
+ final TestDependencyFactory testDependencyFactory = createTestDependencyFactory();
+ final AwTestContainerView testContainerView =
+ testDependencyFactory.createAwTestContainerView(getActivity());
testContainerView.initialize(new AwContents(
mBrowserContext, testContainerView, testContainerView.getInternalAccessDelegate(),
awContentsClient, false, testDependencyFactory.createLayoutSizer()));
@@ -233,12 +234,6 @@ public class AwTestBase
protected AwTestContainerView createAwTestContainerViewOnMainSync(
final AwContentsClient client) throws Exception {
- return createAwTestContainerViewOnMainSync(client, new TestDependencyFactory());
- }
-
- protected AwTestContainerView createAwTestContainerViewOnMainSync(
- final AwContentsClient client,
- final TestDependencyFactory testDependencyFactory) throws Exception {
final AtomicReference<AwTestContainerView> testContainerView =
new AtomicReference<AwTestContainerView>();
getInstrumentation().runOnMainSync(new Runnable() {
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index 2384c24..89c1cf9 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -646,7 +646,7 @@ bool AwContents::OnDraw(JNIEnv* env,
jint clip_bottom) {
return browser_view_renderer_->OnDraw(canvas,
is_hardware_accelerated,
- gfx::Point(scroll_x, scroll_y),
+ gfx::Vector2d(scroll_x, scroll_y),
gfx::Rect(clip_left,
clip_top,
clip_right - clip_left,
@@ -686,6 +686,24 @@ gfx::Point AwContents::GetLocationOnScreen() {
return gfx::Point(location[0], location[1]);
}
+void AwContents::ScrollContainerViewTo(gfx::Vector2d new_value) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (obj.is_null())
+ return;
+ Java_AwContents_scrollContainerViewTo(
+ env, obj.obj(), new_value.x(), new_value.y());
+}
+
+
+void AwContents::SetDipScale(JNIEnv* env, jobject obj, jfloat dipScale) {
+ browser_view_renderer_->SetDipScale(dipScale);
+}
+
+void AwContents::ScrollTo(JNIEnv* env, jobject obj, jint xPix, jint yPix) {
+ browser_view_renderer_->ScrollTo(gfx::Vector2d(xPix, yPix));
+}
+
void AwContents::OnPageScaleFactorChanged(float page_scale_factor) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h
index e2e72c4..15761b98 100644
--- a/android_webview/native/aw_contents.h
+++ b/android_webview/native/aw_contents.h
@@ -149,11 +149,15 @@ class AwContents : public FindHelper::Listener,
virtual void PostInvalidate() OVERRIDE;
virtual void OnNewPicture() OVERRIDE;
virtual gfx::Point GetLocationOnScreen() OVERRIDE;
+ virtual void ScrollContainerViewTo(gfx::Vector2d new_value) OVERRIDE;
void ClearCache(JNIEnv* env, jobject obj, jboolean include_disk_files);
void SetPendingWebContentsForPopup(scoped_ptr<content::WebContents> pending);
jint ReleasePopupAwContents(JNIEnv* env, jobject obj);
+ void ScrollTo(JNIEnv* env, jobject obj, jint xPix, jint yPix);
+ void SetDipScale(JNIEnv* env, jobject obj, jfloat dipScale);
+
void SetSaveFormData(bool enabled);
private:
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
index 89905d1..f3a3e88 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -56,16 +56,19 @@ public class AwTestContainerView extends FrameLayout {
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
mAwContents.onConfigurationChanged(newConfig);
}
@Override
public void onAttachedToWindow() {
+ super.onAttachedToWindow();
mAwContents.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
mAwContents.onDetachedFromWindow();
}
@@ -97,27 +100,40 @@ public class AwTestContainerView extends FrameLayout {
@Override
public void onSizeChanged(int w, int h, int ow, int oh) {
+ super.onSizeChanged(w, h, ow, oh);
mAwContents.onSizeChanged(w, h, ow, oh);
}
@Override
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ if (mAwContents != null) {
+ mAwContents.onContainerViewScrollChanged(l, t, oldl, oldt);
+ }
+ }
+
+ @Override
public void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
mAwContents.onVisibilityChanged(changedView, visibility);
}
@Override
public void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
mAwContents.onWindowVisibilityChanged(visibility);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
return mAwContents.onTouchEvent(ev);
}
@Override
public void onDraw(Canvas canvas) {
mAwContents.onDraw(canvas);
+ super.onDraw(canvas);
}
// TODO: AwContents could define a generic class that holds an implementation similar to
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentView.java b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
index a562efa..22f8fe8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentView.java
@@ -417,12 +417,6 @@ public class ContentView extends FrameLayout
return super.drawChild(canvas, child, drawingTime);
}
- // Needed by ContentViewCore.InternalAccessDelegate
- @Override
- public void onScrollChanged(int l, int t, int oldl, int oldt) {
- super.onScrollChanged(l, t, oldl, oldt);
- }
-
@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
TraceEvent.begin();
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 58ff66c..eeebf03 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -153,11 +153,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
void super_onConfigurationChanged(Configuration newConfig);
/**
- * @see View#onScrollChanged(int, int, int, int)
- */
- void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
-
- /**
* @see View#awakenScrollBars()
*/
boolean awakenScrollBars();
@@ -2149,14 +2144,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
if (needHidePopupZoomer) mPopupZoomer.hide(true);
- if (scrollChanged) {
- mContainerViewInternals.onScrollChanged(
- (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
- (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
- (int) mRenderCoordinates.getScrollXPix(),
- (int) mRenderCoordinates.getScrollYPix());
- }
-
if (pageScaleChanged) {
// This function should be called back from native as soon
// as the scroll is applied to the backbuffer. We should only
diff --git a/content/renderer/gpu/input_handler_manager_client.h b/content/renderer/gpu/input_handler_manager_client.h
index a6a6db8..a98a359 100644
--- a/content/renderer/gpu/input_handler_manager_client.h
+++ b/content/renderer/gpu/input_handler_manager_client.h
@@ -8,6 +8,7 @@
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "content/common/content_export.h"
+#include "ui/gfx/vector2d_f.h"
namespace ui {
struct LatencyInfo;
@@ -17,10 +18,6 @@ namespace cc {
class InputHandler;
}
-namespace gfx {
-class Vector2dF;
-}
-
namespace WebKit {
class WebInputEvent;
}
diff --git a/ui/gfx/vector2d_f.h b/ui/gfx/vector2d_f.h
index fdd3a9e..4faf28a 100644
--- a/ui/gfx/vector2d_f.h
+++ b/ui/gfx/vector2d_f.h
@@ -70,6 +70,10 @@ inline bool operator==(const Vector2dF& lhs, const Vector2dF& rhs) {
return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}
+inline bool operator!=(const Vector2dF& lhs, const Vector2dF& rhs) {
+ return !(lhs == rhs);
+}
+
inline Vector2dF operator-(const Vector2dF& v) {
return Vector2dF(-v.x(), -v.y());
}