chromium/third_party/blink/web_tests/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent.html

<!DOCTYPE html>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../resources/gesture-util.js"></script>
<script src="../../../../resources/scrollbar-util.js"></script>
<script src="../../../../resources/testdriver.js"></script>
<script src="../../../../resources/testdriver-actions.js"></script>
<script src="../../../../resources/testdriver-vendor.js"></script>
<style>
body {
  margin: 0;
}

.greenbox {
  width:  100px;
  height: 100px;
  background: green;
}

.redbox {
  width: 100px;
  height: 100px;
  background: red;
}

.bluebox {
  width: 100px;
  height: 100px;
  background: blue;
}

#innerdiv {
  width: 200px;
  height: 200px;
  overflow-y: scroll;
  overflow-x: scroll;
}

#outerdiv {
  width: 250px;
  height: 250px;
  overflow-y: scroll;
  overflow-x: scroll;
}

td {
  padding: 0px;
}

</style>

<body>
<div id="outerdiv">
  <div id="innerdiv">
    <div class="greenbox"></div>
    <div class="redbox"></div>
    <div class="greenbox"></div>
    <div class="redbox"></div>
  </div>
  <div id="bluebox"></div>
</div>
</body>

<script>

promise_test (async () => {
  await waitForCompositorCommit();

  const innerScroller = document.getElementById("innerdiv");
  const outerScroller = document.getElementById("outerdiv");
  const scrollRange = innerScroller.scrollHeight - innerScroller.clientHeight;
  // Apply insets to move the touch point from the boundary to inside the
  // scrollable area.
  const scrollbarThickness = calculateScrollbarThickness();
  const insets = { x: 0, y: scrollbarThickness + 10 };
  let dragStart = elementPosition(innerScroller,
                                  ElementAlignment.CENTER,
                                  ElementAlignment.BOTTOM,
                                  insets);
  const dragDeltaX = 0;
  const dragDeltaY = -(innerScroller.clientHeight - 2 * insets.y);

  // Assert that we are dragging more than halfway to the scroll boundary to
  // ensure that the second scroll will take us to the boundary.
  assert_true(-dragDeltaY > scrollRange / 2,
              'Dragging more than halfway to the boundary');
  assert_true(-dragDeltaY < scrollRange,
              "Not dragging to the scroll boundary");

  outerScroller.addEventListener('scroll', () => {
    assert_unreached('outer div should not scroll');
  });

  // Repeat the drag action to take us to the scroll boundary.
  await touchScroll(dragStart.x, dragStart.y, dragDeltaX, dragDeltaY,
                    innerScroller);

  // Verify that we have scrolled more than half way to the boundary.
   assert_greater_than(innerScroller.scrollTop, scrollRange / 2,
                'Initial scroll gesture scrolls vertically');

  // Wait for a few extra frames to ensure no scroll-chaining.
  await raf();
  await raf();
  assert_equals(outerScroller.scrollTop, 0,
                'No vertical scroll-chaining on the initial scroll');

  // Repeat the last scroll. Since the last scroll exceeded half the scroll
  // range, repeating will take us to the scroll boundary, but since there was
  // room to scroll at the start of the scroll gestures, the remainder should
  // not propagate to the outer scroller.
  await touchScroll(dragStart.x, dragStart.y, dragDeltaX, dragDeltaY,
                    innerScroller);
  assert_equals(innerScroller.scrollTop, scrollRange,
                'Second scroll gesture scrolls vertically to boundary');
  // Wait for a few extra frames to ensure no scroll-chaining.
  await raf();
  await raf();
  assert_equals(outerScroller.scrollTop, 0,
                'No vertical scroll-chaining on the second scroll');
}, 'This tests that a gesture scroll isn\'t propagated from an inner div to ' +
   'an outer div when the inner div has no remaining scroll offset.');
</script>