@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 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 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);
  }