| Index: LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html | 
| diff --git a/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html b/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..9ff34fc2a444fa9972a3ce71f370b8351d358a73 | 
| --- /dev/null | 
| +++ b/LayoutTests/fast/scroll-behavior/scroll-customization/touch-scroll-customization.html | 
| @@ -0,0 +1,252 @@ | 
| +<!DOCTYPE html> | 
| +<html> | 
| +<head> | 
| +<meta charset="utf-8"> | 
| +<title>Scroll customization methods are called appropriately.</title> | 
| +<script src="../../../resources/testharness.js"></script> | 
| +<script src="../../../resources/testharnessreport.js"></script> | 
| +<style> | 
| + | 
| +* { | 
| +  margin:0; | 
| +  padding:0; | 
| +} | 
| + | 
| +*::-webkit-scrollbar { | 
| +  width: 0 !important; | 
| +  height: 0 !important; | 
| +} | 
| + | 
| +#a { | 
| +  height:400px; | 
| +  width:400px; | 
| +  overflow:scroll; | 
| +} | 
| + | 
| +#b { | 
| +  height:500px; | 
| +  width:500px; | 
| +  background-color:purple; | 
| +} | 
| + | 
| +#c { | 
| +  height:300px; | 
| +  width:300px; | 
| +  overflow:scroll; | 
| +} | 
| + | 
| +#d { | 
| +  height:400px; | 
| +  width:400px; | 
| +  background-color:green; | 
| +} | 
| + | 
| +body { | 
| +  height:3000px; | 
| +} | 
| + | 
| +</style> | 
| +</head> | 
| +<body> | 
| + | 
| +<div id="a"> | 
| +<div id="b"> | 
| +<div id="c"> | 
| +<div id="d"> | 
| +</div> | 
| +</div> | 
| +</div> | 
| +</div> | 
| + | 
| +<script> | 
| +test(function() { | 
| +  assert_true('ScrollState' in window, "'ScrollState' in window"); | 
| +}, "These tests only work with scroll customization enabled."); | 
| + | 
| +internals.settings.setScrollAnimatorEnabled(false); | 
| + | 
| +var originalApplyScrolls = []; | 
| +var originalDistributeScrolls = []; | 
| +var deltas = [-85, -75, -65, -55, -45]; | 
| + | 
| +var elements = [ | 
| +  document.getElementById("d"), | 
| +  document.getElementById("c"), | 
| +  document.getElementById("b"), | 
| +  document.getElementById("a"), | 
| +  document.scrollingElement]; | 
| + | 
| +var scrollableElements = [elements[1], elements[3], elements[4]]; | 
| + | 
| +document.scrollingElement.id = "scrollingElement"; | 
| + | 
| +function reset() { | 
| +  for (var i = 0; i < elements.length; ++i) { | 
| +    elements[i].scrollTop = 0; | 
| +    elements[i].unappliedDeltaY = []; | 
| +    elements[i].distributedDeltaY = []; | 
| +    elements[i].numberOfScrollBegins = 0; | 
| +    elements[i].numberOfScrollEnds = 0; | 
| + | 
| +    elements[i].setApplyScroll((function(scrollState){ | 
| +      if (!scrollState.isEnding && !scrollState.isBeginning) | 
| +        this.unappliedDeltaY.push(scrollState.deltaY); | 
| +    }).bind(elements[i]), "perform-after-native-scroll"); | 
| + | 
| +    elements[i].setDistributeScroll((function(scrollState) { | 
| +      if (scrollState.isBeginning) | 
| +        this.numberOfScrollBegins++; | 
| +      else if (scrollState.isEnding) | 
| +        this.numberOfScrollEnds++; | 
| +      else | 
| +        this.distributedDeltaY.push(scrollState.deltaY); | 
| +    }).bind(elements[i]), "perform-before-native-scroll"); | 
| +  } | 
| +} | 
| + | 
| +function applyDelta(d) { | 
| +  eventSender.gestureScrollBegin(10, 10); | 
| +  eventSender.gestureScrollUpdate(0, d); | 
| +  eventSender.gestureScrollEnd(0, 0); | 
| +} | 
| + | 
| +if ('ScrollState' in window) { | 
| +  test(function() { | 
| +    reset(); | 
| + | 
| +    // Scroll five times, with three scrollable elements. | 
| +    var cScrollTop = [85, 100, 100, 100, 100]; | 
| +    var aScrollTop = [0, 0, 65, 100, 100]; | 
| +    var scrollingElementScrollTop = [0, 0, 0, 0, 45]; | 
| + | 
| +    for (var i = 0; i < deltas.length; ++i) { | 
| +      applyDelta(deltas[i]); | 
| +      assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); | 
| +      assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); | 
| +      assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); | 
| +    } | 
| +  }, "Scroll offsets are modified correctly."); | 
| + | 
| +  test(function() { | 
| +    reset(); | 
| + | 
| +    // Scroll five times, with five elements. | 
| +    var unapplied = [ | 
| +      // d, the innermost element, never applies any scroll. | 
| +      [-85, -75, -65, -55, -45], | 
| +      // c applies the first two scrolls, and then hits its scroll extents. | 
| +      [0, 0, -65, -55, -45], | 
| +      // b doesn't scroll, and so leaves the same deltas unapplied as c. | 
| +      [0, 0, -65, -55, -45], | 
| +      // a hits its scroll extent on the second last step. | 
| +      [0, 0, 0, 0, -45], | 
| +      // The scrollingElement performs the frame scroll. | 
| +      [0, 0, 0, 0, 0]]; | 
| + | 
| +    for (var i = 0; i < deltas.length; ++i) | 
| +      applyDelta(deltas[i]); | 
| + | 
| +    for (var i = 0; i < elements.length; ++i) { | 
| +      var el = elements[i]; | 
| +      // Every element sees the same deltas being distributed. | 
| +      assert_array_equals(el.distributedDeltaY, deltas, "distributed delta for " + el.id + ":"); | 
| +      assert_array_equals(el.unappliedDeltaY, unapplied[i], "unapplied delta for " + el.id + ":"); | 
| +    } | 
| + | 
| +    // Ensure that the document leaves scroll unapplied when appropriate. | 
| +    var documentUnapplied = document.scrollingElement.unappliedDeltaY; | 
| +    applyDelta(-4000); | 
| +    assert_equals(documentUnapplied[documentUnapplied.length - 1], 0); | 
| +    applyDelta(-4000); | 
| +    assert_equals(documentUnapplied[documentUnapplied.length - 1], -4000); | 
| +  }, "Correct amount of delta is consumed."); | 
| + | 
| +  test(function() { | 
| +    reset(); | 
| + | 
| +    // Consume one pixel of delta per call to applyScroll. | 
| +    for (var i = 0; i < elements.length; ++i) { | 
| +      if (scrollableElements.indexOf(elements[i]) == -1) | 
| +        continue; | 
| +      elements[i].setApplyScroll((function(scrollState) { | 
| +        if (scrollState.deltaY !== 0) | 
| +          scrollState.consumeDelta(0, -1); | 
| +      }).bind(elements[i]), "perform-before-native-scroll"); | 
| +    } | 
| + | 
| +    // Scroll five times, with three scrollable elements. | 
| +    // The scroll distance is decreased more the higher up the scroll chain the element is. | 
| +    var cScrollTop = [85 - 1, 100, 100, 100, 100]; | 
| +    var aScrollTop = [0, 0, 65 - 2, 100, 100]; | 
| +    var scrollingElementScrollTop = [0, 0, 0, 0, 45 - 3]; | 
| +    for (var i = 0; i < deltas.length; ++i) { | 
| +      applyDelta(deltas[i]); | 
| +      assert_equals(c.scrollTop, cScrollTop[i], "For id 'c' on step " + i); | 
| +      assert_equals(a.scrollTop, aScrollTop[i], "For id 'a' on step " + i); | 
| +      assert_equals(document.scrollingElement.scrollTop, scrollingElementScrollTop[i], "For scrollingElement on step " + i); | 
| +    } | 
| +  }, "Consuming deltas prevents scrolling."); | 
| + | 
| +  test(function() { | 
| +    reset(); | 
| + | 
| +    for (var i = 0; i < deltas.length; ++i) | 
| +      applyDelta(deltas[i]); | 
| + | 
| +    for (var i = 0; i < elements.length; ++i) { | 
| +      assert_equals(elements[i].numberOfScrollBegins, deltas.length, "Incorrect number of begin events for " + elements[i].id); | 
| +      assert_equals(elements[i].numberOfScrollEnds, deltas.length, "Incorrect number of end events for " + elements[i].id); | 
| +    } | 
| +  }, "Correct number of scroll end and begin events observed."); | 
| + | 
| +  { | 
| +    // NOTE - this async test needs to be run last, as it shares state with the | 
| +    // other tests. If other tests are run after it, they'll modify the state | 
| +    // while this test is still running. | 
| +    var flingTest = async_test("Touchscreen fling doesn't propagate."); | 
| +    reset(); | 
| + | 
| +    function assertScrollTops(cTop, aTop, scrollingElementTop, step) { | 
| +      assert_equals(c.scrollTop, cTop, "For id 'c' on step " + step); | 
| +      assert_equals(a.scrollTop, aTop, "For id 'a' on step " + step); | 
| +      assert_equals(document.scrollingElement.scrollTop, scrollingElementTop, "For scrollingElement on step " + step); | 
| +    }; | 
| + | 
| +    var frame_actions = [ | 
| +      function() { | 
| +        eventSender.gestureFlingStart(10, 10, -1000000, -1000000, "touchscreen"); | 
| +      }, | 
| +      flingTest.step_func(function() { | 
| +        assertScrollTops(0, 0, 0, 1); | 
| +      }), | 
| +      flingTest.step_func(function() { | 
| +        assertScrollTops(100, 0, 0, 2); | 
| +      }), | 
| +      flingTest.step_func(function() { | 
| +        assertScrollTops(100, 0, 0, 3); | 
| +      }), | 
| +      flingTest.step_func(function() { | 
| +        assertScrollTops(100, 0, 0, 4); | 
| +        flingTest.done(); | 
| +      }) | 
| +    ] | 
| + | 
| +    function executeFrameActions(frame_actions) { | 
| +      var frame = 0; | 
| +      function raf() { | 
| +        frame_actions[frame](); | 
| +        frame++; | 
| +        if (frame >= frame_actions.length) | 
| +          return; | 
| +        window.requestAnimationFrame(raf); | 
| +      } | 
| +      window.requestAnimationFrame(raf); | 
| +    } | 
| + | 
| +    executeFrameActions(frame_actions); | 
| +  } | 
| +} | 
| + | 
| +</script> | 
| +</body> | 
| +</html> | 
|  |