<!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"); // Add a gc, to ensure that these callbacks don't get collected. gc(); } } 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. [65, 55, 45], // a hits its scroll extent on the second last step. [0, 0, 45], // The scrollingElement performs the frame scroll. [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(); // 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(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); } }, "Scroll propagation behaves correctly."); 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 begin and end 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>