@SmallTest @Feature({"AndroidWebView"}) public void testNoSpuriousOverScrolls() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); enableJavaScriptOnUiThread(testContainerView.getAwContents()); final int dragSteps = 1; final int targetScrollYPix = 40; setMaxScrollOnMainSync(testContainerView, 0, 0); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); CountDownLatch scrollingCompleteLatch = new CountDownLatch(1); AwTestTouchUtils.dragCompleteView( testContainerView, 0, 0, // these need to be negative as we're scrolling down. 0, -targetScrollYPix, dragSteps, scrollingCompleteLatch); try { scrollingCompleteLatch.await(); } catch (InterruptedException ex) { // ignore } assertEquals(scrollToCallCount + 1, onScrollToCallbackHelper.getCallCount()); }
@SmallTest @Feature({"AndroidWebView"}) public void testOverScrollX() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); final OverScrollByCallbackHelper overScrollByCallbackHelper = testContainerView.getOverScrollByCallbackHelper(); enableJavaScriptOnUiThread(testContainerView.getAwContents()); final int overScrollDeltaX = 30; final int oneStep = 1; loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); // Scroll separately in different dimensions because of vertical/horizontal scroll // snap. final int overScrollCallCount = overScrollByCallbackHelper.getCallCount(); AwTestTouchUtils.dragCompleteView( testContainerView, 0, overScrollDeltaX, 0, 0, oneStep, null /* completionLatch */); overScrollByCallbackHelper.waitForCallback(overScrollCallCount); // Unfortunately the gesture detector seems to 'eat' some number of pixels. For now // checking that the value is < 0 (overscroll is reported as negative values) will have to // do. assertTrue(0 > overScrollByCallbackHelper.getDeltaX()); assertEquals(0, overScrollByCallbackHelper.getDeltaY()); assertScrollOnMainSync(testContainerView, 0, 0); }
@SmallTest @Feature({"AndroidWebView"}) public void testJsScrollFromBody() 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.floor(targetScrollXCss * deviceDIPScale); final int targetScrollYPix = (int) Math.floor(targetScrollYCss * deviceDIPScale); final String scrollFromBodyScript = "<script> " + " window.scrollTo(" + targetScrollXCss + ", " + targetScrollYCss + "); " + "</script> "; final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); loadDataAsync( testContainerView.getAwContents(), makeTestPage(null, null, scrollFromBodyScript), "text/html", false); onScrollToCallbackHelper.waitForCallback(scrollToCallCount); assertScrollOnMainSync(testContainerView, targetScrollXPix, targetScrollYPix); }
@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.ceil(targetScrollXCss * deviceDIPScale); final int targetScrollYPix = (int) Math.ceil(targetScrollYCss * deviceDIPScale); final JavascriptEventObserver onscrollObserver = new JavascriptEventObserver(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { onscrollObserver.register( testContainerView.getContentViewCore(), "onscrollObserver"); } }); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, "onscrollObserver", ""); scrollToOnMainSync(testContainerView, targetScrollXPix, targetScrollYPix); onscrollObserver.waitForEvent(WAIT_TIMEOUT_MS); assertScrollInJs( testContainerView.getAwContents(), contentsClient, targetScrollXCss, targetScrollYCss); }
private void loadTestPageAndWaitForFirstFrame( final ScrollTestContainerView testContainerView, final TestAwContentsClient contentsClient, final String onscrollObserverName, final String extraContent) 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); } }); loadDataSync( testContainerView.getAwContents(), contentsClient.getOnPageFinishedHelper(), makeTestPage(onscrollObserverName, firstFrameObserverName, extraContent), "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_MS); }
@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.floor(maxScrollXPix / deviceDIPScale); final int maxScrollYCss = (int) Math.floor(maxScrollYPix / deviceDIPScale); setMaxScrollOnMainSync(testContainerView, maxScrollXPix, maxScrollYPix); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); AwTestTouchUtils.dragCompleteView( testContainerView, 0, -targetScrollXPix, // these need to be negative as we're scrolling down. 0, -targetScrollYPix, dragSteps, null /* completionLatch */); 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); }
@SmallTest @Feature({"AndroidWebView"}) public void testTouchScrollingConsumesScrollByGesture() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); final TestGestureStateListener testGestureStateListener = new TestGestureStateListener(); 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; loadTestPageAndWaitForFirstFrame( testContainerView, contentsClient, null, "<div>" + " <div style=\"width:10000px; height: 10000px;\"> force scrolling </div>" + "</div>"); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { testContainerView .getContentViewCore() .addGestureStateListener(testGestureStateListener); } }); final CallbackHelper onScrollUpdateGestureConsumedHelper = testGestureStateListener.getOnScrollUpdateGestureConsumedHelper(); final int callCount = onScrollUpdateGestureConsumedHelper.getCallCount(); AwTestTouchUtils.dragCompleteView( testContainerView, 0, -targetScrollXPix, // these need to be negative as we're scrolling down. 0, -targetScrollYPix, dragSteps, null /* completionLatch */); onScrollUpdateGestureConsumedHelper.waitForCallback(callCount); }
@SmallTest @Feature({"AndroidWebView"}) public void testPageDown() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); enableJavaScriptOnUiThread(testContainerView.getAwContents()); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); assertScrollOnMainSync(testContainerView, 0, 0); final int maxScrollYPix = runTestOnUiThreadAndGetResult( new Callable<Integer>() { @Override public Integer call() { return (testContainerView.getAwContents().computeVerticalScrollRange() - testContainerView.getHeight()); } }); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { testContainerView.getAwContents().pageDown(true); } }); // Wait for the animation to hit the bottom of the page. for (int i = 1; ; ++i) { onScrollToCallbackHelper.waitForCallback(scrollToCallCount, i); if (checkScrollOnMainSync(testContainerView, 0, maxScrollYPix)) break; } }
@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.floor(targetScrollXCss * deviceDIPScale); final int targetScrollYPix = (int) Math.floor(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 testPageUp() 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 targetScrollYCss = 243; final int targetScrollYPix = (int) Math.ceil(targetScrollYCss * deviceDIPScale); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); assertScrollOnMainSync(testContainerView, 0, 0); scrollToOnMainSync(testContainerView, 0, targetScrollYPix); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { testContainerView.getAwContents().pageUp(true); } }); // Wait for the animation to hit the bottom of the page. for (int i = 1; ; ++i) { onScrollToCallbackHelper.waitForCallback(scrollToCallCount, i); if (checkScrollOnMainSync(testContainerView, 0, 0)) break; } }
@SmallTest @Feature({"AndroidWebView"}) public void testFlingScroll() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); enableJavaScriptOnUiThread(testContainerView.getAwContents()); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); assertScrollOnMainSync(testContainerView, 0, 0); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { testContainerView.getAwContents().flingScroll(1000, 1000); } }); onScrollToCallbackHelper.waitForCallback(scrollToCallCount); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { assertTrue(testContainerView.getScrollX() > 0); assertTrue(testContainerView.getScrollY() > 0); } }); }
@SmallTest @Feature({"AndroidWebView"}) public void testOverScrollY() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); final OverScrollByCallbackHelper overScrollByCallbackHelper = testContainerView.getOverScrollByCallbackHelper(); enableJavaScriptOnUiThread(testContainerView.getAwContents()); final int overScrollDeltaY = 30; final int oneStep = 1; loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); int overScrollCallCount = overScrollByCallbackHelper.getCallCount(); AwTestTouchUtils.dragCompleteView( testContainerView, 0, 0, 0, overScrollDeltaY, oneStep, null /* completionLatch */); overScrollByCallbackHelper.waitForCallback(overScrollCallCount); assertEquals(0, overScrollByCallbackHelper.getDeltaX()); assertTrue(0 > overScrollByCallbackHelper.getDeltaY()); assertScrollOnMainSync(testContainerView, 0, 0); }
@SmallTest @Feature({"AndroidWebView"}) public void testPinchZoomUpdatesScrollRangeSynchronously() throws Throwable { final TestAwContentsClient contentsClient = new TestAwContentsClient(); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(contentsClient); final OverScrollByCallbackHelper overScrollByCallbackHelper = testContainerView.getOverScrollByCallbackHelper(); final AwContents awContents = testContainerView.getAwContents(); enableJavaScriptOnUiThread(awContents); loadTestPageAndWaitForFirstFrame(testContainerView, contentsClient, null, ""); // Containers to execute asserts on the test thread final AtomicBoolean canZoomIn = new AtomicBoolean(false); final AtomicReference<Float> atomicOldScale = new AtomicReference<Float>(); final AtomicReference<Float> atomicNewScale = new AtomicReference<Float>(); final AtomicInteger atomicOldScrollRange = new AtomicInteger(); final AtomicInteger atomicNewScrollRange = new AtomicInteger(); final AtomicInteger atomicContentHeight = new AtomicInteger(); final AtomicInteger atomicOldContentHeightApproximation = new AtomicInteger(); final AtomicInteger atomicNewContentHeightApproximation = new AtomicInteger(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { canZoomIn.set(awContents.canZoomIn()); int oldScrollRange = awContents.computeVerticalScrollRange() - testContainerView.getHeight(); float oldScale = awContents.getScale(); atomicOldContentHeightApproximation.set( (int) Math.ceil(awContents.computeVerticalScrollRange() / oldScale)); awContents.zoomIn(); int newScrollRange = awContents.computeVerticalScrollRange() - testContainerView.getHeight(); float newScale = awContents.getScale(); atomicNewContentHeightApproximation.set( (int) Math.ceil(awContents.computeVerticalScrollRange() / newScale)); atomicOldScale.set(oldScale); atomicNewScale.set(newScale); atomicOldScrollRange.set(oldScrollRange); atomicNewScrollRange.set(newScrollRange); atomicContentHeight.set(awContents.getContentHeightCss()); } }); assertTrue(canZoomIn.get()); assertTrue( String.format( Locale.ENGLISH, "Scale range should increase after zoom (%f) > (%f)", atomicNewScale.get(), atomicOldScale.get()), atomicNewScale.get() > atomicOldScale.get()); assertTrue( String.format( Locale.ENGLISH, "Scroll range should increase after zoom (%d) > (%d)", atomicNewScrollRange.get(), atomicOldScrollRange.get()), atomicNewScrollRange.get() > atomicOldScrollRange.get()); assertEquals(atomicContentHeight.get(), atomicOldContentHeightApproximation.get()); assertEquals(atomicContentHeight.get(), atomicNewContentHeightApproximation.get()); }
@SmallTest @Feature({"AndroidWebView"}) public void testFlingScrollOnPopup() throws Throwable { final TestAwContentsClient parentContentsClient = new TestAwContentsClient(); final ScrollTestContainerView parentContainerView = (ScrollTestContainerView) createAwTestContainerViewOnMainSync(parentContentsClient); final AwContents parentContents = parentContainerView.getAwContents(); enableJavaScriptOnUiThread(parentContents); final String popupPath = "/popup.html"; final String parentPageHtml = CommonResources.makeHtmlPageFrom( "", "<script>" + "function tryOpenWindow() {" + " var newWindow = window.open('" + popupPath + "');" + "}</script> <h1>Parent</h1>"); final String popupPageHtml = CommonResources.makeHtmlPageFrom( "<title>" + "Popup Window" + "</title>", "This is a popup window"); triggerPopup( parentContents, parentContentsClient, mWebServer, parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()"); final PopupInfo popupInfo = connectPendingPopup(parentContents); assertEquals("Popup Window", getTitleOnUiThread(popupInfo.popupContents)); final ScrollTestContainerView testContainerView = (ScrollTestContainerView) popupInfo.popupContainerView; enableJavaScriptOnUiThread(testContainerView.getAwContents()); loadTestPageAndWaitForFirstFrame(testContainerView, popupInfo.popupContentsClient, null, ""); assertScrollOnMainSync(testContainerView, 0, 0); final CallbackHelper onScrollToCallbackHelper = testContainerView.getOnScrollToCallbackHelper(); final int scrollToCallCount = onScrollToCallbackHelper.getCallCount(); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { testContainerView.getAwContents().flingScroll(1000, 1000); } }); onScrollToCallbackHelper.waitForCallback(scrollToCallCount); getInstrumentation() .runOnMainSync( new Runnable() { @Override public void run() { assertTrue(testContainerView.getScrollX() > 0); assertTrue(testContainerView.getScrollY() > 0); } }); }