| <!DOCTYPE html> | |
| <title>Testing scroll positions when scrolling an element with smooth behavior</title> | |
| <meta name="timeout" content="long"/> | |
| <link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com"> | |
| <link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior"> | |
| <link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box"> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="support/scroll-behavior.js"></script> | |
| <style> | |
| .scrollable { | |
| overflow: auto; | |
| width: 400px; | |
| height: 200px; | |
| scroll-behavior: smooth; | |
| } | |
| </style> | |
| <div id="log"> | |
| </div> | |
| <div id="overflowNode" class="scrollable"> | |
| <div style="width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);"> | |
| <span style="display: inline-block; width: 500px; height: 250px;"></span><span id="elementToReveal" style="display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;"></span> | |
| </div> | |
| </div> | |
| <script> | |
| // For smooth behavior, evolution of scroll positions over time is not specified by CSSOM View. | |
| // This test relies on the minimal assumption that scroll position functions are monotonic. | |
| ["scroll", "scrollTo", "scrollBy", "scrollIntoView"].forEach(function(scrollFunction) { | |
| [{left:0, top:0}, {left:1000, top:0}, {left:0, top:500}, {left:1000, top:500}].forEach((initial) => { | |
| var finalLeft = 500; | |
| var finalTop = 250; | |
| promise_test(() => { | |
| return new Promise(function(resolve, reject) { | |
| scrollNode(overflowNode, "scroll", "instant", initial.left, initial.top); | |
| var oldLeft = overflowNode.scrollLeft; | |
| var oldTop = overflowNode.scrollTop; | |
| assert_equals(oldLeft, initial.left, "ScrollLeft should be at initial position"); | |
| assert_equals(oldTop, initial.top, "ScrollTop should be at initial position"); | |
| if (scrollFunction === "scrollBy") | |
| scrollNode(overflowNode, scrollFunction, "smooth", finalLeft - initial.left, finalTop - initial.top); | |
| else | |
| scrollNode(overflowNode, scrollFunction, "smooth", finalLeft, finalTop); | |
| observeScrolling(overflowNode, function(done) { | |
| try { | |
| var newLeft = overflowNode.scrollLeft; | |
| var newTop = overflowNode.scrollTop; | |
| assert_less_than_equal(Math.hypot(finalLeft - newLeft, finalTop - newTop), Math.hypot(finalLeft - oldLeft, finalTop - oldTop), "Scroll position should move towards the final position"); | |
| if (done) { | |
| assert_equals(newLeft, finalLeft, "ScrollLeft should reach final position"); | |
| assert_equals(newTop, finalTop, "ScrollTop should reach final position"); | |
| } | |
| oldLeft = newLeft; | |
| oldTop = newTop; | |
| } catch(e) { | |
| reject(e); | |
| } | |
| if (done) | |
| resolve(); | |
| }); | |
| }); | |
| }, `Scroll positions when performing smooth scrolling from (${initial.left}, ${initial.top}) to (${finalLeft}, ${finalTop}) using ${scrollFunction}() `); | |
| }); | |
| }); | |
| [{scrollAttribute: "scrollLeft", scrollValue: 500}, {scrollAttribute: "scrollTop", scrollValue: 250}].forEach(function(scrollTest) { | |
| var initialPosition = Number(scrollTest.scrollValue) * 2; | |
| [0, initialPosition].forEach((initial) => { | |
| promise_test(() => { | |
| return new Promise(function(resolve, reject) { | |
| scrollNode(overflowNode, "scroll", "instant", initial, initial); | |
| var oldValue = overflowNode[scrollTest.scrollAttribute]; | |
| assert_equals(oldValue, initial, `${scrollTest.scrollAttribute} should be at initial position`); | |
| var expectedValue = Number(scrollTest.scrollValue); | |
| overflowNode[scrollTest.scrollAttribute] = expectedValue; | |
| observeScrolling(overflowNode, function(done) { | |
| try { | |
| var newValue = overflowNode[scrollTest.scrollAttribute]; | |
| assert_less_than_equal(Math.abs(expectedValue - newValue), Math.abs(expectedValue - oldValue), "Scroll position should move towards the final position"); | |
| if (done) | |
| assert_equals(newValue, expectedValue, `${scrollTest.scrollAttribute} should reach final position`); | |
| oldValue = newValue; | |
| } catch(e) { | |
| reject(e); | |
| } | |
| if (done) | |
| resolve(); | |
| }); | |
| }); | |
| }, `Scroll positions when performing smooth scrolling from ${initial} to ${scrollTest.scrollValue} by setting ${scrollTest.scrollAttribute} `); | |
| }); | |
| }); | |
| promise_test(() => { | |
| return new Promise(function(resolve, reject) { | |
| resetScroll(overflowNode); | |
| var initialScrollAborted = false; | |
| var scrollDirectionChanged = false; | |
| var oldLeft = overflowNode.scrollLeft; | |
| var oldTop = overflowNode.scrollTop; | |
| assert_equals(oldLeft, 0); | |
| assert_equals(oldTop, 0); | |
| scrollNode(overflowNode, "scroll", "smooth", 1500, 750); | |
| observeScrolling(overflowNode, function(done) { | |
| try { | |
| var newLeft = overflowNode.scrollLeft; | |
| var newTop = overflowNode.scrollTop; | |
| if (initialScrollAborted) { | |
| if (scrollDirectionChanged) { | |
| assert_greater_than_equal(oldLeft, newLeft, "ScrollLeft keeps decreasing"); | |
| assert_greater_than_equal(oldTop, newTop, "ScrollTop keeps decreasing"); | |
| } else | |
| scrollDirectionChanged = newLeft <= oldLeft && newTop <= oldTop; | |
| } else { | |
| assert_less_than_equal(oldLeft, newLeft, "ScrollLeft keeps increasing"); | |
| assert_less_than_equal(oldTop, newTop, "ScrollTop keeps increasing"); | |
| if (newLeft > 1000 && newTop > 500) { | |
| // Abort the initial scroll. | |
| initialScrollAborted = true; | |
| scrollNode(overflowNode, "scroll", "smooth", 500, 250); | |
| newLeft = overflowNode.scrollLeft; | |
| newTop = overflowNode.scrollTop; | |
| } | |
| } | |
| if (done) { | |
| assert_equals(newLeft, 500, "ScrollLeft should reach final position"); | |
| assert_equals(newTop, 250, "ScrollTop should reach final position"); | |
| } | |
| oldLeft = newLeft; | |
| oldTop = newTop; | |
| } catch(e) { | |
| reject(e); | |
| } | |
| if (done) | |
| resolve(); | |
| }); | |
| }); | |
| }, "Scroll positions when aborting a smooth scrolling with another smooth scrolling"); | |
| promise_test(() => { | |
| return new Promise(function(resolve, reject) { | |
| resetScroll(overflowNode); | |
| var initialScrollAborted = false; | |
| var oldLeft = overflowNode.scrollLeft; | |
| var oldTop = overflowNode.scrollTop; | |
| assert_equals(oldLeft, 0); | |
| assert_equals(oldTop, 0); | |
| scrollNode(overflowNode, "scroll", "smooth", 1500, 750); | |
| observeScrolling(overflowNode, function(done) { | |
| try { | |
| var newLeft = overflowNode.scrollLeft; | |
| var newTop = overflowNode.scrollTop; | |
| if (!initialScrollAborted) { | |
| assert_less_than_equal(oldLeft, newLeft, "ScrollLeft keeps increasing"); | |
| assert_less_than_equal(oldTop, newTop, "ScrollTop keeps increasing"); | |
| if (newLeft > 1000 && newTop > 500) { | |
| // Abort the initial scroll. | |
| initialScrollAborted = true; | |
| scrollNode(overflowNode, "scroll", "instant", 500, 250); | |
| newLeft = overflowNode.scrollLeft; | |
| newTop = overflowNode.scrollTop; | |
| assert_equals(newLeft, 500, "ScrollLeft should reach final position"); | |
| assert_equals(newTop, 250, "ScrollTop should reach final position"); | |
| } | |
| } | |
| if (done) { | |
| assert_equals(newLeft, 500, "ScrollLeft should stay at final position"); | |
| assert_equals(newTop, 250, "ScrollTop should stay at final position"); | |
| } | |
| oldLeft = newLeft; | |
| oldTop = newTop; | |
| } catch(e) { | |
| reject(e); | |
| } | |
| if (done) | |
| resolve(); | |
| }); | |
| }); | |
| }, "Scroll positions when aborting a smooth scrolling with an instant scrolling"); | |
| </script> |