diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/css/css-scroll-snap | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-scroll-snap')
125 files changed, 8458 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-scroll-snap/META.yml b/testing/web-platform/tests/css/css-scroll-snap/META.yml new file mode 100644 index 0000000000..df776353a3 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/META.yml @@ -0,0 +1,3 @@ +spec: https://drafts.csswg.org/css-scroll-snap/ +suggested_reviewers: + - tabatkins diff --git a/testing/web-platform/tests/css/css-scroll-snap/capturing-snap-positions.html b/testing/web-platform/tests/css/css-scroll-snap/capturing-snap-positions.html new file mode 100644 index 0000000000..7bc96b9d33 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/capturing-snap-positions.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset=utf-8> +<title>scroll-snap-type on non-scrollers traps snap positions</title> +<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#captures-snap-positions"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + +.scroller { + width: 100px; + height: 100px; + overflow: auto; + scroll-snap-type: block mandatory; +} +.item { + background: gray; + height: 100px; + scroll-snap-type: block mandatory; +} +.item.snapped { + background: green; + scroll-snap-align: center; +} +.subitem { + background: red; + height: 100px; + scroll-snap-align: center; +} + +</style> + + +<!-- +Boxes with a non-none value for scroll-snap-type +will capture snap positions from their descendents, +preventing them from affecting higher-up scrollers, +even if they are not, themselves, scrollers. +--> + +<div class=scroller> + <div class=item></div> + <div class=item><div class=subitem></div></div> + <div class="item snapped"></div> + <div class=item></div> +</div> + +<script> + +test(()=>{ + const el = document.querySelector('.scroller'); + assert_equals(el.scrollTop, 200); +}, "The third item should be snapped to by default, not the second's child."); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/crashtests/fractional-covering-area-crash.html b/testing/web-platform/tests/css/css-scroll-snap/crashtests/fractional-covering-area-crash.html new file mode 100644 index 0000000000..6905b0fd39 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/crashtests/fractional-covering-area-crash.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<style> + #container { + scroll-snap-type: both mandatory; + overflow: overlay; + position: absolute; + height: 33554400px; + width: 33554400px; + } + + #area { + scroll-snap-align: end; + position: absolute; + left: 33554400px; + top: -57971.9px; + width: 0px; + height: 33554400px; + } +</style> +<div id="container"> + <div id="area"></div> +</div> + +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/ignore-snap-points-orthogonal-to-snap-axis.html b/testing/web-platform/tests/css/css-scroll-snap/ignore-snap-points-orthogonal-to-snap-axis.html new file mode 100644 index 0000000000..b0cde7fed1 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/ignore-snap-points-orthogonal-to-snap-axis.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<title>Ignore snap points orthogonal to scroll snap axis</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#snap-axis" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: x mandatory; +} + +#y-target { + width: 300px; + height: 300px; + top: 100px; + left: 0; + background-color: green; + /* align only on y-axis */ + scroll-snap-align: start none; +} + +#x-target { + width: 300px; + height: 300px; + top: 0; + left: 100px; + background-color: red; + scroll-snap-align: none start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="x-target"></div> + <div id="y-target"></div> +</div> + +<script> +test(t => { + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 100); +}, "Ignore snap points orthogonal to scroll snap axis"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/inheritance.html b/testing/web-platform/tests/css/css-scroll-snap/inheritance.html new file mode 100644 index 0000000000..2569cf3d8b --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/inheritance.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Inheritance of CSS Scroll Snap properties</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#property-index"> +<meta name="assert" content="Properties inherit or not according to the spec."> +<meta name="assert" content="Properties have initial values according to the spec."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +</head> +<body> +<div id="container"> +<div id="target"></div> +</div> +<script> +assert_not_inherited('scroll-margin-block-end', '0px', '10px'); +assert_not_inherited('scroll-margin-block-start', '0px', '10px'); +assert_not_inherited('scroll-margin-bottom', '0px', '10px'); +assert_not_inherited('scroll-margin-inline-end', '0px', '10px'); +assert_not_inherited('scroll-margin-inline-start', '0px', '10px'); +assert_not_inherited('scroll-margin-left', '0px', '10px'); +assert_not_inherited('scroll-margin-right', '0px', '10px'); +assert_not_inherited('scroll-margin-top', '0px', '10px'); +assert_not_inherited('scroll-padding-block-end', 'auto', '10px'); +assert_not_inherited('scroll-padding-block-start', 'auto', '10px'); +assert_not_inherited('scroll-padding-bottom', 'auto', '10px'); +assert_not_inherited('scroll-padding-inline-end', 'auto', '10px'); +assert_not_inherited('scroll-padding-inline-start', 'auto', '10px'); +assert_not_inherited('scroll-padding-left', 'auto', '10px'); +assert_not_inherited('scroll-padding-right', 'auto', '10px'); +assert_not_inherited('scroll-padding-top', 'auto', '10px'); +assert_not_inherited('scroll-snap-align', 'none', 'start end'); +assert_not_inherited('scroll-snap-stop', 'normal', 'always'); +assert_not_inherited('scroll-snap-type', 'none', 'inline'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/input/keyboard.html b/testing/web-platform/tests/css/css-scroll-snap/input/keyboard.html new file mode 100644 index 0000000000..79d0fc9f36 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/input/keyboard.html @@ -0,0 +1,182 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<title>Arrow key scroll snapping</title> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<meta name="flags" content="should"> +<meta name="assert" + content="Test passes if keyboard scrolling correctly snaps on a snap + container"> + +<link rel="stylesheet" href="../support/common.css"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/dom/events/scrolling/scroll_support.js"></script> +<script src="../support/common.js"></script> + + +<div id="scroller" tabindex="0"> + <div id="space"></div> + <div class="snap left top" id="top-left"></div> + <div class="snap right top" id="top-right"></div> + <div class="snap left bottom" id="bottom-left"></div> +</div> + +<script> +const scroller = document.getElementById("scroller"); +const topLeft = document.getElementById("top-left"); +const topRight = document.getElementById("top-right"); + +scrollLeft = () => scroller.scrollLeft; +scrollTop = () => scroller.scrollTop; + +function ScrollCounter(test, eventTarget) { + this.count = 0; + const scrollListener = () => { + this.count++; + } + eventTarget.addEventListener('scroll', scrollListener); + test.add_cleanup(() => { + eventTarget.removeEventListener('scroll', scrollListener); + }); +} + +async function initializeScrollPosition(scroller, x, y) { + return new Promise(async (resolve) => { + if (scroller.scrollLeft != x || scroller.scrollTop != y) { + const scrollEndPromise = waitForScrollEnd(scroller); + scroller.scrollTo(x, y); + await scrollEndPromise; + } + resolve(); + }); +} + +promise_test(async t => { + await initializeScrollPosition(scroller, 0, 0); + assert_equals(scroller.scrollTop, 0, "verify test pre-condition"); + const scrollCounter = new ScrollCounter(t, scroller); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowDown"); + await scrollEndPromise; + assert_equals(scroller.scrollTop, 400); + // Make sure we don't jump directly to the new snap position. + assert_greater_than(scrollCounter.count, 1); +}, "Snaps to bottom-left after pressing ArrowDown"); + +promise_test(async t => { + await initializeScrollPosition(scroller, 0, 400); + assert_equals(scroller.scrollTop, 400, "verify test pre-condition"); + const scrollCounter = new ScrollCounter(t, scroller); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowUp"); + await scrollEndPromise; + assert_equals(scroller.scrollTop, 0); + // Make sure we don't jump directly to the new snap position. + assert_greater_than(scrollCounter.count, 1); +}, "Snaps to top-left after pressing ArrowUp"); + +promise_test(async t => { + await initializeScrollPosition(scroller, 0, 0); + assert_equals(scroller.scrollTop, 0, "verify test pre-condition"); + const scrollCounter = new ScrollCounter(t, scroller); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowRight"); + await scrollEndPromise; + assert_equals(scroller.scrollLeft, 400); + // Make sure we don't jump directly to the new snap position. + assert_greater_than(scrollCounter.count, 1); +}, "Snaps to top-right after pressing ArrowRight"); + +promise_test(async t => { + await initializeScrollPosition(scroller, 400, 0); + assert_equals(scroller.scrollLeft, 400, "verify test pre-condition"); + const scrollCounter = new ScrollCounter(t, scroller); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowLeft"); + await scrollEndPromise; + assert_equals(scroller.scrollLeft, 0); + // Make sure we don't jump directly to the new snap position. + assert_greater_than(scrollCounter.count, 1); +}, "Snaps to top-left after pressing ArrowLeft"); + +promise_test(async t => { + t.add_cleanup(function() { + topLeft.style.width = ""; + topRight.style.left = "400px"; + }); + + // Make the snap area cover the snapport. + topLeft.style.width = "800px"; + // Make the distance between the previous and the next snap position larger + // than snapport. + topRight.style.left = "500px"; + await initializeScrollPosition(scroller, 0, 0); + assert_equals(scroller.scrollLeft, 0, "verify test pre-condition"); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowRight"); + await scrollEndPromise; + assert_between_exclusive(scroller.scrollLeft, 0, 500); +}, "If the original intended offset is valid as making a snap area cover the" ++ "snapport, and there's no other snap offset in between, use the original" ++ "intended offset"); + +promise_test(async t => { + t.add_cleanup(function() { + topLeft.style.width = ""; + topRight.style.left = "400px"; + topRight.style.scrollSnapStop = ""; + }); + + // Make the snap area cover the snapport. + topLeft.style.width = "800px"; + // Make the next snap offset closer than the original intended offset. + topRight.style.left = "20px"; + topRight.style.scrollSnapStop = "always"; + await initializeScrollPosition(scroller, 0, 0); + assert_equals(scroller.scrollLeft, 0, "verify test pre-condition"); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowRight"); + await scrollEndPromise; + assert_equals(scroller.scrollLeft, 20); +}, "If the original intended offset is valid as making a snap area cover the " ++ "snapport, but there's a defined snap offset in between, use the defined snap" ++ " offset."); + +promise_test(async t => { + await initializeScrollPosition(scroller, 400, 0); + await keyPress(scroller, "ArrowRight"); + await waitForScrollStop(scroller); + assert_equals(scroller.scrollLeft, 400); +}, "If there is no valid snap offset on the arrow key's direction other than " ++ "the current offset, and the scroll-snap-type is mandatory, stay at the " ++ "current offset."); + +promise_test(async t => { + t.add_cleanup(function() { + scroller.style.scrollSnapType = "both mandatory"; + scroller.style.width = ""; + topLeft.style.width = ""; + }); + + scroller.style.scrollSnapType = "both proximity"; + + // This test case works only if the scroll amount of pressing "ArrowRight" is + // greater than the scroll snap proximity value of the scroll container. + // "100px" width of the scroll container works on major browsers. + scroller.style.width = "100px"; + topLeft.style.width = "80px"; + + await initializeScrollPosition(scroller, 400, 0); + assert_equals(scroller.scrollLeft, 400, "verify test pre-condition"); + const scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowRight"); + await scrollEndPromise; + assert_greater_than(scroller.scrollLeft, 400); +}, "If there is no valid snap offset on the arrow key's direction other than " ++ "the current offset, and the scroll-snap-type is proximity, go to the " ++ "original intended offset"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/input/mouse-wheel.html b/testing/web-platform/tests/css/css-scroll-snap/input/mouse-wheel.html new file mode 100644 index 0000000000..287e594cab --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/input/mouse-wheel.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<title>Mouse-wheel scroll snapping speed</title> +<meta name="assert" + content="Test passes if mousewheel snaps without pausing"> +<style> + #scroller { + scroll-snap-type: block mandatory; + overflow: scroll; + height: 400px; + width: 400px + } + #space { + width: 200px; + height: 4000px; + } + .box { + scroll-snap-align: start; + background: blue; + margin-bottom: 10px; + width: 100px; + height: 100px; + } +</style> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="../support/common.js"></script> +<div id="scroller"> + <div class="box"></div> + <div class="box"></div> + <div id="space"></div> +</div> +<script> +promise_test(async t => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollTop, 0, "verify test pre-condition"); + const scrollTop = () => { + return scroller.scrollTop; + }; + const scrollPromise = waitForScrollEvent(scroller); + const wheelPromise = waitForWheelEvent(scroller); + const actions = new test_driver.Actions() + .scroll(50, 50, 0, 50, {origin: scroller, duration: 100}); + await actions.send(); + await wheelPromise; + await scrollPromise; + let scrollEndTime; + let snapEndTime; + // Detect first pause in scrolling. + const scrollStabilizedPromise = + waitForAnimationEnd(scrollTop).then((timestamp) => { + scrollEndTime = timestamp; + }); + const snapEndPromise = + waitForScrollTo(scroller, () => { + return scroller.scrollTop; + }, 110).then((evt) => { + snapEndTime = evt.timestamp; + }); + await Promise.all([scrollStabilizedPromise, snapEndPromise]); + assert_equals(scroller.scrollTop, 110, + 'Failed to advance to next snap target'); + assert_true(snapEndTime < scrollEndTime, + 'Detected pause in scrolling before reaching snap target'); +}, "Wheel-scroll triggers snap to target position immediately."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/input/snap-area-overflow-boundary-viewport-covering.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/input/snap-area-overflow-boundary-viewport-covering.tentative.html new file mode 100644 index 0000000000..0978f127fa --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/input/snap-area-overflow-boundary-viewport-covering.tentative.html @@ -0,0 +1,158 @@ +<!DOCTYPE html> +<link rel="help" href="https://www.w3.org/TR/css-scroll-snap-1/#snap-overflow" /> +<title></title> +<meta name="assert" content="Test passes if snap is to the nearest edge"> +<style> + body { + margin: 0px; + } + #scroller { + scroll-snap-type: block mandatory; + overflow-y: scroll; + height: 400px; + width: 400px + } + #space { + width: 200px; + height: 4000px; + } + .box { + scroll-snap-align: start; + background: #ccccff; + margin-bottom: 10px; + width: 300px; + height: 500px; + position: relative; + } + .header { + top: 0; + position: absolute; + } + .footer { + bottom: 0; + position: absolute; + } +</style> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/dom/events/scrolling/scroll_support.js"></script> +<script src="../support/common.js"></script> + +<div id="scroller" tabindex="0"> + <div id="target" class="box"> + <div class="header">Header 1</div> + <div class="footer">Footer 1</div> + </div> + <div id="next" class="box"> + <div class="header">Header 2</div> + <div class="footer">Footer 2</div> + </div> + <div id="space"></div> +</div> + +<script> +// If all of the following conditions are met: +// 1. the snap area is larger than the snapport along the scroll axis, and +// 2. the distance between the previous and subsequent snap positions along +// the axis is greater then the snapport size. +// +// Then any scroll position in which the snap area covers the snapport is +// valid snap position. This rule facilitates scrolling around in oversized +// elements. +// +// These test covers edge cases with snap-areas that overflow the snapport. +// It should be possible to scroll to the end of an oversized snap-area. + +const scroller = document.getElementById("scroller"); +const target = document.getElementById("target"); +const next = document.getElementById("next"); +const scrollTop = () => { + return scroller.scrollTop; +}; +const cleanup = () => { + target.style.height = '500px'; +}; + +promise_test(async t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollTop, 0, "verify test pre-condition"); + + // Ensure we can freely scroll in an oversized element. + let scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowDown"); + await scrollEndPromise; + assert_greater_than(scroller.scrollTop, 0, + 'Arrowkey scroll moved scroll position'); + assert_less_than_equal(scroller.scrollTop, target.clientHeight, + 'Scroll within snap-area overflow'); + + // Resize the element so it is oversized by less than the line scroll amount. + // The next keyboard-triggered scroll should stop at the end of the snap-area. + // Otherwise it is not possible to scroll to the last line of the snap-area + // via keyboard. + const scrollAmount = scroller.scrollTop; + target.style.height = `${scroller.clientHeight + 2 * scrollAmount - 1}px`; + assert_equals(scroller.scrollTop, scrollAmount, "Verify container remains " + + "at the same covering snap offset."); + scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowDown"); + await scrollEndPromise; + assert_equals(scroller.scrollTop, + target.clientHeight - scroller.clientHeight, + 'End boundary of snap-area is valid snap target'); + + // Must not get stuck at a snap position. Since already at the end of the + // snap area, we should advance to the next. + scrollEndPromise = waitForScrollEnd(scroller); + await keyPress(scroller, "ArrowDown"); + await scrollEndPromise; + assert_equals(scroller.scrollTop, + next.offsetTop, + 'Advance to next snap-area'); + +}, "Keyboard scrolling with vertical snap-area overflow"); + +promise_test(async t => { + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollTop, 0, "verify test pre-condition"); + + // Ensure we can freely scroll in an oversized element. + let scrollEndPromise = waitForScrollEnd(scroller); + await new test_driver.Actions() + .scroll(50, 50, 0, 50, {origin: scroller}) + .send(); + await scrollEndPromise; + assert_equals(scroller.scrollTop, 50, + 'Wheel-scroll moved scroll position'); + + // Target position for wheel scroll overshoots the boundary of the snap-area. + // Ensure that we stop at the boundary. + let scrollAmount = + target.clientHeight - scroller.clientHeight - scroller.scrollTop + 1; + + scrollEndPromise = waitForScrollEnd(scroller); + await new test_driver.Actions() + .scroll(50, 50, 0, scrollAmount, {origin: scroller}) + .send(); + await scrollEndPromise; + assert_equals(scroller.scrollTop, 100, + 'End boundary of snap-area is valid snap target'); + + // Must not get stuck at a snap position. Since already at the end of the + // snap area, we should advance to the next. scrollAmount must be enough to + // advance to next snap position. + scrollAmount = next.clientHeight / 2 + 10 /* margin-bottom */; + scrollEndPromise = waitForScrollEnd(scroller); + await new test_driver.Actions() + .scroll(50, 50, 0, scrollAmount, {origin: scroller}) + .send(); + await scrollEndPromise; + assert_equals(scroller.scrollTop, next.offsetTop, + 'Advance to next snap-area'); + +}, "Mouse-wheel scrolling with vertical snap-area overflow"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/nested-scrollIntoView-snaps.html b/testing/web-platform/tests/css/css-scroll-snap/nested-scrollIntoView-snaps.html new file mode 100644 index 0000000000..b7a2d6551d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/nested-scrollIntoView-snaps.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<meta name="viewport" content="user-scalable=no"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +:root { + overflow: scroll; + scroll-snap-type: both mandatory; +} +.scroller { + overflow: scroll; + scroll-snap-type: both mandatory; + padding: 0px; +} +#outer { + left: 1000px; + top: 1000px; + width: 600px; + height: 600px; +} +#out-snap-1 { + scroll-snap-align: start; + left: 1200px; + top: 1200px; + width: 10px; + height: 10px; +} +#out-snap-2 { + scroll-snap-align: start; + left: 1100px; + top: 1100px; + width: 10px; + height: 10px; +} +#inner { + left: 1000px; + top: 1000px; + width: 400px; + height: 400px; +} +.space { + left: 0px; + top: 0px; + width: 3000px; + height: 3000px; +} +#target { + scroll-snap-align: end; + left: 800px; + top: 800px; + width: 200px; + height: 200px; +} +</style> + +<div class="space"></div> +<div id="out-snap-1"></div> +<div id="out-snap-2"></div> +<div class="scroller" id="outer"> + <div class="space"></div> + <div class="scroller" id="inner"> + <div class="space"></div> + <div id="target"></div> + </div> +</div> + +<script> +var outer = document.getElementById("outer"); +var inner = document.getElementById("inner"); +var target = document.getElementById("target"); + +test(() => { + // Initial layout triggers a scroll snap. Reset position before calling + // scrollIntoView. + window.scrollTo(0, 0); + outer.scrollTo(0, 0); + inner.scrollTo(0, 0); + + target.scrollIntoView({inline: "start", block: "start"}); + // Although the scrollIntoView specified "start" as the alignment, the target + // has "end" as its snap-alignment. So the inner scroller finishes with "end" + // alignment + assert_equals(inner.scrollLeft, 1000 - inner.clientWidth, "ScrollIntoView lands on the target's snap position regardless of the specified alignment."); + assert_equals(inner.scrollTop, 1000 - inner.clientHeight, "ScrollIntoView lands on the target's snap position regardless of the specified alignment."); + + // Since there is no snap points defined in the outer scroller, the outer + // scroller finishes with "start" alignment, as specified in scrollIntoView. + // Note that the "start" alignment aligns the target's top-left corner + //(inner.left + inner.clientWidth - target.width, inner.top + inner.clientHeight - target.height) + // with the outer scroller's top-left corner. + assert_equals(outer.scrollLeft, 800 + inner.clientWidth, "ScrollIntoView ends with the specified alignment if no snap position is specified."); + assert_equals(outer.scrollTop, 800 + inner.clientHeight, "ScrollIntoView ends with the specified alignment if no snap position is specified."); + + // Although the scrollIntoView specified "start" as the alignment, the window + // has two other elements with snap points. So the window finishes with the + // closest snap point. + assert_equals(window.scrollX, 1100, "ScrollIntoView lands on the snap position closest to the specified alignment."); + assert_equals(window.scrollY, 1100, "ScrollIntoView lands on the snap position closest to the specified alignment."); +}, "All the scrollers affected by scrollIntoView should land on a snap position if one exists. Otherwise, land according to the specified alignment"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/no-red-ref.html b/testing/web-platform/tests/css/css-scroll-snap/no-red-ref.html new file mode 100644 index 0000000000..061454fb67 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/no-red-ref.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<html> +<title>CSS Reference: No Red</title> + +<p>Test passes if there is no red. diff --git a/testing/web-platform/tests/css/css-scroll-snap/no-snap-position.html b/testing/web-platform/tests/css/css-scroll-snap/no-snap-position.html new file mode 100644 index 0000000000..8f1f44af59 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/no-snap-position.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#valdef-scroll-snap-type-mandatory" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./support/common.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + height: 500px; + width: 500px; + overflow: hidden; +} +.child { + width: 300px; + height: 300px; + background-color: blue; +} +</style> + +<div id="scroller"> + <div class="child" style="top: 0px; left: 0px;"></div> + <div class="child" style="top: 1000px; left: 1000px;"></div> + <div style="width: 2000px; height: 2000px;"></div> +</div> + +<script> +test(() => { + scroller.style.scrollSnapType = "both mandatory"; + + // Scroll to where the first child is in view. + scroller.scrollTo(100, 100); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 100); + + // Scroll to where the second child is in view. + scroller.scrollTo(900, 900); + assert_equals(scroller.scrollLeft, 900); + assert_equals(scroller.scrollTop, 900); +}, "No snapping occurs if there is no valid snap position"); + +test(() => { + scroller.style.scrollSnapType = "x mandatory"; + + for (const target of document.querySelectorAll(".child")) { + target.scrollSnapAlign = "start none"; + } + + // Scroll to where the first child is in view. + scroller.scrollTo(100, 100); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 100); + + // Scroll to where the second child is in view. + scroller.scrollTo(900, 900); + assert_equals(scroller.scrollLeft, 900); + assert_equals(scroller.scrollTop, 900); +}, "No snapping occurs if there is no valid snap position matches scroll-snap-type"); + +promise_test(async t => { + // Start with valid snap positions. + scroller.style.scrollSnapType = "y mandatory"; + document.querySelectorAll('.child').forEach(el => { + el.style.scrollSnapAlign = 'start'; + t.add_cleanup(() => { + el.style.scrollSnapAlign = ''; + }); + }); + scroller.scrollTo(100, 100); + await waitForNextFrame(); + const scrollPosition = scroller.scrollTop; + // Elements no longer snap along the y-axis. + document.querySelectorAll('.child').forEach(el => { + el.style.scrollSnapAlign = 'none start'; + // Bump the position to verify that we don't stay pinned to the same element + // after layout update. + el.style.top = `${parseInt(el.style.top) + 100}px`; + }); + await waitForNextFrame(); + assert_equals(scroller.scrollTop, scrollPosition); + scroller.scrollTo(900, 900); + assert_equals(scroller.scrollLeft, 900); + assert_equals(scroller.scrollTop, 900); + +}, "No snapping occurs when last remaining valid snap point is no longer " + + "valid."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas-nested.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas-nested.tentative.html new file mode 100644 index 0000000000..046f3c88ed --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas-nested.tentative.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +.scroller-x { + overflow: scroll; + scroll-snap-type: x mandatory; + width: 500px; + height: 500px; +} +.scroller-y { + overflow: scroll; + scroll-snap-type: y mandatory; + width: 500px; + height: 500px; +} +.space { + width: 4000px; + height: 4000px; +} +.target { + scroll-snap-align: start; + height: 400px; + width: 400px; +} +.large-x { + width: 3000px; + background-color: yellow; +} +.large-y { + height: 2000px; + background-color: yellow; +} +.small { + height: 200px; + width: 200px; + background-color: black; +} +</style> +<div class="scroller-x" id="one-target"> + <div class="space"></div> + <div class="large-x target" id="single" style="left: 200px;"></div> +</div> + +<div class="scroller-x" id="x"> + <div class="space"></div> + <div style="left: 200px;"> + <div class="target large-x"></div> + <div class="target small" style="left: 200px"></div> + <div class="target small" style="left: 600px"></div> + <div class="target small" style="left: 1200px"></div> + </div> +</div> + +<div class="scroller-y" id="y"> + <div class="space"></div> + <div style="top: 200px;"> + <div class="target large-y"></div> + <div class="target small" style="top: 200px"></div> + <div class="target small" style="top: 600px"></div> + <div class="target small" style="top: 1200px"></div> + <div class="target large-y" style="top: 2000px"></div> + </div> +</div> + +<div class="scroller-x" id="two-axes" style="scroll-snap-type: both mandatory"> + <div class="space"></div> + <div class="target large-x" style="top: 200px"></div> +</div> + +<div class="scroller-x" id="overlapping-overflow" style="scroll-snap-type: both mandatory"> + <div class="space"></div> + <div style="left: 200px; top: 200px;"> + <div class="target small"></div> + <div class="target small"></div> + <div class="target small"></div> + <div class="target large-y large-x"></div> + <div class="target small"></div> + <div class="target small"></div> + <div class="target small"></div> + </div> +</div> + +<script> +var one_target_scroller = document.getElementById("one-target"); +var scroller_x = document.getElementById("x"); +var scroller_y = document.getElementById("y"); +var two_axes_scroller = document.getElementById("two-axes"); +var overlapping_scroller = document.getElementById("overlapping-overflow"); + +test(() => { + scroller_x.scrollTo(950, 0); + assert_equals(scroller_x.scrollLeft, 1000); + assert_equals(scroller_x.scrollTop, 0); +}, "Snap within a snap area which covers snapport on x selects a valid snap " + + "position that avoids the overlapping areas at 800-1000 and 1400-1600."); + +test(() => { + scroller_y.scrollTo(0, 950); + assert_equals(scroller_y.scrollLeft, 0); + assert_equals(scroller_y.scrollTop, 1000); +}, "Snap within a snap area which covers snapport on y selects a valid snap " + + "position that avoids the overlapping areas at 800-1000 and 1400-1600."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas.html b/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas.html new file mode 100644 index 0000000000..1e72710811 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/overflowing-snap-areas.html @@ -0,0 +1,180 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +.scroller-x { + overflow: scroll; + scroll-snap-type: x mandatory; + width: 500px; + height: 500px; +} +.scroller-y { + overflow: scroll; + scroll-snap-type: y mandatory; + width: 500px; + height: 500px; +} +.space { + width: 4000px; + height: 4000px; +} +.target { + scroll-snap-align: start; + height: 400px; + width: 400px; +} +.large-x { + width: 3000px; + background-color: yellow; +} +.large-y { + height: 2000px; + background-color: yellow; +} +.small { + height: 200px; + width: 200px; + background-color: black; +} +</style> +<div class="scroller-x" id="one-target"> + <div class="space"></div> + <div class="large-x target" id="single" style="left: 200px;"></div> +</div> + +<div class="scroller-x" id="x"> + <div class="space"></div> + <div style="left: 200px;"> + <div class="target large-x"></div> + <div class="target small" style="left: 200px"></div> + <div class="target small" style="left: 600px"></div> + <div class="target small" style="left: 1200px"></div> + </div> +</div> + +<div class="scroller-y" id="y"> + <div class="space"></div> + <div style="top: 200px;"> + <div class="target large-y"></div> + <div class="target small" style="top: 200px"></div> + <div class="target small" style="top: 600px"></div> + <div class="target small" style="top: 1200px"></div> + <div class="target large-y" style="top: 2000px"></div> + </div> +</div> + +<div class="scroller-x" id="two-axes" style="scroll-snap-type: both mandatory"> + <div class="space"></div> + <div class="target large-x" style="top: 200px"></div> +</div> + +<div class="scroller-x" id="overlapping-overflow" style="scroll-snap-type: both mandatory"> + <div class="space"></div> + <div style="left: 200px; top: 200px;"> + <div class="target small"></div> + <div class="target small"></div> + <div class="target small"></div> + <div class="target large-y large-x"></div> + <div class="target small"></div> + <div class="target small"></div> + <div class="target small"></div> + </div> +</div> + +<script> +var one_target_scroller = document.getElementById("one-target"); +var scroller_x = document.getElementById("x"); +var scroller_y = document.getElementById("y"); +var two_axes_scroller = document.getElementById("two-axes"); +var overlapping_scroller = document.getElementById("overlapping-overflow"); + +test(() => { + one_target_scroller.scrollTo(10, 0); + assert_equals(one_target_scroller.scrollLeft, 200); + assert_equals(one_target_scroller.scrollTop, 0); +}, "Snaps to the snap position if the snap area doesn't cover the snapport on x."); + +test(() => { + var right_align = 3200 - one_target_scroller.clientWidth; + one_target_scroller.scrollTo(right_align, 0); + assert_equals(one_target_scroller.scrollLeft, right_align); + assert_equals(one_target_scroller.scrollTop, 0); +}, "Snaps to the snap position if the snap area covers the snapport on x on the right border."); + +// We use end alignment for this test so that we don't fall on a snap +// position when the snap area just covers the snapport on the left border. +test(() => { + document.getElementById("single").style.scrollSnapAlign = 'end'; + one_target_scroller.scrollTo(200, 0); + assert_equals(one_target_scroller.scrollLeft, 200); + assert_equals(one_target_scroller.scrollTop, 0); +}, "Snaps to the snap position if the snap area covers the snapport on x on the left border."); + +test(() => { + scroller_x.scrollTo(450, 0); + assert_equals(scroller_x.scrollLeft, 400); + assert_equals(scroller_x.scrollTop, 0); +}, "Snaps to a snap area (400) that is closer than the position that reveals " + + "the space between snap areas (600) within the larger snap area on x."); + +test(() => { + scroller_y.scrollTo(0, 450); + assert_equals(scroller_y.scrollLeft, 0); + assert_equals(scroller_y.scrollTop, 400); +}, "Snaps to a snap area (400) that is closer than the position that reveals " + + "the space between snap areas (600) within the larger snap area on y."); + +test(() => { + scroller_x.scrollTo(1650, 0); + assert_equals(scroller_x.scrollLeft, 1650); + assert_equals(scroller_x.scrollTop, 0); +}, "Snap to current scroll position which is a valid snap position, as the " + + "snap area covers snapport on x and there is no intruding snap area."); + +test(() => { + scroller_y.scrollTo(0, 1650); + assert_equals(scroller_y.scrollLeft, 0); + assert_equals(scroller_y.scrollTop, 1650); +}, "Snap to current scroll position which is a valid snap position, as the " + + "snap area covers snapport on y and there is no intruding snap area."); + +test(() => { + const maxScrollTop = scroller_y.scrollHeight - scroller_y.clientHeight; + + // Scroll to the bottom edge which is a valid snap position that a large + // target element covers the snapport. + scroller_y.scrollTo(0, maxScrollTop); + assert_equals(scroller_y.scrollTop, maxScrollTop); + + // Scroll to `the bottom edge + 1`. + scroller_y.scrollTo(0, maxScrollTop + 1); + assert_equals(scroller_y.scrollTop, maxScrollTop); +}, "Don't snap back even if scrollTo tries to scroll to positions which are " + + "outside of the scroll range and if a snap target element covers the snaport"); + +test(() => { + two_axes_scroller.scrollTo(10, 100); + assert_equals(two_axes_scroller.scrollLeft, 10); + assert_equals(two_axes_scroller.scrollTop, 200); +}, "Snap to current scroll position on x as the area is covering x axis." + + "However, we snap to the specified snap position on y as the area is not " + + "covering y axis."); + +test(() => { + overlapping_scroller.scrollTo(200, 800); + assert_equals(overlapping_scroller.scrollLeft, 200); + assert_equals(overlapping_scroller.scrollTop, 800); +}, "snap to current scroll position on y as the area is covering y axis, " + + "even though that area is not the only scroll area at the same position."); + +test(() => { + overlapping_scroller.scrollTo(800, 200); + assert_equals(overlapping_scroller.scrollLeft, 800); + assert_equals(overlapping_scroller.scrollTop, 200); +}, "snap to current scroll position on x as the area is covering x axis, " + + "even though that area is not the only scroll area at the same position."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/overscroll-snap.html b/testing/web-platform/tests/css/css-scroll-snap/overscroll-snap.html new file mode 100644 index 0000000000..13437492ca --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/overscroll-snap.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> + </head> + <body> + <style> + #scroller { + width: 200px; + height: 400px; + overflow-y: scroll; + scroll-snap-type: y mandatory; + background-color: blue; + } + #snap_target { + width: 100px; + height: 1942.5px; + scroll-snap-align: start; + background-color: pink; + } + </style> + <div id="scroller"> + <div id="snap_target"></div> + </div> + <script> + promise_test(async (t) => { + await waitForCompositorCommit(); + let scrollend_promise = new Promise((resolve) => { + scroller.addEventListener("scrollend", resolve); + }); + let scroll_promise = new Promise((resolve) => { + scroller.addEventListener("scroll", resolve); + }); + await new test_driver.Actions().scroll(0, 0, 0, + scroller.scrollHeight, { origin: scroller }).send(); + await scroll_promise; + await scrollend_promise; + assert_approx_equals(scroller.scrollTop, + scroller.scrollHeight - scroller.clientHeight, 1, + "scroller is scrolled to its bottom and not its top."); + }, "snapport covered by snap area doesn't jump"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-computed.html new file mode 100644 index 0000000000..3bb0e740ac --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-computed.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollMarginBlock / scrollMarginInline</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#margin-longhands-logical"> +<meta name="assert" content="scroll-margin-block, scroll-margin-inline computed value is absolute length."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +test_computed_value("scroll-margin-block-start", "10px"); +test_computed_value("scroll-margin-block-start", "calc(10px - 0.5em)", "-10px"); + +test_computed_value("scroll-margin-block-end", "10px"); +test_computed_value("scroll-margin-block-end", "calc(10px - 0.5em)", "-10px"); + +test_computed_value("scroll-margin-inline-start", "10px"); +test_computed_value("scroll-margin-inline-start", "calc(10px - 0.5em)", "-10px"); + +test_computed_value("scroll-margin-inline-end", "10px"); +test_computed_value("scroll-margin-inline-end", "calc(10px - 0.5em)", "-10px"); + + +test_computed_value("scroll-margin-block", "10px"); +test_computed_value("scroll-margin-block", "calc(10px - 0.5em)", "-10px"); +test_computed_value("scroll-margin-block", "1px 2px"); + +test_computed_value("scroll-margin-inline", "10px"); +test_computed_value("scroll-margin-inline", "calc(10px - 0.5em)", "-10px"); +test_computed_value("scroll-margin-inline", "1px 2px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-invalid.html new file mode 100644 index 0000000000..b8a70b0d48 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-invalid.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin-block, scroll-margin-inline with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#margin-longhands-logical"> +<meta name="assert" content="scroll-margin-block, scroll-margin-inline support only the grammar '<length>{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-margin-block-start", "auto"); +test_invalid_value("scroll-margin-block-start", "20%"); +test_invalid_value("scroll-margin-block-start", "-30%"); +test_invalid_value("scroll-margin-block-start", "1px 2px"); + +test_invalid_value("scroll-margin-block-end", "auto"); +test_invalid_value("scroll-margin-block-end", "20%"); +test_invalid_value("scroll-margin-block-end", "-30%"); +test_invalid_value("scroll-margin-block-end", "1px 2px"); + +test_invalid_value("scroll-margin-inline-start", "auto"); +test_invalid_value("scroll-margin-inline-start", "20%"); +test_invalid_value("scroll-margin-inline-start", "-30%"); +test_invalid_value("scroll-margin-inline-start", "1px 2px"); + +test_invalid_value("scroll-margin-inline-end", "auto"); +test_invalid_value("scroll-margin-inline-end", "20%"); +test_invalid_value("scroll-margin-inline-end", "-30%"); +test_invalid_value("scroll-margin-inline-end", "1px 2px"); + + +test_invalid_value("scroll-margin-block", "auto"); +test_invalid_value("scroll-margin-block", "20%"); +test_invalid_value("scroll-margin-block", "-30%"); +test_invalid_value("scroll-margin-block", "1px 2px 3px"); + +test_invalid_value("scroll-margin-inline", "auto"); +test_invalid_value("scroll-margin-inline", "20%"); +test_invalid_value("scroll-margin-inline", "-30%"); +test_invalid_value("scroll-margin-inline", "1px 2px 3px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-shorthand.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-shorthand.html new file mode 100644 index 0000000000..04e1d42864 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-shorthand.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin-block, scroll-margin-inline set longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#margin-longhands-logical"> +<meta name="assert" content="scroll-margin-block, scroll-margin-inline support the full grammar '<length>{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('scroll-margin-block', '10px 20px', { + 'scroll-margin-block-start': '10px', + 'scroll-margin-block-end': '20px' +}); + +test_shorthand_value('scroll-margin-block', '30px', { + 'scroll-margin-block-start': '30px', + 'scroll-margin-block-end': '30px' +}); + +test_shorthand_value('scroll-margin-inline', '50px 60px', { + 'scroll-margin-inline-start': '50px', + 'scroll-margin-inline-end': '60px' +}); + +test_shorthand_value('scroll-margin-inline', '-40px', { + 'scroll-margin-inline-start': '-40px', + 'scroll-margin-inline-end': '-40px' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-valid.html new file mode 100644 index 0000000000..e675eeb427 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-block-inline-valid.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin-block, scroll-margin-inline with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#margin-longhands-logical"> +<meta name="assert" content="scroll-margin-block, scroll-margin-inline support the full grammar '<length>{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-margin-block-start", "-10px"); +test_valid_value("scroll-margin-block-start", "calc(2em + 3ex)"); + +test_valid_value("scroll-margin-block-end", "-10px"); +test_valid_value("scroll-margin-block-end", "calc(2em + 3ex)"); + +test_valid_value("scroll-margin-inline-start", "-10px"); +test_valid_value("scroll-margin-inline-start", "calc(2em + 3ex)"); + +test_valid_value("scroll-margin-inline-end", "-10px"); +test_valid_value("scroll-margin-inline-end", "calc(2em + 3ex)"); + + +test_valid_value("scroll-margin-block", "-10px"); +test_valid_value("scroll-margin-block", "calc(2em + 3ex)"); +test_valid_value("scroll-margin-block", "1px 2px"); + +test_valid_value("scroll-margin-inline", "-10px"); +test_valid_value("scroll-margin-inline", "calc(2em + 3ex)"); +test_valid_value("scroll-margin-inline", "1px 2px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-computed.html new file mode 100644 index 0000000000..ae7b6de5dd --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-computed.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollMargin</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin"> +<meta name="assert" content="scroll-margin computed value is absolute length."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +test_computed_value("scroll-margin-top", "10px"); +test_computed_value("scroll-margin-top", "calc(10px - 0.5em)", "-10px"); + + +test_computed_value("scroll-margin-right", "10px"); +test_computed_value("scroll-margin-right", "calc(10px - 0.5em)", "-10px"); + + +test_computed_value("scroll-margin-bottom", "10px"); +test_computed_value("scroll-margin-bottom", "calc(10px - 0.5em)", "-10px"); + + +test_computed_value("scroll-margin-left", "10px"); +test_computed_value("scroll-margin-left", "calc(10px - 0.5em)", "-10px"); + + +test_computed_value("scroll-margin", "10px"); +test_computed_value("scroll-margin", "calc(10px - 0.5em)", "-10px"); + +test_computed_value("scroll-margin", "1px 2px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-invalid.html new file mode 100644 index 0000000000..97beb0d295 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-invalid.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin"> +<meta name="assert" content="scroll-margin supports only the grammar '<length>{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-margin-top", "auto"); +test_invalid_value("scroll-margin-top", "20%"); +test_invalid_value("scroll-margin-top", "-30%"); +test_invalid_value("scroll-margin-top", "1px 2px"); + + +test_invalid_value("scroll-margin-right", "auto"); +test_invalid_value("scroll-margin-right", "20%"); +test_invalid_value("scroll-margin-right", "-30%"); +test_invalid_value("scroll-margin-right", "1px 2px"); + + +test_invalid_value("scroll-margin-bottom", "auto"); +test_invalid_value("scroll-margin-bottom", "20%"); +test_invalid_value("scroll-margin-bottom", "-30%"); +test_invalid_value("scroll-margin-bottom", "1px 2px"); + + +test_invalid_value("scroll-margin-left", "auto"); +test_invalid_value("scroll-margin-left", "20%"); +test_invalid_value("scroll-margin-left", "-30%"); +test_invalid_value("scroll-margin-left", "1px 2px"); + + +test_invalid_value("scroll-margin", "auto"); +test_invalid_value("scroll-margin", "20%"); +test_invalid_value("scroll-margin", "-30%"); +test_invalid_value("scroll-margin", "1px 2px 3px 4px 5px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-shorthand.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-shorthand.html new file mode 100644 index 0000000000..96a4595226 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-shorthand.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin sets longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin"> +<meta name="assert" content="scroll-margin supports the full grammar '<length>{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('scroll-margin', '10px', { + 'scroll-margin-top': '10px', + 'scroll-margin-right': '10px', + 'scroll-margin-bottom': '10px', + 'scroll-margin-left': '10px' +}); + +test_shorthand_value('scroll-margin', '30px 20px', { + 'scroll-margin-top': '30px', + 'scroll-margin-right': '20px', + 'scroll-margin-bottom': '30px', + 'scroll-margin-left': '20px' +}); + +test_shorthand_value('scroll-margin', '1px 2px 3px', { + 'scroll-margin-top': '1px', + 'scroll-margin-right': '2px', + 'scroll-margin-bottom': '3px', + 'scroll-margin-left': '2px' +}); + +test_shorthand_value('scroll-margin', '-1px 2px 3px 0', { + 'scroll-margin-top': '-1px', + 'scroll-margin-right': '2px', + 'scroll-margin-bottom': '3px', + 'scroll-margin-left': '0px' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-valid.html new file mode 100644 index 0000000000..be34998691 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-margin-valid.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-margin with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin"> +<meta name="assert" content="scroll-margin supports the full grammar '<length>{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-margin-top", "0", "0px"); +test_valid_value("scroll-margin-top", "-10px"); +test_valid_value("scroll-margin-top", "calc(2em + 3ex)"); + + +test_valid_value("scroll-margin-right", "0", "0px"); +test_valid_value("scroll-margin-right", "-10px"); +test_valid_value("scroll-margin-right", "calc(2em + 3ex)"); + + +test_valid_value("scroll-margin-bottom", "0", "0px"); +test_valid_value("scroll-margin-bottom", "-10px"); +test_valid_value("scroll-margin-bottom", "calc(2em + 3ex)"); + + +test_valid_value("scroll-margin-left", "0", "0px"); +test_valid_value("scroll-margin-left", "-10px"); +test_valid_value("scroll-margin-left", "calc(2em + 3ex)"); + + +test_valid_value("scroll-margin", "0", "0px"); +test_valid_value("scroll-margin", "-10px"); +test_valid_value("scroll-margin", "calc(2em + 3ex)"); + +test_valid_value("scroll-margin", "1px 2px"); +test_valid_value("scroll-margin", "1px 2px 3px"); +test_valid_value("scroll-margin", "1px 2px 3px 4px"); +test_valid_value("scroll-margin", "0 0 0 0", "0px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-computed.html new file mode 100644 index 0000000000..6a66110cda --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-computed.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollPaddingBlock / scrollPaddingInline</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#padding-longhands-logical"> +<meta name="assert" content="scroll-padding-block, scroll-padding-inline computed value is per side, either the keyword auto or a computed <length-percentage> value."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +test_computed_value("scroll-padding-block-start", "auto"); +test_computed_value("scroll-padding-block-start", "10px"); +test_computed_value("scroll-padding-block-start", "20%"); +test_computed_value("scroll-padding-block-start", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-block-start", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-block-start", "calc(50% + 60px)"); + +test_computed_value("scroll-padding-block-end", "auto"); +test_computed_value("scroll-padding-block-end", "10px"); +test_computed_value("scroll-padding-block-end", "20%"); +test_computed_value("scroll-padding-block-end", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-block-end", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-block-end", "calc(50% + 60px)"); + +test_computed_value("scroll-padding-inline-start", "auto"); +test_computed_value("scroll-padding-inline-start", "10px"); +test_computed_value("scroll-padding-inline-start", "20%"); +test_computed_value("scroll-padding-inline-start", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-inline-start", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-inline-start", "calc(50% + 60px)"); + +test_computed_value("scroll-padding-inline-end", "auto"); +test_computed_value("scroll-padding-inline-end", "10px"); +test_computed_value("scroll-padding-inline-end", "20%"); +test_computed_value("scroll-padding-inline-end", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-inline-end", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-inline-end", "calc(50% + 60px)"); + + +test_computed_value("scroll-padding-block", "auto"); +test_computed_value("scroll-padding-block", "10px"); +test_computed_value("scroll-padding-block", "20%"); +test_computed_value("scroll-padding-block", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-block", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-block", "calc(50% + 60px)"); +test_computed_value("scroll-padding-block", "1px 2px"); +test_computed_value("scroll-padding-block", "1px auto"); +test_computed_value("scroll-padding-block", "auto auto", "auto"); + +test_computed_value("scroll-padding-inline", "auto"); +test_computed_value("scroll-padding-inline", "10px"); +test_computed_value("scroll-padding-inline", "20%"); +test_computed_value("scroll-padding-inline", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-inline", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-inline", "calc(50% + 60px)"); +test_computed_value("scroll-padding-inline", "1px 2px"); +test_computed_value("scroll-padding-inline", "1px auto"); +test_computed_value("scroll-padding-inline", "auto auto", "auto"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-invalid.html new file mode 100644 index 0000000000..da995cfcc0 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-invalid.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding-block, scroll-padding-inline with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#padding-longhands-logical"> +<meta name="assert" content="scroll-padding-block, scroll-padding-inline supports only the grammar '[ auto | <length-percentage> ]{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-padding-block-start", "none"); +test_invalid_value("scroll-padding-block-start", "-10px"); +test_invalid_value("scroll-padding-block-start", "10px 20%"); +test_invalid_value("scroll-padding-block-start", "fit-content"); + +test_invalid_value("scroll-padding-block-end", "none"); +test_invalid_value("scroll-padding-block-end", "-10px"); +test_invalid_value("scroll-padding-block-end", "10px 20%"); +test_invalid_value("scroll-padding-block-end", "max-content"); + +test_invalid_value("scroll-padding-inline-start", "none"); +test_invalid_value("scroll-padding-inline-start", "-10px"); +test_invalid_value("scroll-padding-inline-start", "10px 20%"); +test_invalid_value("scroll-padding-inline-start", "min-content"); + +test_invalid_value("scroll-padding-inline-end", "none"); +test_invalid_value("scroll-padding-inline-end", "-10px"); +test_invalid_value("scroll-padding-inline-end", "10px 20%"); +test_invalid_value("scroll-padding-inline-end", "max-content"); + + +test_invalid_value("scroll-padding-block", "none"); +test_invalid_value("scroll-padding-block", "-10px"); +test_invalid_value("scroll-padding-block", "-20%"); +test_invalid_value("scroll-padding-block", "calc(auto)"); +test_invalid_value("scroll-padding-block", "10px 20px 30px 40px 50px"); +test_invalid_value("scroll-padding-block", "fit-content"); +test_invalid_value("scroll-padding-block", "max-content"); +test_invalid_value("scroll-padding-block", "min-content"); + +test_invalid_value("scroll-padding-inline", "none"); +test_invalid_value("scroll-padding-inline", "-10px"); +test_invalid_value("scroll-padding-inline", "-20%"); +test_invalid_value("scroll-padding-inline", "calc(auto)"); +test_invalid_value("scroll-padding-inline", "10px 20px 30px 40px 50px"); +test_invalid_value("scroll-padding-inline", "fit-content"); +test_invalid_value("scroll-padding-inline", "max-content"); +test_invalid_value("scroll-padding-inline", "min-content"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-shorthand.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-shorthand.html new file mode 100644 index 0000000000..491cfa9e96 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-shorthand.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding-block, scroll-padding-inline set longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#padding-longhands-logical"> +<meta name="assert" content="scroll-padding-block, scroll-padding-inline support the full grammar '[ auto | <length-percentage> ]{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('scroll-padding-block', 'auto 10px', { + 'scroll-padding-block-start': 'auto', + 'scroll-padding-block-end': '10px' +}); + +test_shorthand_value('scroll-padding-block', '20%', { + 'scroll-padding-block-start': '20%', + 'scroll-padding-block-end': '20%' +}); + +test_shorthand_value('scroll-padding-inline', '10px auto', { + 'scroll-padding-inline-start': '10px', + 'scroll-padding-inline-end': 'auto' +}); + +test_shorthand_value('scroll-padding-inline', '0%', { + 'scroll-padding-inline-start': '0%', + 'scroll-padding-inline-end': '0%' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-valid.html new file mode 100644 index 0000000000..a932bb6393 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-block-inline-valid.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding-block, scroll-padding-inline with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#padding-longhands-logical"> +<meta name="assert" content="scroll-padding-block, scroll-padding-inline supports the full grammar '[ auto | <length-percentage> ]{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-padding-block-start", "auto"); +test_valid_value("scroll-padding-block-start", "10px"); +test_valid_value("scroll-padding-block-start", "20%"); +test_valid_value("scroll-padding-block-start", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-block-start", "calc(50% + 60px)"); + +test_valid_value("scroll-padding-block-end", "auto"); +test_valid_value("scroll-padding-block-end", "10px"); +test_valid_value("scroll-padding-block-end", "20%"); +test_valid_value("scroll-padding-block-end", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-block-end", "calc(50% + 60px)"); + +test_valid_value("scroll-padding-inline-start", "auto"); +test_valid_value("scroll-padding-inline-start", "10px"); +test_valid_value("scroll-padding-inline-start", "20%"); +test_valid_value("scroll-padding-inline-start", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-inline-start", "calc(50% + 60px)"); + +test_valid_value("scroll-padding-inline-end", "auto"); +test_valid_value("scroll-padding-inline-end", "10px"); +test_valid_value("scroll-padding-inline-end", "20%"); +test_valid_value("scroll-padding-inline-end", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-inline-end", "calc(50% + 60px)"); + + +test_valid_value("scroll-padding-block", "auto"); +test_valid_value("scroll-padding-block", "10px"); +test_valid_value("scroll-padding-block", "20%"); +test_valid_value("scroll-padding-block", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-block", "calc(50% + 60px)"); +test_valid_value("scroll-padding-block", "1px 2px"); +test_valid_value("scroll-padding-block", "1px auto"); +test_valid_value("scroll-padding-block", "auto auto", "auto"); + +test_valid_value("scroll-padding-inline", "auto"); +test_valid_value("scroll-padding-inline", "10px"); +test_valid_value("scroll-padding-inline", "20%"); +test_valid_value("scroll-padding-inline", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-inline", "calc(50% + 60px)"); +test_valid_value("scroll-padding-inline", "1px 2px"); +test_valid_value("scroll-padding-inline", "1px auto"); +test_valid_value("scroll-padding-inline", "auto auto", "auto"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-computed.html new file mode 100644 index 0000000000..f638138a7f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-computed.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollPadding</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"> +<meta name="assert" content="scroll-padding computed value is per side, either the keyword auto or a computed <length-percentage> value."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +test_computed_value("scroll-padding-top", "auto"); +test_computed_value("scroll-padding-top", "0", "0px"); +test_computed_value("scroll-padding-top", "10px"); +test_computed_value("scroll-padding-top", "20%"); +test_computed_value("scroll-padding-top", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-top", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-top", "calc(50% + 60px)"); + + +test_computed_value("scroll-padding-right", "auto"); +test_computed_value("scroll-padding-right", "0", "0px"); +test_computed_value("scroll-padding-right", "10px"); +test_computed_value("scroll-padding-right", "20%"); +test_computed_value("scroll-padding-right", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-right", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-right", "calc(50% + 60px)"); + + +test_computed_value("scroll-padding-bottom", "auto"); +test_computed_value("scroll-padding-bottom", "0", "0px"); +test_computed_value("scroll-padding-bottom", "10px"); +test_computed_value("scroll-padding-bottom", "20%"); +test_computed_value("scroll-padding-bottom", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-bottom", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-bottom", "calc(50% + 60px)"); + + +test_computed_value("scroll-padding-left", "auto"); +test_computed_value("scroll-padding-left", "0", "0px"); +test_computed_value("scroll-padding-left", "10px"); +test_computed_value("scroll-padding-left", "20%"); +test_computed_value("scroll-padding-left", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding-left", "calc(10px - 0.5em)", "0px"); +test_computed_value("scroll-padding-left", "calc(50% + 60px)"); + + +test_computed_value("scroll-padding", "auto"); +test_computed_value("scroll-padding", "10px"); +test_computed_value("scroll-padding", "0", "0px"); +test_computed_value("scroll-padding", "20%"); +test_computed_value("scroll-padding", "calc(10px + 0.5em)", "30px"); +test_computed_value("scroll-padding", "calc(10px - 0.5em)", "0px"); + +test_computed_value("scroll-padding", "1px 2px"); +test_computed_value("scroll-padding", "1px 2px 3%"); +test_computed_value("scroll-padding", "1px 2px 3% 4px"); +test_computed_value("scroll-padding", "1px auto"); +test_computed_value("scroll-padding", "0 0 0 0", "0px"); +test_computed_value("scroll-padding", "auto auto auto auto", "auto"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-invalid.html new file mode 100644 index 0000000000..c805ee2e55 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-invalid.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"> +<meta name="assert" content="scroll-padding supports only the grammar '[ <length-percentage> | auto ]{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-padding-top", "20"); +test_invalid_value("scroll-padding-top", "-20px"); +test_invalid_value("scroll-padding-top", "none"); +test_invalid_value("scroll-padding-top", "10px 20%"); +test_invalid_value("scroll-padding-top", "fit-content"); + + +test_invalid_value("scroll-padding-right", "20"); +test_invalid_value("scroll-padding-right", "-20px"); +test_invalid_value("scroll-padding-right", "none"); +test_invalid_value("scroll-padding-right", "10px 20%"); +test_invalid_value("scroll-padding-right", "max-content"); + + +test_invalid_value("scroll-padding-bottom", "20"); +test_invalid_value("scroll-padding-bottom", "-20px"); +test_invalid_value("scroll-padding-bottom", "none"); +test_invalid_value("scroll-padding-bottom", "10px 20%"); +test_invalid_value("scroll-padding-bottom", "min-content"); + + +test_invalid_value("scroll-padding-left", "20"); +test_invalid_value("scroll-padding-left", "-20px"); +test_invalid_value("scroll-padding-left", "none"); +test_invalid_value("scroll-padding-left", "10px 20%"); +test_invalid_value("scroll-padding-left", "fit-content"); + + +test_invalid_value("scroll-padding", "20"); +test_invalid_value("scroll-padding", "-20px"); +test_invalid_value("scroll-padding", "none"); +test_invalid_value("scroll-padding", "calc(auto)"); +test_invalid_value("scroll-padding", "10px 20px 30px 40px 50px"); +test_invalid_value("scroll-padding", "fit-content"); +test_invalid_value("scroll-padding", "max-content"); +test_invalid_value("scroll-padding", "min-content"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-shorthand.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-shorthand.html new file mode 100644 index 0000000000..2162c7f10d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-shorthand.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding sets longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"> +<meta name="assert" content="scroll-padding supports the full grammar '[ <length-percentage> | auto ]{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('scroll-padding', '10px', { + 'scroll-padding-top': '10px', + 'scroll-padding-right': '10px', + 'scroll-padding-bottom': '10px', + 'scroll-padding-left': '10px' +}); + +test_shorthand_value('scroll-padding', 'auto 20px', { + 'scroll-padding-top': 'auto', + 'scroll-padding-right': '20px', + 'scroll-padding-bottom': 'auto', + 'scroll-padding-left': '20px' +}); + +test_shorthand_value('scroll-padding', '1px 2px 3px', { + 'scroll-padding-top': '1px', + 'scroll-padding-right': '2px', + 'scroll-padding-bottom': '3px', + 'scroll-padding-left': '2px' +}); + +test_shorthand_value('scroll-padding', '1% 2px 3px 0', { + 'scroll-padding-top': '1%', + 'scroll-padding-right': '2px', + 'scroll-padding-bottom': '3px', + 'scroll-padding-left': '0px' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-valid.html new file mode 100644 index 0000000000..0e7c86b12b --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-padding-valid.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-padding with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"> +<meta name="assert" content="scroll-padding supports the full grammar '[ <length-percentage> | auto ]{1,4}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-padding-top", "auto"); +test_valid_value("scroll-padding-top", "0", "0px"); +test_valid_value("scroll-padding-top", "10px"); +test_valid_value("scroll-padding-top", "20%"); +test_valid_value("scroll-padding-top", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-top", "calc(50% + 60px)"); + + +test_valid_value("scroll-padding-right", "auto"); +test_valid_value("scroll-padding-right", "0", "0px"); +test_valid_value("scroll-padding-right", "10px"); +test_valid_value("scroll-padding-right", "20%"); +test_valid_value("scroll-padding-right", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-right", "calc(50% + 60px)"); + + +test_valid_value("scroll-padding-bottom", "auto"); +test_valid_value("scroll-padding-bottom", "0", "0px"); +test_valid_value("scroll-padding-bottom", "10px"); +test_valid_value("scroll-padding-bottom", "20%"); +test_valid_value("scroll-padding-bottom", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-bottom", "calc(50% + 60px)"); + + +test_valid_value("scroll-padding-left", "auto"); +test_valid_value("scroll-padding-left", "0", "0px"); +test_valid_value("scroll-padding-left", "10px"); +test_valid_value("scroll-padding-left", "20%"); +test_valid_value("scroll-padding-left", "calc(2em + 3ex)"); +test_valid_value("scroll-padding-left", "calc(50% + 60px)"); + + +test_valid_value("scroll-padding", "auto"); +test_valid_value("scroll-padding", "10px"); +test_valid_value("scroll-padding", "0", "0px"); +test_valid_value("scroll-padding", "20%"); +test_valid_value("scroll-padding", "calc(2em + 3ex)"); + +test_valid_value("scroll-padding", "1px 2px"); +test_valid_value("scroll-padding", "1px 2px 3%"); +test_valid_value("scroll-padding", "1px 2px 3% 4px"); +test_valid_value("scroll-padding", "1px auto"); +test_valid_value("scroll-padding", "0 0 0 0", "0px"); +test_valid_value("scroll-padding", "auto auto auto auto", "auto"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-computed.html new file mode 100644 index 0000000000..daed998ef2 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-computed.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollSnapAlign</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-align"> +<meta name="assert" content="scroll-snap-align computed value is as specified."> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("scroll-snap-align", "none"); +test_computed_value("scroll-snap-align", "start"); +test_computed_value("scroll-snap-align", "end"); +test_computed_value("scroll-snap-align", "center"); + +test_computed_value("scroll-snap-align", "start none"); +test_computed_value("scroll-snap-align", "center end"); +test_computed_value("scroll-snap-align", "start start", "start"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-invalid.html new file mode 100644 index 0000000000..9a1eeb77bb --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-invalid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-align with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-align"> +<meta name="assert" content="scroll-snap-align supports only the grammar '[ none | start | end | center ]{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-snap-align", "auto"); + +test_invalid_value("scroll-snap-align", "start invalid"); + +test_invalid_value("scroll-snap-align", "start end center"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-valid.html new file mode 100644 index 0000000000..0201448825 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-align-valid.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-align with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-align"> +<meta name="assert" content="scroll-snap-align supports the full grammar '[ none | start | end | center ]{1,2}'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-snap-align", "none"); +test_valid_value("scroll-snap-align", "start"); +test_valid_value("scroll-snap-align", "end"); +test_valid_value("scroll-snap-align", "center"); + +test_valid_value("scroll-snap-align", "start none"); +test_valid_value("scroll-snap-align", "center end"); +test_valid_value("scroll-snap-align", "start start", "start"); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed.html new file mode 100644 index 0000000000..49b369a0fe --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollSnapStop</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-stop"> +<meta name="assert" content="scroll-snap-stop computed value is as specified."> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("scroll-snap-stop", "normal"); +test_computed_value("scroll-snap-stop", "always"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-invalid.html new file mode 100644 index 0000000000..67feda0ca7 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-invalid.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-stop with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-stop"> +<meta name="assert" content="scroll-snap-stop supports only the grammar 'normal | always'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-snap-stop", "auto"); + +test_invalid_value("scroll-snap-stop", "normal always"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid.html new file mode 100644 index 0000000000..a59caff396 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-stop-valid.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-stop with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-stop"> +<meta name="assert" content="scroll-snap-stop supports the full grammar 'normal | always'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-snap-stop", "normal"); +test_valid_value("scroll-snap-stop", "always"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-computed.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-computed.html new file mode 100644 index 0000000000..660a1fee06 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-computed.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap: getComputedStyle().scrollSnapType</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-type"> +<meta name="assert" content="scroll-snap-type computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("scroll-snap-type", "none"); + +test_computed_value("scroll-snap-type", "x"); +test_computed_value("scroll-snap-type", "y"); +test_computed_value("scroll-snap-type", "block"); +test_computed_value("scroll-snap-type", "inline"); +test_computed_value("scroll-snap-type", "both"); + +test_computed_value("scroll-snap-type", "y mandatory"); +test_computed_value("scroll-snap-type", "inline proximity", "inline"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-invalid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-invalid.html new file mode 100644 index 0000000000..6177ff3baf --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-invalid.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-type with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-type"> +<meta name="assert" content="scroll-snap-type supports only the grammar 'none | [ x | y | block | inline | both ] [ mandatory | proximity ]?'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("scroll-snap-type", "auto"); + +test_invalid_value("scroll-snap-type", "x y"); +test_invalid_value("scroll-snap-type", "block mandatory inline"); + +test_invalid_value("scroll-snap-type", "both none"); +test_invalid_value("scroll-snap-type", "mandatory"); +test_invalid_value("scroll-snap-type", "proximity"); +test_invalid_value("scroll-snap-type", "mandatory inline"); +test_invalid_value("scroll-snap-type", "proximity both"); +test_invalid_value("scroll-snap-type", "mandatory x"); +test_invalid_value("scroll-snap-type", "proximity y"); +test_invalid_value("scroll-snap-type", "mandatory block"); +test_invalid_value("scroll-snap-type", "proximity mandatory"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-valid.html b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-valid.html new file mode 100644 index 0000000000..ca995770f4 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/parsing/scroll-snap-type-valid.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Scroll Snap Test: scroll-snap-type with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-snap-type"> +<meta name="assert" content="scroll-snap-type supports the full grammar 'none | [ x | y | block | inline | both ] [ mandatory | proximity ]?'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("scroll-snap-type", "none"); + +test_valid_value("scroll-snap-type", "x"); +test_valid_value("scroll-snap-type", "y"); +test_valid_value("scroll-snap-type", "block"); +test_valid_value("scroll-snap-type", "inline"); +test_valid_value("scroll-snap-type", "both"); + +test_valid_value("scroll-snap-type", "y mandatory"); +test_valid_value("scroll-snap-type", "block mandatory"); +test_valid_value("scroll-snap-type", "both mandatory"); +test_valid_value("scroll-snap-type", "inline proximity", "inline"); // The shortest serialization is preferable +test_valid_value("scroll-snap-type", "x proximity", "x"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/resnap-on-snap-alignment-change.html b/testing/web-platform/tests/css/css-scroll-snap/resnap-on-snap-alignment-change.html new file mode 100644 index 0000000000..e4648b1d79 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/resnap-on-snap-alignment-change.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<title> + Resnap when the current snap position is no longer a valid snap target. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/css-scroll-snap/support/common.js"></script> +<style> + html { + scroll-snap-type: y mandatory; + } + body { + margin: 0; + } + .snap { + scroll-snap-align: center; + height: 100vh; + margin: 0; + } +</style> +<body onload = "runTest()"> +<div id = "card1" class="snap">ONE</div> +<div id = "card2" class="snap">TWO</div> +<div id = "card3" class="snap">THREE</div> +</body> +<script type="text/javascript"> + function runTest() { + promise_test(async t => { + const scroller = document.scrollingElement; + // scroll to card THREE + let expectedSnapPosition = card3.offsetTop; + scroller.scrollTo(0, expectedSnapPosition); + assert_equals(scroller.scrollTop, expectedSnapPosition, + 'Aligned with card THREE before style change'); + + // Snap to card TWO after invalidating card THREE as a snap target. + card3.style.scrollSnapAlign = 'none center'; + await waitForNextFrame(); + expectedSnapPosition = card2.offsetTop; + assert_equals(scroller.scrollTop, expectedSnapPosition, + 'Aligned with card TWO after style change'); + }, 'Resnap when the current snap position is no longer a valid snap target'); + } +</script> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-visibility-check.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-visibility-check.html new file mode 100644 index 0000000000..b41ccb36fd --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-visibility-check.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-margin"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1708303"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +body { margin: 0 } +#scroller { + height: 500px; + width: 500px; + scrollbar-width: none; + overflow: scroll; +} +#target { + width: 100px; + height: 100px; + background-color: blue; + scroll-margin: 100px; + margin-left: 510px; +} +</style> + +<div id="scroller"> + <div style="width: 450px; height: 450px;"></div> + <div tabindex="0" id="target"></div> + <div style="width: 2000px; height: 2000px;"></div> +</div> + +<script> +let scroller = document.getElementById("scroller"); +let target = document.getElementById("target"); +promise_test(async function() { + scroller.scrollTo(0, 0); + await new Promise(resolve => { + scroller.addEventListener("scroll", resolve, { once: true }); + document.getElementById("target").focus(); + }); + assert_true(scroller.scrollTop > 0, "Visibility check should not account for margin"); + assert_true(scroller.scrollLeft > 0, "Visibility check should not account for margin"); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-margin.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin.html new file mode 100644 index 0000000000..e6ce4ac49c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-margin" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} +#target { + width: 300px; + height: 300px; + background-color: blue; +} +#another-target { + width: 300px; + height: 300px; + top: 400px; + left: 400px; + background-color: blue; + scroll-snap-align: start; +} +</style> + +<div id="scroller"> + <div style="width: 2000px; height: 2000px;"></div> + <div id="target"></div> + <div id="another-target"></div> +</div> + +<script> +test(() => { + target.style.scrollSnapAlign = "start"; + target.style.scrollMargin = "100px"; + target.style.left = "300px"; + target.style.top = "300px"; + + scroller.scrollTo(0, 0); + // `target position (300px, 300px)` - `margin (100px, 100px)`. + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); + + target.style.scrollSnapAlign = "end"; + + // `target position (300px, 300px)` + `target size (300px, 300px) + + // `margin (100px, 100px) - `scroller size (500px, 500px)`. + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); +}, "Snaps to the positions adjusted by scroll-margin"); + +test(() => { + target.style.left = "0px"; + target.style.top = "0px"; + target.style.scrollSnapAlign = "start"; + + // Since the target is at (0px, 0px) in the scroll port, the added margin + // should not be considered, and the snap points for this snap area should be + // the closest points in the scroll port (i.e x=0 or y=0). + target.style.scrollMargin = "200px"; + + // Distance from target without margin: + // `scroll position (150px, 150px)` - `target position (0px, 0px)` + // = (150px, 150px) + // + // Distance from target with margin: + // `scroll position (150px, 150px)` - [`target position (0px, 0px)` - + // `target margin (200px, 200px)`] + // = (350px, 350px) + // + // Distance from other target: + // `other target position (400px, 400px)` - `scroll position (150px, 150px)` + // = (250px, 250px) + // + // Therefore if the "out-of-scrollport" scroll-margin contributes to the + // calculation, then the other target would be snapped to. However if the + // scroll-margin is not considered, then the (0px, 0px) target should be + // snapped to. + scroller.scrollTo(150, 150); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "scroll-margin doesn't contribute to the snap position of the element " + + "if it's outside of the scroll port"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-on-large-element-not-covering-snapport.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-on-large-element-not-covering-snapport.tentative.html new file mode 100644 index 0000000000..df4fc20ec8 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-on-large-element-not-covering-snapport.tentative.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<title> + A test case that scrolling to a point on large element where the snap area + doesn't cover over the snapport +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + width: 100%; +} +#scroller { + left: 10px; + height: 200px; + width: 100px; + overflow-y: scroll; + overflow-x: hidden; + scroll-snap-type: y mandatory; +} +.snap { + scroll-snap-align: start; + background-color: green; +} +.target { + background-color: red; + border-radius: 100%; + height: 88px; + top: 536px; +} +</style> +<div id="scroller"> + <div style="height: 2000px;"></div> + <div class="snap" style="top: 0px; height: 40px;">1</div> + <div class="snap" style="top: 40px; height: 40px;">2</div> + <div class="snap" style="top: 80px; height: 1000px;">3</div> + <div class="snap" style="top: 1080px; height: 40px;">4</div> + <div class="snap" style="top: 1120px; height: 40px;">5</div> + <div class="target"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + + scroller.scrollBy(0, 10); + // Snap to the first snap point. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 40); + + scroller.scrollBy(0, 10); + // Snap to the second snap point. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 80); + + scroller.scrollBy(0, 100); + // Snap to the given scrolling position since the third snap target element + // is larger than the snapport. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 180); + + scroller.scrollBy(0, 100); + // Again, snap to the given scrolling position. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 280); + + // Scroll to a point where the third target element is still covering over the + // snapport. + scroller.scrollBy(0, 600); + assert_equals(scroller.scrollLeft, 0); + // Again, snap to the given scrolling position. + assert_equals(scroller.scrollTop, 880); + + // Scroll to a point where the third target element is no longer convering + // over the snapport, rather the forth snap point is in the snapport. + scroller.scrollBy(0, 10); + // Now, snap to the forth snap point. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 1080); + + // Scroll back a bit. + scroller.scrollBy(0, -10); + // This should snap to the bottom of the 3rd snap area and not all the way + // back up to its top. + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 880); +}, "snaps to bottom edge of large snap area that doesn't cover the snap port."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-padding-and-margin.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-padding-and-margin.html new file mode 100644 index 0000000000..97d30c702d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-padding-and-margin.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-margin"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1708303"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +body { margin: 0 } +#scroller { + height: 500px; + width: 500px; + scroll-padding: 10px; + scrollbar-width: none; + overflow: hidden; +} +#target { + width: 100px; + height: 100px; + background-color: blue; + scroll-margin: 10px; + margin-left: 1000px; +} +</style> + +<div id="scroller"> + <div style="width: 2000px; height: 2000px;"></div> + <div id="target"></div> + <div style="width: 2000px; height: 2000px;"></div> +</div> + +<script> +let scroller = document.getElementById("scroller"); +let target = document.getElementById("target"); +test(() => { + scroller.scrollTo(0, 0); + target.scrollIntoView({ block: "start", inline: "start" }); + + assert_equals(scroller.scrollTop, 2000 - 20, "Top should account for margin + padding"); + assert_equals(scroller.scrollLeft, 1000 - 20, "Left should account for margin + padding"); +}); + +test(() => { + scroller.scrollTo(0, 0); + target.scrollIntoView({ block: "end", inline: "end" }); + assert_equals(scroller.scrollTop, 2000 - 500 + 100 + 20, "Top should account for margin + padding"); + assert_equals(scroller.scrollLeft, 1000 - 500 + 100 + 20, "Left should account for margin + padding"); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-padding.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-padding.html new file mode 100644 index 0000000000..0c637ed6db --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-padding.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} +#target { + width: 300px; + height: 300px; + background-color: blue; +} +</style> + +<div id="scroller"> + <div style="width: 2000px; height: 2000px;"></div> + <div id="target"></div> +</div> + +<script> +test(() => { + scroller.style.scrollPadding = "100px"; + + target.style.scrollSnapAlign = "start"; + target.style.left = "300px"; + target.style.top = "300px"; + + scroller.scrollTo(0, 0); + // `target position (300px, 300px)` - `padding (100px, 100px)`. + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); + + target.style.scrollSnapAlign = "end"; + + // `target position (300px, 300px)` + `target size (300px, 300px) + + // `padding (100px, 100px) - `scroller size (500px, 500px)`. + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); +}, "Snaps to the positions adjusted by scroll-padding"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-nested-snap-area-layout-changed.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-nested-snap-area-layout-changed.tentative.html new file mode 100644 index 0000000000..be0bd5c4a5 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-nested-snap-area-layout-changed.tentative.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html> + +<head> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> +</head> +<body> + <style> + #scroller { + overflow: scroll; + scroll-snap-type: y mandatory; + height: 200px; + width: 200px; + border: solid 1px; + position:absolute; + } + .snap_area { + position: absolute; + width: 100px; + left: calc(50% - 50px); + } + #outer_snap_area { + scroll-snap-align: start; + height: 1000px; + background-color: blue; + } + #inner_snap_area { + scroll-snap-align: start; + height: 100px; + top: 100px; + background-color: yellow; + } + </style> + <div id="scroller"> + <div id="outer_snap_area" class="snap_area"></div> + <div id="inner_snap_area" class="snap_area"></div> + </div> + <script> + async function reset() { + inner_snap_area.style.height = "100px"; + await waitForCompositorCommit(); + } + + let scroller = document.getElementById("scroller"); + promise_test(async (t) => { + await reset(); + await resetTargetScrollState(t, scroller); + assert_equals(scroller.scrollTop, 0, "test precondition: scroller is " + + "not scrolled"); + let scrollend_promise = waitForScrollendEventNoTimeout(scroller); + let target_snap_position = inner_snap_area.offsetTop + + inner_snap_area.offsetHeight; + // Scroll to an offset close to the bottom of the inner snap area and + // expect to snap to an offset just below this snap area. + scroller.scrollTo(0, target_snap_position - 10); + await scrollend_promise; + assert_equals(scroller.scrollTop, target_snap_position, + "scroller snaps to just below the inner snap area."); + // We are just below the inner snap area. Increase its height so that it + // is larger than the snapport and straddled by the snapport. Verify + // that we snap to its bottom. + inner_snap_area.style.height = + `${scroller.clientHeight + inner_snap_area.clientHeight - 10}px`; + await waitForCompositorCommit(); + target_snap_position = inner_snap_area.offsetTop + + inner_snap_area.offsetHeight - scroller.clientHeight; + assert_equals(scroller.scrollTop, target_snap_position, + "scroller snaps to the bottom of the smaller snap area (which is now " + + "covering)."); + }, "newly larger-than-snapport area is snapped to when straddled close " + + "to bottom."); + + promise_test(async (t) => { + await reset(); + await resetTargetScrollState(t, scroller); + assert_equals(scroller.scrollTop, 0, "test precondition: scroller is " + + "not scrolled"); + let scrollend_promise = waitForScrollendEventNoTimeout(scroller); + let target_snap_position = inner_snap_area.offsetTop + + inner_snap_area.offsetHeight; + // Scroll to an offset close to the bottom of the inner snap area and + // expect to snap to an offset just below this snap area. + scroller.scrollTo(0, target_snap_position - 10); + await scrollend_promise; + assert_equals(scroller.scrollTop, target_snap_position, + "scroller snaps to just below the inner snap area."); + // We are just below the inner snap area. Increase its height so that it + // is larger than the snapport and covers the snapport. Verify that we + // remain in the covering position. + inner_snap_area.style.height = + `${scroller.clientHeight + inner_snap_area.clientHeight + 10}px`; + await waitForCompositorCommit(); + assert_equals(scroller.scrollTop, target_snap_position, + "scroller snaps to the bottom of the smaller snap area (which is now " + + "covering)."); + }, "snapport remains within newly covering snap area when already in " + + "covering position."); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001-ref.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001-ref.html new file mode 100644 index 0000000000..88f028fb39 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001-ref.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<title>CSS Scroll Snap Reference</title> +<style> +html, body { margin: 0; padding: 0; } + +:root { + overflow: hidden; /* hide scrollbars for reftest analysis */ +} + +#target { + position: absolute; + top: 25%; + width: 100%; + margin: 25vh 0; + border-top: solid blue; +} +</style> + +<div id="target"> + <div>Test passes if the blue line above is centered in the viewport.</div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001.html new file mode 100644 index 0000000000..43028cb874 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-001.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<title>scroll-snap-type + scroll-padding propagates root to viewport</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'> +<link rel='match' href='scroll-snap-root-001-ref.html'> +<meta name='assert' + content="Test passes if scroll snap properties on root are applied to viewport."> + +<style type='text/css'> +html, body { margin: 0; padding: 0; } + +:root { + scroll-snap-type: block mandatory; + scroll-padding: 25%; + overflow: hidden; /* hide scrollbars for reftest analysis */ +} + +#fail { + font: bold 2em; + background: red; + height: 120vh; + margin-bottom: 60vh; +} + +#target { + margin-bottom: 120vh; + scroll-margin: 25vh; + scroll-snap-align: start; + border-top: solid blue; +} +</style> + +<div id="fail">FAIL</div> + +<div id="target"> + <div>Test passes if the blue line above is centered in the viewport.</div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002-ref.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002-ref.html new file mode 100644 index 0000000000..663b02b8c4 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002-ref.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<title>CSS Scroll Snap Reference</title> + +<style type='text/css'> +html, body { margin: 0; padding: 0; } + +#target { + border-bottom: solid orange thick; + position: absolute; + bottom: 0; + width: 100%; +} +</style> + +<div id="target"> + <div>Test passes if the orange stripe below is exactly at the bottom of the viewport.</div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002.html new file mode 100644 index 0000000000..302c756341 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-002.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<title>scroll-padding does not propagate body to viewport</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'> +<link rel='match' href='scroll-snap-root-002-ref.html'> +<meta name='assert' + content="Test passes if scroll-snap-padding on body is not applied to viewport."> + +<style type='text/css'> +html, body { margin: 0; padding: 0; } + +:root { + scroll-snap-type: block mandatory; + overflow: hidden; /* hide scrollbars for reftest analysis */ +} + +body { + scroll-padding: 25%; +} + +#fail { + height: 120vh; + font: bold 2em; + background: red; +} + +#target { + margin: 120vh 0; + scroll-snap-align: end; + border-bottom: solid orange thick; +} +</style> + +<div id="fail">FAIL</div> + +<div id="target"> + <div>Test passes if the orange stripe below is exactly at the bottom of the viewport.</div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-003.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-003.html new file mode 100644 index 0000000000..fc7b28fdf5 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-root-003.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<title>scroll-snap-type does not propagate body to viewport</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'> +<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'> +<link rel='match' href='no-red-ref.html'> +<meta name='assert' + content="Test passes if scroll-snap-type on body is not applied to viewport."> + +<style type='text/css'> +:root { + overflow: hidden; /* hide scrollbars for reftest analysis */ +} + +body { + scroll-snap-type: block mandatory; +} + +#pass { + height: 120vh; +} + +#target { + scroll-snap-align: start; + height: 100vh; + background: red; +} +</style> + +<p id="pass">Test passes if there is no red. + +<div id="target"> + <div>FAIL</div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-001.html new file mode 100644 index 0000000000..7d2a228688 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-001.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +#scroller { + width: 400px; + height: 400px; + overflow: scroll; + scroll-snap-type: both mandatory; +} +#space { + left: 0px; + top: 0px; + width: 2100px; + height: 2100px; +} +.target { + width: 50px; + height: 50px; + scroll-snap-align: start; +} +.origin { + left: 0px; + top: 0px; +} +.always-stop { + left: 100px; + top: 0px; + scroll-snap-stop: always; +} +.closer { + left: 200px; + top: 0px; +} +</style> + +<div id="scroller"> + <div id="space"></div> + <div class="target origin"></div> + <div class="target always-stop"></div> + <div class="target closer"></div> +</div> + +<script> +var scroller = document.getElementById("scroller"); +test(() => { + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollBy(300, 0); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); +}, "A scroll with intended direction and end position should not pass a snap " + + "area with scroll-snap-stop: always.") + +test(() => { + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollTo(300, 0); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 0); +}, "A scroll with intended end position should always choose the closest snap " + + "position regardless of the scroll-snap-stop value.") + +// Tests for programmatic scrolls beyond the scroller bounds. + +test(() => { + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollBy(100000, 0); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); +}, "A scroll outside bounds in the snapping axis with intended direction and " + + "end position should not pass a snap area with scroll-snap-stop: always.") + +test(() => { + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollBy(300, -10); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); +}, "A scroll outside bounds in the non-snapping axis with intended direction " + + "and end position should not pass a snap area with scroll-snap-stop: always.") + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002-nested.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002-nested.tentative.html new file mode 100644 index 0000000000..34b6dc97e2 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002-nested.tentative.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop" /> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +.scroller { + width: 400px; + height: 100px; + overflow: scroll; + scroll-snap-type: x mandatory; +} +#space { + left: 0px; + top: 0px; + width: 2100px; + height: 50px; +} +.target { + width: 50px; + height: 50px; + scroll-snap-align: start; + top: 0px; +} +</style> + +<div class="scroller" id="scroller7"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 400px; width: 900px;"></div> + <div class="target" style="left: 900px; scroll-snap-stop: always;"></div +</div> + +<script> + +test(() => { + assert_equals(scroller7.scrollLeft, 0); + assert_equals(scroller7.scrollTop, 0); + + // start dest always + // |-------------------------|================|=====|===== + // 0 400 700 900 + + // Scoll on the element whose snap area is larger than the snapport. + scroller7.scrollBy(600, 0); + // Stops before the smaller snap area @ 900px enters the snapport. + assert_equals(scroller7.scrollLeft + scroller7.clientWidth, 900); + assert_equals(scroller7.scrollTop, 0); + + scroller7.scrollTo(0, 0); + scroller7.scrollBy(800, 0); + // Now the smaller snap area is closer, so we snap to it. + assert_equals(scroller7.scrollLeft, 900); + assert_equals(scroller7.scrollTop, 0); +}, "`scroll-snap-stop: always` snap point is further than the scroll " + + "destination and a snap area covers the snapport"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002.html new file mode 100644 index 0000000000..1aae7aea60 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-002.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop" /> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +.scroller { + width: 400px; + height: 100px; + overflow: scroll; + scroll-snap-type: x mandatory; +} +#space { + left: 0px; + top: 0px; + width: 2100px; + height: 50px; +} +.target { + width: 50px; + height: 50px; + scroll-snap-align: start; + top: 0px; +} +</style> + +<!-- + start dest closest always + |------------------------------|--------|--------| + --> +<div class="scroller" id="scroller1"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <!-- Add `scroll-snap-stop: always` element into the DOM tree prior to the + closest snap point --> + <div class="target" style="left: 120px; scroll-snap-stop: always;"></div> + <!-- Add a snap point closest-to-destination but further than the destination + from the start position --> + <div class="target" style="left: 110px;"></div> +</div> + +<!-- + start closest dest always + |------------------------------|------|--------| + --> +<div class="scroller" id="scroller2"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 120px; scroll-snap-stop: always;"></div> + <!-- Add a snap point closest-to-destination and closer than the destination + from the start position --> + <div class="target" style="left: 95px;"></div> +</div> +</div> + +<!-- + A test case where there's a snap point whose snap area covers the snapport and + there's a `scroll-snap-stop: always` snap point on the opposite side. + --> +<div class="scroller" id="scroller3"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 700px; scroll-snap-stop: always;"></div> + <!-- Add a snap point where the snap area covers the snapport entirely --> + <div class="target" style="left: 100px; width: 500px;"></div> + <!-- Add a snap point where the distance between this and the 100px point is + larger than the snapport size (400px) --> + <div class="target" style="left: 600px;"></div> +</div> + +<!-- + A similar case above, but two `scroll-snap-stop: always` snap points. + --> +<div class="scroller" id="scroller4"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 700px; scroll-snap-stop: always;"></div> + <!-- Add a snap point where the snap area covers the snapport entirely --> + <div class="target" style="left: 100px; width: 500px;"></div> + <!-- Add a snap point where the distance between this and the 100px point is + larger than the snapport size (400px) --> + <div class="target" style="left: 600px; scroll-snap-stop: always;"></div> +</div> + +<!-- + Another similar case, but the nearest snap point to the start point is + `scroll-snap-stop: always`. + --> +<div class="scroller" id="scroller5"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 700px; scroll-snap-stop: always;"></div> + <!-- Add a snap point where the snap area covers the snapport entirely --> + <div class="target" style="left: 100px; width: 500px; scroll-snap-stop: always;"></div> + <!-- Add a snap point where the distance between this and the 100px point is + larger than the snapport size (400px) --> + <div class="target" style="left: 600px;"></div> +</div> + +<!-- + A test case that a `scroll-snap-stop: always` snap point is further than the + scroll destination. + --> +<div class="scroller" id="scroller6"> + <div id="space"></div> + <div class="target" style="left: 0px;"></div> + <div class="target" style="left: 400px;"></div> + <div class="target" style="left: 700px; scroll-snap-stop: always;"></div +</div> + +<script> + +test(() => { + assert_equals(scroller1.scrollLeft, 0); + assert_equals(scroller1.scrollTop, 0); + + // start dest closest always + // |------------------------------|--------|--------| + // 0 100 110 120 + scroller1.scrollBy(100, 0); + assert_equals(scroller1.scrollLeft, 110); + assert_equals(scroller1.scrollTop, 0); +}, "The closest snap point is preferred than scroll-snap-stop: always where " + + "it's further than the destination (the closest one is closer to the " + + "scroll start position than the destination)"); + +test(() => { + assert_equals(scroller2.scrollLeft, 0); + assert_equals(scroller2.scrollTop, 0); + + // start closest dest always + // |------------------------------|------|--------| + // 0 95 100 120 + scroller2.scrollBy(100, 0); + assert_equals(scroller2.scrollLeft, 95); + assert_equals(scroller2.scrollTop, 0); +}, "The closest snap point is preferred than scroll-snap-stop: always where " + + "it's further than the destination (the closest one is futrher than the " + + "destination from the start position)"); + +test(() => { + assert_equals(scroller3.scrollLeft, 0); + assert_equals(scroller3.scrollTop, 0); + + // start dest always + // |-----|===|============================|------| + // 0 100 150 600 700 + + // Scoll on the element whose snap area is larger than the snapport. + scroller3.scrollBy(150, 0); + assert_equals(scroller3.scrollLeft, 150); + assert_equals(scroller3.scrollTop, 0); +}, "The scroll destination on a large element whose snap area covers " + + "the snapport entirely is a valid snap position"); + +test(() => { + assert_equals(scroller4.scrollLeft, 0); + assert_equals(scroller4.scrollTop, 0); + + // start dest always always + // |-----|====|============================|------| + // 0 100 150 600 700 + + // Scoll on the element whose snap area is larger than the snapport. + scroller4.scrollBy(150, 0); + assert_equals(scroller4.scrollLeft, 150); + assert_equals(scroller4.scrollTop, 0); +}, "The scroll destination on a large element whose snap area covers " + + "the snapport entirely is a valid snap position (with two " + + "`scroll-snap-stop: always` snap points"); + +test(() => { + assert_equals(scroller5.scrollLeft, 0); + assert_equals(scroller5.scrollTop, 0); + + // start always dest always + // |-----|=====|============================|------| + // 0 100 150 600 700 + + // Scoll on the element whose snap area is larger than the snapport, but + // the scroll-snap-stop property is `always`. + scroller5.scrollBy(150, 0); + assert_equals(scroller5.scrollLeft, 100); + assert_equals(scroller5.scrollTop, 0); + + // Scroll the direction further, it should NOT be trapped by the + // `scroll-snap-stop: always` snap point. + scroller5.scrollBy(50, 0); + assert_equals(scroller5.scrollLeft, 150); + assert_equals(scroller5.scrollTop, 0); +}, "`scroll-snap-stop: always` snap point is preferred even if the snap area " + + "entire snapport"); + +test(() => { + assert_equals(scroller6.scrollLeft, 0); + assert_equals(scroller6.scrollTop, 0); + + // start dest always + // |-------------------------|-----------------|------| + // 0 400 600 700 + + // Scroll to a point where it's closer to a `scroll-snap-stop: always` snap + // position. + scroller6.scrollBy(600, 0); + assert_equals(scroller6.scrollLeft, 700); + assert_equals(scroller6.scrollTop, 0); +}, "`scroll-snap-stop: always` snap point is further than the scroll " + + "destination"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-change.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-change.html new file mode 100644 index 0000000000..4615487f56 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-change.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop" /> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div, html, body { + margin: 0; + padding: 0; +} + +html { + scroll-snap-type: x mandatory; + overflow: scroll; +} + +#scroller { + scroll-snap-type: x mandatory; + overflow: scroll; + height: 400px; + width: 400px; +} + +.large_space { + width: 2000px; + height: 2000px; +} + +.snap_area { + scroll-snap-align: none start; + width: 100px; + height: 100px; + + background-color: blue; +} + +.snap_area:nth-child(1) { + margin-left: 0; +} + +.snap_area:nth-child(2) { + margin-left: 100px; +} + +.snap_area:nth-child(3) { + margin-left: 300px; +} + +.snap_area:nth-child(4) { + margin-left: 500px; +} +</style> + +<!-- Add snap area to the root scroller --> +<div class="snap_area"></div> +<div class="snap_area"></div> +<div class="snap_area"></div> +<div class="snap_area"></div> + +<div id="scroller"> + <div class="snap_area"></div> + <div class="snap_area"></div> + <div class="snap_area"></div> + <div class="snap_area"></div> + <div class="large_space"></div> +</div> + +<div class="large_space"></div> + +<script> +const scrollers = [document.scrollingElement, document.getElementById("scroller")]; +for (const scroller of scrollers) { + test(_ => { + assert_equals(scroller.scrollLeft, 0); // sanity check + + scroller.querySelectorAll('.snap_area').forEach(area => area.style.scrollSnapStop = 'always'); + scroller.scrollBy(500, 0); + assert_equals(scroller.scrollLeft, 100, "scrollBy should not skip area with stop always"); + + scroller.querySelectorAll('.snap_area').forEach(area => area.style.scrollSnapStop = 'normal'); + scroller.scrollBy(500, 0); + assert_equals(scroller.scrollLeft, 500, "scrollBy should skip secon area with stop normal"); + + // When snap type is changed back to mandatory, scrolling should snap. + scroller.querySelectorAll('.snap_area').forEach(area => area.style.scrollSnapStop = 'always'); + scroller.scrollBy(-500, 0); + assert_equals(scroller.scrollLeft, 300, "scrollBy should not skip area with stop always"); + }, `scroll-snap-stop for areas on ${scroller.nodeName} should control snapping behavior and changing it takes effect`); +} +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-dynamic-crash.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-dynamic-crash.html new file mode 100644 index 0000000000..0039a0168e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-stop-dynamic-crash.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1472537"> +<style> +div { overflow-y: scroll; } +::-webkit-scrollbar { width: 10px; } +::-webkit-scrollbar-corner { } +.crash::-webkit-scrollbar-corner { + scroll-snap-stop: always; +} +</style> +<div id="target"></div> +<script> +document.body.offsetTop; +document.getElementById('target').className = 'crash'; +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-change.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-change.html new file mode 100644 index 0000000000..ed86bace44 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-change.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-type" /> +<meta name="viewport" content="user-scalable=no"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div, html, body { + margin: 0; + padding: 0; +} +html { + margin: 0px; + overflow: scroll; +} +#scroller { + margin-left: 200px; + + height: 300px; + width: 300px; + overflow: scroll; +} +.space { + width: 2000px; + height: 2000px; +} +.snap_area { + margin-left: 200px; + top: 0; + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: none start; +} +</style> + +<!-- Add snap area to the root scroller --> +<div class="snap_area" id="viewport"></div> + +<div id="scroller"> + <div class="snap_area" id="inner"></div> + <div class="space"></div> +</div> + +<div class="space"></div> + +<script> + +const scrollers = [document.scrollingElement, document.getElementById("scroller")]; +for (const scroller of scrollers) { + test(_ => { + scroller.style.scrollSnapType = 'x mandatory'; + scroller.scrollTo(100, 0); + assert_equals(scroller.scrollLeft, 200, "scrolling should snap"); + + // When snap type is 'none' the scroller, scrolling should not snap. + scroller.style.scrollSnapType = 'none'; + scroller.scrollTo(100, 0); + assert_equals(scroller.scrollLeft, 100, "scrolling should not snap"); + + // When snap type is changed back to mandatory, scrolling should snap. + scroller.style.scrollSnapType = 'x mandatory'; + scroller.scrollTo(110, 0); + assert_equals(scroller.scrollLeft, 200, "scrolling should snap after change"); + }, `scroll-snap-type on ${scroller.nodeName} should control snapping behavior and changing it takes effect`); +} +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-on-root-element.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-on-root-element.html new file mode 100644 index 0000000000..a45ac92e5a --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type-on-root-element.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#principal-flow" /> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<style> +body { + height: 8000px; + width: 3000px; + margin: 0px; +} +#target { + position: absolute; + background-color: blue; + top: 2000px; + left: 100px; + + width: 100vw; + height: 100px; +} +</style> +<div id="target"></div> +<script> +const documentHeight = document.documentElement.clientHeight; + +function cleanup() { + document.documentElement.style.scrollSnapType = "none"; + target.style.scrollSnapAlign = ""; + document.body.style.writingMode = ""; + window.scrollTo(0, 0); +} + +test(t => { + t.add_cleanup(cleanup); + document.documentElement.style.scrollSnapType = "y mandatory"; + target.style.scrollSnapAlign = "end none"; + + window.scrollTo(0, 1800); + + // `target y (2000px)` + `target height (100px)` - document height. + assert_equals(document.scrollingElement.scrollTop, 2100 - documentHeight); + assert_equals(document.scrollingElement.scrollLeft, 0, "x should not snap"); +}, "The scroll-snap-type on the root element is applied"); + +test(t => { + t.add_cleanup(cleanup); + + document.documentElement.style.scrollSnapType = "inline mandatory"; + document.body.style.writingMode = "vertical-lr"; + target.style.scrollSnapAlign = "none end"; + + window.scrollTo(200, 1800); + + // Since inline axis is vertical, scrolling viewport vertically on block + // axis should snap. + assert_equals(document.scrollingElement.scrollTop, 2100 - documentHeight, "inline should snap"); + // `target x (100px)`. + assert_equals(document.scrollingElement.scrollLeft, 200, "block should not snap"); +}, "The writing-mode (vertical-lr) on the body is used"); + +test(t => { + t.add_cleanup(cleanup); + + document.documentElement.style.scrollSnapType = "inline mandatory"; + document.body.style.writingMode = "horizontal-tb"; // inline is horizontal + target.style.scrollSnapAlign = "none start"; + + window.scrollTo(200, 1800); + + // Though the target's width is 100vw, there may be room to scroll the window + // within the target because of the scrollbar width and the browser may snap + // to the right edge (instead of the left edge of the target, since it is + // closer to the offset of 200 and still a valid snap point) within this room. + const scrollbar_width = window.innerWidth - document.documentElement.clientWidth; + assert_approx_equals(document.scrollingElement.scrollLeft, 100, scrollbar_width, "inline should snap"); + assert_equals(document.scrollingElement.scrollTop, 1800, "block should not snap"); +}, "The writing-mode (horizontal-tb) on the body is used "); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type.html new file mode 100644 index 0000000000..1577aa7afc --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-snap-type.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-type" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + height: 400px; + width: 400px; + overflow: scroll; +} +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} +#left-top { + left: 0px; + top: 0px; +} +#right-bottom { + left: 1000px; + top: 1000px; +} +</style> + +<div id="scroller"> + <div id="space"></div> + <div class="snap" id="left-top"></div> + <div class="snap" id="right-bottom"></div> +</div> + +<script> +var scroller = document.getElementById("scroller"); +var visible_x = 1000 - scroller.clientWidth; +var visible_y = 1000 - scroller.clientHeight; + +test(() => { + scroller.style.scrollSnapType = "both mandatory"; + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollTo(visible_x + 10, visible_y + 10); + assert_equals(scroller.scrollLeft, 1000); + assert_equals(scroller.scrollTop, 1000); +}, "mandatory scroll-snap-type should snap as long as the element is visible."); + +test(() => { + scroller.style.scrollSnapType = "both proximity"; + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollTo(visible_x + 10, visible_y + 10); + assert_equals(scroller.scrollLeft, visible_x + 10); + assert_equals(scroller.scrollTop, visible_y + 10); +}, "proximity scroll-snap-type shouldn't snap if the snap position is too far away."); + +test(() => { + scroller.style.scrollSnapType = "both proximity"; + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + + scroller.scrollTo(995, 995); + assert_equals(scroller.scrollLeft, 1000); + assert_equals(scroller.scrollTop, 1000); +}, "proximity scroll-snap-type should snap if the snap position is close."); + +test(_ => { + scroller.style.scrollSnapType = "none"; + scroller.scrollTo(100, 100); + assert_equals(scroller.scrollLeft, 100, "scrolling should not snap"); + assert_equals(scroller.scrollTop, 100, "scrolling should not snap"); +}, "none scroll-snap-type shouldn't snap."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-001-ref.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-001-ref.html new file mode 100644 index 0000000000..28b00184c2 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-001-ref.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<title>Reference</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + font: 20px/1 sans-serif; + } + .container > div { + height: 1em; + margin: 1em 0; + background: green; + } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-001.html new file mode 100644 index 0000000000..eeda674e07 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-001.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<title>#target and snap position with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="may"> +<meta name='assert' + content="Test passes if scroll snapping is honored + on a scroll container with 'scroll-snap-type: none' + when navigating to an element with the target fragment ID."> + +<style type='text/css'> + iframe { + border: solid blue 4px; + height: 80px; + width: calc(100% - 8px); + } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<iframe class="container" src="support/scroll-target-align-001-iframe.html#target">This UA doesn't support iframes; please request a custom version of this test!</iframe> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-002.html new file mode 100644 index 0000000000..01db026dff --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-002.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<title>scrollIntoView() and snap position with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="may"> +<meta name='assert' + content="Test passes if scroll snapping is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + explicitly by script."> + +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div { + height: 1em; + } + .container { scroll-padding: .5em 0 0; } /* set up a snap position */ + #target { scroll-margin: .5em 0 0; + scroll-snap-align: center; } + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div id="target"></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-003.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-003.html new file mode 100644 index 0000000000..d13efa0abb --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-align-003.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<title>focus() and snap position with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="may"> +<meta name='assert' + content="Test passes if scroll snapping is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + even if that operation is implied (in this case, by .focus())."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div, a { + height: 1em; + display: block; + outline: none; + } + .container { scroll-padding: .5em 0 0; } /* set up a snap position */ + #target { scroll-margin: .5em 0 0; + scroll-snap-align: center; } + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <a href="" id="target"></a> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').focus(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-001.html new file mode 100644 index 0000000000..8ddbbcec5f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-001.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<title>#target and scroll-margin with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="should"> +<meta name='assert' + content="Test passes if scroll-margin is honored + on a scroll container with 'scroll-snap-type: none' + when navigating to an element with the target fragment ID."> + +<style type='text/css'> + iframe { + border: solid blue 4px; + height: 80px; + width: calc(100% - 8px); + } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<iframe class="container" src="support/scroll-target-margin-001-iframe.html#target">This UA doesn't support iframes; please request a custom version of this test!</iframe> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-002.html new file mode 100644 index 0000000000..51cf553b36 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-002.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<title>scrollIntoView() and scroll-margin with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="should"> +<meta name='assert' + content="Test passes if scroll-margin is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + explicitly by script."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div { + height: 1em; + } + #target { scroll-margin: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div id="target"></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-003.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-003.html new file mode 100644 index 0000000000..2ea8eff67c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-003.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html> +<title>focus() and scroll-margin with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="should"> +<meta name='assert' + content="Test passes if scroll-margin is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + even if that operation is implied (in this case, by .focus())."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div, a { + height: 1em; + display: block; + outline: none; + } + #target { scroll-margin: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <a href="" id="target"></a> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').focus(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-004.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-004.html new file mode 100644 index 0000000000..ac87c8684d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-004.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<title>scroll-margin on elements with 'display: table;'</title> +<link rel='author' title='Jan Grosser' href='mailto:jan_firefoxdev@t-online.de'> +<link rel='help' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1633192'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name="flags" content="should"> +<meta name='assert' content="Test passes if scroll-margin is honored on elements with 'display: table'."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div { + height: 1em; + } + #target { scroll-margin: 2em 0 1em; display: table; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div id="target"></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-005.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-005.html new file mode 100644 index 0000000000..9296202354 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-005.html @@ -0,0 +1,36 @@ +<!doctype html> +<title>scroll-margin on input widget</title> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1729292"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #target { + scroll-margin-top: 200px; + } + .padding { + height: 5000px; + } +</style> + +<div id="container"> + <div class="padding"></div> + <input type="date" id="target"> + <div class="padding"></div> +</div> + +<script> +promise_test(async function() { + document.scrollingElement.scrollTo(0, 20000); + await new Promise(resolve => { + document.addEventListener("scroll", resolve, { once: true }); + document.getElementById("target").focus(); + }); + // Should be around 5000 - 200px of margin - (window.innerHeight / 2) + const targetPos = 4900 - (window.innerHeight / 2); + assert_between_exclusive(document.scrollingElement.scrollTop, targetPos - 300, + targetPos + 300, "Should honor date input scroll-margin"); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-006.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-006.html new file mode 100644 index 0000000000..48e246249f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-margin-006.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<title>scrollIntoView() and scroll-margin applied to an inline element</title> +<link rel='author' title='Martin Robinson' href='http://igalia.com'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style type='text/css'> + .container { + border: solid black 1px; + height: 40px; + width: 40px; + overflow: auto; + } +</style> + +<div class="container"> + <div style="height: 1000px; width: 2000px;"></div> + <div style="width: 2000px;"> + <span>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span> + <span id="target">TARGETTARGETTARGETTARGET</span> + <span>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span> + </div> + <div style="height: 1000px; width: 2000px;"></div> +</div> + +<script> + +test(() => { + target.scrollIntoView(); + const scroller = target.parentElement.parentElement; + let expectedScrollPosition = [scroller.scrollLeft - 20, scroller.scrollTop - 20]; + scroller.scrollTo(0, 0); + + target.style.scrollMarginTop = "20px"; + target.style.scrollMarginLeft = "20px"; + target.scrollIntoView(); + assert_equals(scroller.scrollLeft, expectedScrollPosition[0]); + assert_equals(scroller.scrollTop, expectedScrollPosition[1]); + + target.style.scrollMarginTop = "0px"; + target.style.scrollMarginLeft = "0px"; + + scroller.scrollTo(2000, 2000); + target.scrollIntoView({"block": "end", "inline": "end"}); + expectedScrollPosition = [scroller.scrollLeft + 20, scroller.scrollTop + 20]; + scroller.scrollTo(2000, 2000); + + target.style.scrollMarginBottom = "20px"; + target.style.scrollMarginRight = "20px"; + target.scrollIntoView({"block": "end", "inline": "end"}); + assert_equals(scroller.scrollLeft, expectedScrollPosition[0]); + assert_equals(scroller.scrollTop, expectedScrollPosition[1]); + +}, "scroll-margin is taken into account when scrolling an inline element into view"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-001.html new file mode 100644 index 0000000000..5cd4fddcc5 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-001.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<title>#target and scroll-padding with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-padding'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll-padding is honored + on a scroll container with 'scroll-snap-type: none' + when navigating to an element with the target fragment ID."> +<style type='text/css'> + iframe { + border: solid blue 4px; + height: 80px; + width: calc(100% - 8px); + } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<iframe class="container" src="support/scroll-target-padding-001-iframe.html#target">This UA doesn't support iframes; please request a custom version of this test!</iframe> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-002.html new file mode 100644 index 0000000000..fbed1e132e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-002.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<title>scrollIntoView() and scroll-padding with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-padding'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll-padding is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + explicitly by script."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div { + height: 1em; + } + .container { scroll-padding: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div id="target"></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-003.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-003.html new file mode 100644 index 0000000000..ccbe7b0ec9 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-padding-003.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> +<title>focus() and scroll-padding with snapping off (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#scroll-margin'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll-padding is honored + on a scroll container with 'scroll-snap-type: none' + when scrolling an element into view + even if that operation is implied (in this case, by .focus())."> +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div, a { + height: 1em; + display: block; + outline: none; + } + .container { scroll-padding: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <a href="" id="target"></a> + <div></div> + <div class="fail">FAIL</div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').focus(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-001.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-001.html new file mode 100644 index 0000000000..76d3222a0b --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-001.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<title>#target and snap position with snapping on (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll snapping is honored + when navigating to an element with the target fragment ID."> + +<style type='text/css'> + iframe { + border: solid blue 4px; + height: 80px; + width: calc(100% - 8px); + } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<iframe class="container" src="support/scroll-target-snap-001-iframe.html#target">This UA doesn't support iframes; please request a custom version of this test!</iframe> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-002.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-002.html new file mode 100644 index 0000000000..6e928a435e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-002.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> +<title>scrollIntoView() and snap position with snapping on (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll snapping is honored + when scrolling an element into view + explicitly by script."> + +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + scroll-snap-type: block; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div { + height: 1em; + } + /* Note: we use "start" for #target to avoid spec ambiguity where + * scroll-snap-align conflicts with default ScrollIntoViewOptions. + * See https://github.com/w3c/csswg-drafts/issues/9576. + */ + #target { scroll-margin: 2em 0 0; + scroll-snap-align: start; } /* set up a snap position */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* Try to foil the UA */ + .foilup { margin-bottom: -1em; scroll-snap-align: start; } + .foildn { margin-top: -1em; scroll-snap-align: end; } + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="foilup"></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div class="foilup"></div> + <div id="target"></div> + <div class="foildn"></div> + <div></div> + <div class="fail">FAIL</div> + <div class="foildn"></div> + <div></div> + <div class="foildn"></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').scrollIntoView(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-003.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-003.html new file mode 100644 index 0000000000..6fe3901e51 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-target-snap-003.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html> +<title>focus() and snap position with snapping on (y-axis)</title> +<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'> +<link rel='help' href='https://www.w3.org/TR/css-scroll-snap-1/#choosing'> +<link rel='match' href='scroll-target-001-ref.html'> +<meta name='assert' + content="Test passes if scroll snapping is honored + when scrolling an element into view + even if that operation is implied (in this case, by .focus())."> + +<style type='text/css'> + .container { + border: solid blue 4px; + height: 4em; + overflow: auto; + scroll-snap-type: block; + + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + .container > div, a { + height: 1em; + display: block; + outline: none; + } + #target { scroll-margin: 1em 0 0; + scroll-snap-align: center; } /* set up a snap position */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* Try to foil the UA */ + .foilup { margin-bottom: -1em; scroll-snap-align: start; } + .foildn { margin-top: -1em; scroll-snap-align: end; } + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div id='instructions'>Test passes if there is a green stripe across the second quarter of the box below and no red.</div> + +<div class="container"> + <div></div> + <div></div> + <div></div> + <div></div> + <div class="foilup"></div> + <div class="fail">FAIL</div> + <div></div> + <div id="stripe"></div> + <div class="foilup"></div> + <a href="" id="target"></a> + <div class="foildn"></div> + <div></div> + <div class="fail">FAIL</div> + <div class="foildn"></div> + <div></div> + <div class="foildn"></div> + <div></div> + <div></div> + <div></div> +</div> + +<script> + document.getElementById('target').focus(); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/scrollTo-scrollBy-snaps.html b/testing/web-platform/tests/css/css-scroll-snap/scrollTo-scrollBy-snaps.html new file mode 100644 index 0000000000..6013de5044 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scrollTo-scrollBy-snaps.html @@ -0,0 +1,160 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<meta name="viewport" content="user-scalable=no"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +html { + margin: 0px; + overflow: scroll; + scroll-snap-type: both mandatory; +} +div { + position: absolute; +} +.scroller { + overflow: scroll; + scroll-snap-type: both mandatory; +} +#inner-scroller { + top: 3000px; + width: 800px; + height: 800px; +} +.space { + left: 0px; + top: 0px; + width: 4000px; + height: 4000px; +} +.target { + width: 600px; + height: 600px; + scroll-snap-align: start; +} + +.left { + left: 0px; +} +.right { + left: 1000px; +} +.top { + top: 0px; +} +.bottom { + top: 1000px; +} +</style> +<body class="scroller"> + <div class="space"></div> + <div class="target left top"></div> + <div class="target right top"></div> + <div class="target left bottom"></div> + <div class="target right bottom"></div> + <div class="scroller" id="inner-scroller"> + <div class="space"></div> + <div class="target left top"></div> + <div class="target right top"></div> + <div class="target left bottom"></div> + <div class="target right bottom"></div> + </div> +</body> + +<script> +function format_dict(dict) { + const props = []; + for (let prop in dict) { + props.push(`${prop}: ${format_value(dict[prop])}`); + } + return `{${props.join(', ')}}`; +} + +var divScroller = document.getElementById("inner-scroller"); +var viewport = document.scrollingElement; +[ + [{left: 800}, 1000, 0], + [{top: 900}, 0, 1000], + [{left: 900, top: 800}, 1000, 1000], + [{left: 800, top: -100}, 1000, 0], /* outside bounds on y axis */ + [{left: 10000, top: -100}, 1000, 0] /* outside bounds on both axes */ +].forEach(([input, expectedX, expectedY]) => { + test(() => { + divScroller.scrollTo(0, 0); + assert_equals(divScroller.scrollLeft, 0); + assert_equals(divScroller.scrollTop, 0); + if (input.left) + divScroller.scrollLeft = input.left; + if (input.top) + divScroller.scrollTop = input.top; + assert_equals(divScroller.scrollLeft, expectedX); + assert_equals(divScroller.scrollTop, expectedY); + }, `assign scrollLeft and scrollTop for ${format_dict(input)} on div lands on (${expectedX}, ${expectedY})`); + + test(() => { + viewport.scrollTo(0, 0); + assert_equals(viewport.scrollLeft, 0); + assert_equals(viewport.scrollTop, 0); + if (input.left) + viewport.scrollLeft = input.left; + if (input.top) + viewport.scrollTop = input.top; + assert_equals(viewport.scrollLeft, expectedX); + assert_equals(viewport.scrollTop, expectedY); + }, `assign scrollLeft and scrollTop for ${format_dict(input)} on viewport-defining element lands on (${expectedX}, ${expectedY})`); + + test(() => { + divScroller.scrollTo(0, 0); + assert_equals(divScroller.scrollLeft, 0); + assert_equals(divScroller.scrollTop, 0); + divScroller.scrollTo(input); + assert_equals(divScroller.scrollLeft, expectedX); + assert_equals(divScroller.scrollTop, expectedY); + }, `scrollTo(${format_dict(input)}) on div lands on (${expectedX}, ${expectedY})`); + + test(() => { + divScroller.scrollTo(0, 0); + assert_equals(divScroller.scrollLeft, 0); + assert_equals(divScroller.scrollTop, 0); + divScroller.scrollBy(input); + assert_equals(divScroller.scrollLeft, expectedX); + assert_equals(divScroller.scrollTop, expectedY); + }, `scrollBy(${format_dict(input)}) on div lands on (${expectedX}, ${expectedY})`); + + test(() => { + viewport.scrollTo(0, 0); + assert_equals(viewport.scrollLeft, 0); + assert_equals(viewport.scrollTop, 0); + viewport.scrollTo(input); + assert_equals(viewport.scrollLeft, expectedX); + assert_equals(viewport.scrollTop, expectedY); + }, `scrollTo(${format_dict(input)}) on viewport-defining element lands on (${expectedX}, ${expectedY})`); + + test(() => { + viewport.scrollTo(0, 0); + assert_equals(viewport.scrollLeft, 0); + assert_equals(viewport.scrollTop, 0); + viewport.scrollBy(input); + assert_equals(viewport.scrollLeft, expectedX); + assert_equals(viewport.scrollTop, expectedY); + }, `scrollBy(${format_dict(input)}) on viewport-defining element lands on (${expectedX}, ${expectedY})`); + + test(() => { + window.scrollTo(0, 0); + assert_equals(window.scrollX, 0); + assert_equals(window.scrollY, 0); + window.scrollTo(input); + assert_equals(window.scrollX, expectedX); + assert_equals(window.scrollY, expectedY); + }, `scrollTo(${format_dict(input)}) on window lands on (${expectedX}, ${expectedY})`); + + test(() => { + window.scrollTo(0, 0); + assert_equals(window.scrollX, 0); + assert_equals(window.scrollY, 0); + window.scrollBy(input); + assert_equals(window.scrollX, expectedX); + assert_equals(window.scrollY, expectedY); + }, `scrollBy(${format_dict(input)}) on window lands on (${expectedX}, ${expectedY})`); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/selection-target.html b/testing/web-platform/tests/css/css-scroll-snap/selection-target.html new file mode 100644 index 0000000000..1c8435ea6e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/selection-target.html @@ -0,0 +1,49 @@ +<!doctype html> +<meta charset=utf-8> +<title>scroll-padding is respected when typing into an out-of-view textfield</title> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1701928"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-padding"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<style> + body { + margin: 0; + } + :root { + scroll-padding-top: 100px; + } +</style> +<div style="height: 300vh;"></div> +<input type="text"> +<div style="height: 300vh;"></div> +<script> +function tick() { + return new Promise(resolve => { + requestAnimationFrame(() => requestAnimationFrame(resolve)); + }); +} + +promise_test(async function() { + let input = document.querySelector("input"); + input.focus(); + await tick(); + // Scroll out of view. + scrollTo(0, document.scrollingElement.scrollHeight); + await tick(); + assert_not_equals(window.scrollY, 0); + assert_true(input.getBoundingClientRect().bottom < 0, "Should be offscreen"); + // NOTE(emilio): Using test_driver.Actions() instead of test_driver.send_keys + // because the later scrolls the target into view which would defeat the + // point of the test... + await new test_driver.Actions().keyDown('a').send(); + await tick(); + // We assert the bottom rather than the top because Gecko scrolls the + // selection bounds into view, not the whole input. + assert_true(input.getBoundingClientRect().bottom > 100, "Scroll-padding should be respected"); +}, "Test scrolling into view when typing"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/direction-rtl.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/direction-rtl.html new file mode 100644 index 0000000000..85724c31fe --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/direction-rtl.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title> + Scrollers should snap to the closest snap point on initial layout (using 'direction: rtl') +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<link rel="match" href="snap-after-initial-layout-ref.html" /> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; + direction: rtl; +} + +#close-target { + width: 200px; + height: 200px; + border: solid green 50px; + top: 50px; + right: 150px; + margin: 50px; + background-color: green; + scroll-snap-align: start; +} + +#far-target { + width: 300px; + height: 300px; + top: 100px; + right: 700px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="close-target"></div> + <div id="far-target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000-ref.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000-ref.html new file mode 100644 index 0000000000..f3eaa06ac9 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000-ref.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<title> + CSS Scroll Snap Reference +</title> +<style> + +.scroller { + width: 100px; + height: 100px; + border: solid blue; + margin: 10px; + display: inline-block; +} + +.scroller > div { + width: 30px; + height: 30px; + background: orange; +} + +.proxfar { + border-color: orange; +} + +</style> + +<p>Test passes if there is an orange square precisely at the top left corner of each blue box (no gap), +and each orange box is empty. + + +<div class="mandatory"> + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> + + <!-- on-screen --> + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> +</div> + +<div class="proximity"> + <!-- off-screen --> + <div class="scroller proxfar"> + </div> + + <div class="scroller proxfar"> + </div> + + <div class="scroller proxfar"> + </div> + + <!-- on-screen --> + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> + + <div class="scroller"> + <div></div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000.html new file mode 100644 index 0000000000..ea47c9f36c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-initial-layout-000.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<title> + On-screen vs. Off-screen Snapped Initial Scroll Position (Mandatory and Proximity) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap"> +<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact"> +<!-- Test assumes 2px proximity is enough to snap. + If your implementation has a cogent argument as to why this is too much, + please request a change to this test. ~fantasai --> +<link rel="match" href="scroll-snap-initial-layout-000-ref.html"> + +<style> + +.scroller { + scroll-snap-type: both mandatory; + overflow: hidden; + scroll-padding: 0; + width: 100px; + height: 100px; + border: solid blue; + margin: 10px; + display: inline-block; +} + +.mandatory > .scroller { + scroll-snap-type: both mandatory; +} + +.proximity > .scroller { + scroll-snap-type: both proximity; +} + +.scroller > div { + /* padding wrapper */ + width: 30px; +} + +.scroller > div > div { + /* target box */ + height: 30px; + background: orange; + scroll-snap-align: start; +} + +.proxfar { + border-color: orange; +} +.proxfar > div > div { + background: red; +} + +</style> + +<p>Test passes if there is an orange square precisely at the top left corner of each blue box (no gap), +and each orange box is empty. + + +<div class="mandatory"> + <!-- off-screen --> + <div class="scroller"> + <div style="padding: 110px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding-block: 110px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding-inline: 110px;"><div class="small-target"></div></div> + </div> + + <!-- on-screen --> + <div class="scroller"> + <div style="padding: 90px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding-block: 90px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding-inline: 90px;"><div class="small-target"></div></div> + </div> +</div> + +<div class="proximity"> + <!-- off-screen --> + <div class="scroller proxfar"> + <div style="padding: 110px;"><div class="small-target"></div></div> + </div> + + <div class="scroller proxfar"> + <div style="padding-block: 110px;"><div class="small-target"></div></div> + </div> + + <div class="scroller proxfar"> + <div style="padding-inline: 110px;"><div class="small-target"></div></div> + </div> + + <!-- on-screen --> + <div class="scroller"> + <div style="padding: 2px 110px 110px 2px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding: 2px 110px 110px 2px;"><div class="small-target"></div></div> + </div> + + <div class="scroller"> + <div style="padding: 2px 110px 110px 2px;"><div class="small-target"></div></div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000-ref.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000-ref.html new file mode 100644 index 0000000000..428eb3facb --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000-ref.html @@ -0,0 +1,188 @@ +<!DOCTYPE html> +<title> + CSS Scroll Snap Reference +</title> + +<style> + +.wrapper { + /* lay out in a nice grid */ + display: grid; + gap: 0.25em; + grid-template-columns: repeat(6, max-content); +} + +.scroller { + width: 50px; + height: 50px; + border: solid silver; + border-block-start-color: blue; + border-inline-start-color: blue; + position: relative; +} + +.target { + width: 30px; + height: 30px; + background: orange; + top: 0; left: 0; right: 0; bottom: 0; + position: absolute; +} + +.TB { writing-mode: horizontal-tb; } +.LR { writing-mode: vertical-lr; } +.RL { writing-mode: vertical-rl; } +.ltr { direction: ltr; } +.rtl { direction: rtl; } + +/* not absolutizing the border colors, so that the test passes even if css-logical is not supported; */ +.large.invert { + border: solid silver; + border-block-end-color: blue; + border-inline-end-color: blue; +} + +.TB.large.invert .target::before { top: auto; } +.LR.large.invert .target::before { left: auto; } +.RL.large.invert .target::before { right: auto; } + +.TB.ltr.large.invert .target::before { left: auto; } +.TB.rtl.large.invert .target::before { right: auto; } +.LR.ltr.large.invert .target::before { top: auto; } +.LR.rtl.large.invert .target::before { bottom: auto; } +.RL.ltr.large.invert .target::before { top: auto; } +.RL.rtl.large.invert .target::before { bottom: auto; } + +.large.invert .target::before { + width: 9px; + height: 9px; + background: orange; + top: 0; left: 0; right: 0; bottom: 0; + position: absolute; + content: ''; +} + +.large.invert .target { + display: block; + background: none; + width: 30px; + height: 30px; + border-block-start: 20px solid red; + border-inline-start: 20px solid red; +} + +</style> + +<p>Test passes if there is an orange square tucked into each blue corner without gaps, +and there is no red, except for the large inverted cases which should have red +in the silver corner and smaller orange boxes in the blue corner. + +<div class="wrapper"> +<!-- Simple Small Cases --> + +<div class="scroller TB ltr small"> + <div class="target"></div> +</div> + +<div class="scroller LR ltr small"> + <div class="target"></div> +</div> + +<div class="scroller RL ltr small"> + <div class="target"></div> +</div> + +<div class="scroller TB rtl small"> + <div class="target"></div> +</div> + +<div class="scroller LR rtl small"> + <div class="target"></div> +</div> + +<div class="scroller RL rtl small"> + <div class="target"></div> +</div> + +<!-- Target-inverted Small Cases + This row should be identical to the previous. --> + +<div class="scroller TB ltr small invert"> + <div class="target"></div> +</div> + +<div class="scroller LR ltr small invert"> + <div class="target"></div> +</div> + +<div class="scroller RL ltr small invert"> + <div class="target"></div> +</div> + +<div class="scroller TB rtl small invert"> + <div class="target"></div> +</div> + +<div class="scroller LR rtl small invert"> + <div class="target"></div> +</div> + +<div class="scroller RL rtl small invert"> + <div class="target"></div> +</div> + +<!-- Simple Large Cases --> + +<div class="scroller TB ltr large"> + <div class="target"></div> +</div> + +<div class="scroller LR ltr large"> + <div class="target"></div> +</div> + +<div class="scroller RL ltr large"> + <div class="target"></div> +</div> + +<div class="scroller TB rtl large"> + <div class="target"></div> +</div> + +<div class="scroller LR rtl large"> + <div class="target"></div> +</div> + +<div class="scroller RL rtl large"> + <div class="target"></div> +</div> + +<!-- Target-inverted Large Cases + This is the fun one. --> + +<div class="scroller TB ltr large invert"> + <div class="target"></div> +</div> + +<div class="scroller LR ltr large invert"> + <div class="target"></div> +</div> + +<div class="scroller RL ltr large invert"> + <div class="target"></div> +</div> + +<div class="scroller TB rtl large invert"> + <div class="target"></div> +</div> + +<div class="scroller LR rtl large invert"> + <div class="target"></div> +</div> + +<div class="scroller RL rtl large invert"> + <div class="target"></div> +</div> + +</div> <!-- wrapper --> + diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000.html new file mode 100644 index 0000000000..f44317c858 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000.html @@ -0,0 +1,238 @@ +<!DOCTYPE html> +<title> + scroll-snap-align vs writing-mode +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap"> +<link rel="match" href="scroll-snap-writing-mode-000-ref.html"> +<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact"> +<style> + +.wrapper { + /* lay out in a nice grid */ + display: grid; + gap: 0.25em; + grid-template-columns: repeat(6, max-content); +} + +.scroller { + scroll-snap-type: both mandatory; + overflow: hidden; + scroll-padding: 0; + width: 50px; + height: 50px; + border: solid silver; + border-block-start-color: blue; + border-inline-start-color: blue; +} +.area { + width: 200px; + height: 200px; +} + +.target { + margin: 5px; + scroll-snap-align: start; +} + +.small .target { + width: 30px; + height: 30px; + background: orange; +} + +.large .target { + width: 51px; + height: 51px; + border-block-end: 20px solid red; + border-inline-end: 20px solid red; +} + +.large .target::before { + content: ''; + display: block; + width: 30px; + height: 30px; + background: orange; +} + +.TB { writing-mode: horizontal-tb; } +.LR { writing-mode: vertical-lr; } +.RL { writing-mode: vertical-rl; } +.ltr { direction: ltr; } +.rtl { direction: rtl; } + +.TB.ltr.invert .target { writing-mode: vertical-rl; direction: rtl; } +.TB.rtl.invert .target { writing-mode: vertical-lr; direction: rtl; } +.LR.ltr.invert .target { writing-mode: vertical-rl; direction: rtl; } +.LR.rtl.invert .target { writing-mode: vertical-rl; direction: ltr; } +.RL.ltr.invert .target { writing-mode: vertical-lr; direction: rtl; } +.RL.rtl.invert .target { writing-mode: horizontal-tb; direction: ltr; } + +.large.invert { + /* key off target‘s writing mode, which we just inverted */ + border: solid silver; + border-block-end-color: blue; + border-inline-end-color: blue; +} +</style> + +<p>Test passes if there is an orange square tucked into each blue corner without gaps, + and there is no red, except for the large inverted cases which should have red + in the silver corner and smaller orange boxes in the blue corner. + +<div class="wrapper"> +<!-- Simple Small Cases --> + +<div class="scroller TB ltr small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR ltr small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL ltr small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller TB rtl small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR rtl small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL rtl small"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<!-- Target-inverted Small Cases + This row should be identical to the previous. --> +<div class="scroller TB ltr small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR ltr small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL ltr small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller TB rtl small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR rtl small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL rtl small invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<!-- Simple Large Cases --> + +<div class="scroller TB ltr large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR ltr large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL ltr large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller TB rtl large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR rtl large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL rtl large"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<!-- Target-inverted Large Cases + This is the fun one. --> + +<div class="scroller TB ltr large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR ltr large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL ltr large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller TB rtl large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller LR rtl large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +<div class="scroller RL rtl large invert"> + <div class="area"> + <div class="target"></div> + </div> +</div> + +</div> <!-- wrapper --> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/snap-after-initial-layout-ref.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/snap-after-initial-layout-ref.html new file mode 100644 index 0000000000..c8009b626c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/snap-after-initial-layout-ref.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<title>Reference</title> +<style> +div { + margin: 0; + position: absolute; +} + +#target { + width: 300px; + height: 300px; + top: 0; + left: 200px; + background-color: green; +} +</style> + +<div> + <div id="target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-horizontal-tb.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-horizontal-tb.html new file mode 100644 index 0000000000..9a680d10d9 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-horizontal-tb.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<title> + Scrollers should snap to the closest snap point on initial layout + (using 'writing-mode: horizontal-tb') +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<link rel="match" href="snap-after-initial-layout-ref.html" /> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; + writing-mode: horizontal-tb; +} + +#close-target { + width: 200px; + height: 200px; + border: solid green 50px; + top: 50px; + left: 150px; + margin: 50px; + background-color: green; + scroll-snap-align: start end; +} + +#far-target { + width: 300px; + height: 300px; + top: 100px; + left: 500px; + background-color: red; + scroll-snap-align: start end; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="close-target"></div> + <div id="far-target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-lr.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-lr.html new file mode 100644 index 0000000000..f4de0411e0 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-lr.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<title> + Scrollers should snap to the closest snap point on initial layout + (using 'writing-mode: vertical-lr') +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<link rel="match" href="snap-after-initial-layout-ref.html" /> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; + writing-mode: vertical-lr; +} + +#close-target { + width: 200px; + height: 200px; + border: solid green 50px; + top: 50px; + left: 150px; + margin: 50px; + background-color: green; + scroll-snap-align: end start; +} + +#far-target { + width: 300px; + height: 300px; + top: 100px; + left: 500px; + background-color: red; + scroll-snap-align: end start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="close-target"></div> + <div id="far-target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-rl.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-rl.html new file mode 100644 index 0000000000..1710bc16dd --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-initial-layout/writing-mode-vertical-rl.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title> + Scrollers should snap to the closest snap point on initial layout + (using 'writing-mode: vertical-rl') +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<link rel="match" href="snap-after-initial-layout-ref.html" /> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + /* Chrome bug using 'position:absolute' with LayoutNG disabled */ + position: relative; + + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; + writing-mode: vertical-rl; +} + +#close-target { + width: 200px; + height: 200px; + border: solid green 50px; + top: 50px; + left: 150px; + margin: 50px; + background-color: green; + scroll-snap-align: start; +} + +#far-target { + width: 300px; + height: 300px; + top: 100px; + left: 500px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="close-target"></div> + <div id="far-target"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-only-snap-area.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-only-snap-area.html new file mode 100644 index 0000000000..53141707bb --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-only-snap-area.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title> + Adding a new snap area when there are none should make the scroller snap to it. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#target { + width: 300px; + height: 300px; + top: 100px; + left: 100px; + background-color: green; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="target"></div> +</div> + +<script> +const target = document.getElementById("target"); +const scroller = document.getElementById("scroller"); + +test(() => { + scroller.removeChild(target); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.appendChild(target); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); +}, "Adding a new snap area when there are none should make the scroller snap to it."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-snap-area-while-snapped.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-snap-area-while-snapped.html new file mode 100644 index 0000000000..d26359658f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/adding-snap-area-while-snapped.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title> + Adding a new snap area while already snapped should not make the scroller snap to it. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#initial-target { + width: 300px; + height: 300px; + top: 100px; + left: 100px; + background-color: green; + scroll-snap-align: start; +} + +#other-target { + width: 300px; + height: 300px; + top: 300px; + left: 300px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="initial-target"></div> + <div id="other-target"></div> +</div> + +<script> +const initial_target = document.getElementById("initial-target"); +const other_target = document.getElementById("other-target"); +const scroller = document.getElementById("scroller"); + +test(() => { + scroller.removeChild(other_target); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + scroller.appendChild(other_target); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); +}, "Adding a new snap area while already snapped should not make the scroller snap to it."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align-nested.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align-nested.tentative.html new file mode 100644 index 0000000000..ddea570551 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align-nested.tentative.html @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<title> + Updating the snap alignment of a snap container's content should make the snap + container resnap accordingly. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 200px; + width: 200px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#initial-target { + width: 300px; + height: 300px; + top: 100px; + left: 100px; + background-color: green; + scroll-snap-align: start; +} + +#other-target { + width: 300px; + height: 300px; + top: 300px; + left: 300px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} + +.snap-area { + scroll-snap-align: start !important; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="initial-target"></div> + <div id="other-target"></div> +</div> + +<script> +const initial_target = document.getElementById("initial-target"); +const other_target = document.getElementById("other-target"); +const scroller = document.getElementById("scroller"); + +function cleanup() { + initial_target.style.setProperty("scroll-snap-align", "start"); + other_target.style.setProperty("scroll-snap-align", "start"); + initial_target.removeAttribute("class"); +} + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("scroll-snap-align", "end"); + // scroller maintains scroll position which is still valid as the target's + // snap area covers the snap port. + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); +}, "Changing a large target's snap alignment shouldn't make the scroller" + + " resnap if the scroller is already in a valid snap position."); + +// Similar to above test case except targets are too small to cover snap port, +// so scroller must snap in response to change in scroll-snap-align. +test(t => { + t.add_cleanup(cleanup); + const initial_target_height = initial_target.offsetHeight; + const initial_target_width = initial_target.offsetWidth; + const other_target_height = initial_target.offsetHeight; + const other_target_width = initial_target.offsetWidth; + t.add_cleanup(() => { + initial_target.style.setProperty("height", `${initial_target_height}px`); + initial_target.style.setProperty("width", `${initial_target_width}px`); + other_target.style.setProperty("height", `${other_target_height}px`); + other_target.style.setProperty("width", `${other_target_width}px`); + }) + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("height", `${scroller.clientHeight * 2/3 }px`); + initial_target.style.setProperty("width", `${scroller.clientWidth * 2/3 }px`); + other_target.style.setProperty("height", `${scroller.clientHeight * 2/3 }px`); + other_target.style.setProperty("width", `${scroller.clientWidth * 2/3 }px`); + + // scroll (and snap) to top left of other target. + scroller.scrollTo(other_target.offsetTop, + other_target.offsetLeft); + assert_equals(scroller.scrollTop, other_target.offsetTop,); + assert_equals(scroller.scrollLeft, other_target.offsetLeft); + + other_target.style.setProperty("scroll-snap-align", "end"); + // should be scrolled so as to align scroller's bottom-right with + // other_target's bottom-right. + assert_equals(scroller.scrollTop, + other_target.offsetTop + other_target.offsetHeight - scroller.clientHeight); + assert_equals(scroller.scrollLeft, + other_target.offsetLeft + other_target.offsetWidth - scroller.clientWidth); +}, "Changing the current (non-covering) target's snap alignment should make " + + "the scroller snap according to the new alignment."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align.html new file mode 100644 index 0000000000..a625621c27 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-align.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<title> + Updating the snap alignment of a snap container's content should make the snap + container resnap accordingly. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 200px; + width: 200px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#initial-target { + width: 300px; + height: 300px; + top: 100px; + left: 100px; + background-color: green; + scroll-snap-align: start; +} + +#other-target { + width: 300px; + height: 300px; + top: 300px; + left: 300px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} + +.snap-area { + scroll-snap-align: start !important; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="initial-target"></div> + <div id="other-target"></div> +</div> + +<script> +const initial_target = document.getElementById("initial-target"); +const other_target = document.getElementById("other-target"); +const scroller = document.getElementById("scroller"); + +function cleanup() { + initial_target.style.setProperty("scroll-snap-align", "start"); + other_target.style.setProperty("scroll-snap-align", "start"); + initial_target.removeAttribute("class"); +} + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("scroll-snap-align", "none"); + assert_equals(scroller.scrollTop, 300); + assert_equals(scroller.scrollLeft, 300); +}, "Removing the current target's snap alignment should make the scroller" ++ " resnap to a new snap area."); + +test(t => { + t.add_cleanup(cleanup); + initial_target.style.setProperty("scroll-snap-align", "none"); + other_target.style.setProperty("scroll-snap-align", "none"); + + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + initial_target.style.setProperty("scroll-snap-align", "start"); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); +}, "Changing an element snap alignment from none to start should make the" ++ "scroller resnap."); + +test(t => { + t.add_cleanup(cleanup); + initial_target.style.setProperty("scroll-snap-align", "none"); + other_target.style.setProperty("scroll-snap-align", "none"); + + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + initial_target.classList.add("snap-area"); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); +}, "Changing an element snap alignment from none to start by adding a class" ++ " should make the scroller resnap."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type-on-root-element.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type-on-root-element.html new file mode 100644 index 0000000000..c86f39b9d6 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type-on-root-element.html @@ -0,0 +1,95 @@ +<!DOCTYPE html> +<html> +<title> + Updating the scroll-snap-type of the root element should make it resnap accordingly. + This is another vairant of changing-scroll-snap-type.html for the root element. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +html { + overflow: hidden; + scroll-snap-type: none; +} + +#y-target { + width: 300px; + height: 300px; + top: 100px; + left: 0; + background-color: green; + scroll-snap-align: start none; +} + +#x-target { + width: 300px; + height: 300px; + top: 0; + left: 100px; + background-color: red; + scroll-snap-align: none start; +} + +.area { + width: 1000vw; + height: 1000vh; +} +</style> + +<div class="area"></div> +<div id="x-target"></div> +<div id="y-target"></div> + +<script> +const x_target = document.getElementById("x_target"); +const y_target = document.getElementById("y_target"); +const scroller = document.documentElement; + +function cleanup() { + scroller.style.setProperty("scroll-snap-type", "none"); +} + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "y mandatory"); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 0); +}, "Changing the scroller's snap type to y should make it resnap on the y-axis."); + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "x mandatory"); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); +}, "Changing the scroller's snap type to x should make it resnap on the x-axis."); + + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "x mandatory"); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); + + scroller.style.setProperty("scroll-snap-type", "y mandatory"); + assert_equals(scroller.scrollTop, 100); +}, "Changing the scroller's snap type axis should make it resnap."); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type.html new file mode 100644 index 0000000000..70774b3d40 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/changing-scroll-snap-type.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<title> + Updating the scroll-snap-type of a snap container should make it resnap accordingly. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: none; +} + +#y-target { + width: 300px; + height: 300px; + top: 100px; + left: 0; + background-color: green; + scroll-snap-align: start none; +} + +#x-target { + width: 300px; + height: 300px; + top: 0; + left: 100px; + background-color: red; + scroll-snap-align: none start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="x-target"></div> + <div id="y-target"></div> +</div> + +<script> +const x_target = document.getElementById("x_target"); +const y_target = document.getElementById("y_target"); +const scroller = document.getElementById("scroller"); + +function cleanup() { + scroller.style.setProperty("scroll-snap-type", "none"); +} + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "y mandatory"); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 0); +}, "Changing the scroller's snap type to y should make it resnap on the y-axis."); + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "x mandatory"); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); +}, "Changing the scroller's snap type to x should make it resnap on the x-axis."); + + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + scroller.style.setProperty("scroll-snap-type", "x mandatory"); + assert_equals(scroller.scrollLeft, 100); + assert_equals(scroller.scrollTop, 0); + + scroller.style.setProperty("scroll-snap-type", "y mandatory"); + assert_equals(scroller.scrollTop, 100); +}, "Changing the scroller's snap type axis should make it resnap."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/focus-element-no-snap.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/focus-element-no-snap.html new file mode 100644 index 0000000000..9ec004c628 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/focus-element-no-snap.html @@ -0,0 +1,51 @@ +<!doctype html> +<title>Resnap to focused element after relayout</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + +#snapper { + width: 100px; + height: 200px; + overflow-x: scroll; + scroll-snap-type: x mandatory; + color: white; + background-color: oldlace; + display: flex; + align-items: center; +} +.child { + margin-right: 0.5rem; + height: 90%; + scroll-snap-align: start; + padding: 1rem; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + width: 100px; + height: 100px; + background-color: indigo; +} +</style> + +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap"> + +<div id=snapper> + <div class="child no-snap" tabindex=-1></div> + <div class=child></div> + <div class="child" id ="focus" tabindex=-1></div> + <div class="child" tabindex=-1></div> + <div class=child></div> + <div class=child></div> +</div> + +<script> + +test(t => { + document.getElementById("focus").focus(); + const element = document.getElementById("snapper"); + element.style.width = "101px"; + assert_equals(element.scrollLeft, 0); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/move-current-target.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/move-current-target.html new file mode 100644 index 0000000000..ccadc884c5 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/move-current-target.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<title> + Moving the current snap target should make the scroller resnap to it. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: relative; + margin: 0; +} + +#block { + height: 100px; + width: 100px; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#initial-target { + width: 300px; + height: 300px; + left: 100px; + top: 0; + transform: none; + background-color: green; + scroll-snap-align: start; +} + +#other-target { + width: 300px; + height: 300px; + left: 300px; + background-color: red; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div id="block"></div> + <div id="initial-target"></div> + <div id="other-target"></div> + <div class="area"></div> +</div> + +<script> +const initial_target = document.getElementById("initial-target"); +const other_target = document.getElementById("other-target"); +const block = document.getElementById("block"); +const scroller = document.getElementById("scroller"); + +function cleanup() { + initial_target.style.setProperty("transform", "none"); + initial_target.style.setProperty("top", "0"); + block.style.setProperty("height", "100px"); +} + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("top", "300px"); + assert_equals(scroller.scrollTop, 400); + assert_equals(scroller.scrollLeft, 100); +}, "Moving the current snap target should make the scroller resnap to it."); + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + block.style.setProperty("height", "200px"); + assert_equals(scroller.scrollTop, 200); + assert_equals(scroller.scrollLeft, 100); +}, "Changing the layout of other elements should be able to cause resnapping to \ +the target."); + +test(t => { + t.add_cleanup(cleanup); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("transform", "translate(0,100px)"); + assert_equals(scroller.scrollTop, 200); + assert_equals(scroller.scrollLeft, 100); +}, "Transforming the current snap target should make the scroller resnap to it."); + +test(t => { + t.add_cleanup(cleanup); + initial_target.style.setProperty("top", "100px"); + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 200); + assert_equals(scroller.scrollLeft, 100); + + initial_target.style.setProperty("transform", "translate(0,100px)"); + initial_target.style.setProperty("top", "0"); + assert_equals(scroller.scrollTop, 200); + assert_equals(scroller.scrollLeft, 100); +}, "Applying two property changes that do not change the visual offset of the \ +target should not change the scroll offset."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-element.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-element.html new file mode 100644 index 0000000000..f15a291f08 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-element.html @@ -0,0 +1,128 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="resources/common.js" ></script> + </head> + <body> + <style> + .scroller { + overflow: scroll; + position: relative; + height: 400px; + width: 400px; + border:solid 1px black; + scroll-snap-type: y mandatory; + } + .no-snap { scroll-snap-align: none } + .scroller div:focus { + border: solid 1px red; + } + .large-space { + height: 300vh; + width: 300vw; + } + .target { + scroll-snap-align: start; + position: absolute; + width: 100px; + height: 100px; + border: solid 1px black; + } + .top { + top: 0px; + } + .left { + left: 0px; + } + .right { + left: 200px; + } + .bottom { + top: 200px; + } + </style> + <div id="scroller" class="scroller"> + <div class="large-space no-snap" tabindex="1" id="space"></div> + <div id="topleft" tabindex="1" class="top left target">top left</div> + <div id="topright" tabindex="1" class="top right target">top right</div> + <div id="bottomleft" tabindex="1" class="bottom left target">bottom left</div> + <div id="bottomright" tabindex="1" class="bottom right target">bottom right</div> + </div> + <script> + window.onload = () => { + const bottomright = document.getElementById("bottomright"); + const bottomleft = document.getElementById("bottomleft"); + const scroller = document.getElementById("scroller"); + + async function commonInitialization() { + await waitForCompositorCommit(); + assert_equals(scroller.scrollTop, 0, "snapped to top row"); + } + + promise_test(async (t) => { + await commonInitialization(); + + focusAndAssert(bottomright); + await runScrollSnapSelectionVerificationTest(t, scroller, + [bottomright, + bottomleft], + /*expected_target=*/bottomright, "y"); + + focusAndAssert(bottomleft); + await runScrollSnapSelectionVerificationTest(t, scroller, + [bottomright, + bottomleft], + /*expected_target=*/bottomleft, "y"); + }, "scroller selects focused target from aligned choices on snap"); + + promise_test(async (t) => { + t.add_cleanup(() => { + bottomright.style.left = "200px"; + }) + await commonInitialization(); + + // Move bottomright out of the snapport. + bottomright.style.left = "500px"; + + // Set focus on bottomright without scrolling to it. + focusAndAssert(bottomright, true); + await runScrollSnapSelectionVerificationTest(t, scroller, + [bottomright, + bottomleft], + /*expected_target=*/bottomleft, "y"); + }, "out-of-viewport focused element is not the selected snap target."); + + promise_test(async(t) => { + t.add_cleanup(() => { + bottomleft.style.top = "200px"; + }); + await commonInitialization(); + + // Set focus on bottomright without scrolling to it. + focusAndAssert(bottomright, true); + + // Move bottomleft below bottomright. + bottomleft.style.top = "400px"; + + // Snap to bottomleft. + scroller.scrollTop = bottomleft.offsetTop; + + // Test that if bottomright is also shifted so that it is aligned with + // bottomleft, bottomleft remains the selected snap target, despite + // bottomright's having focus. + await runLayoutSnapSeletionVerificationTest(t, scroller, [bottomright], + bottomleft, "y"); + }, "scroller follows selected snap target through layout shift," + + "regardless of focus"); + + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-nested-containers.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-nested-containers.html new file mode 100644 index 0000000000..a6a087316f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-nested-containers.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="resources/common.js" ></script> + </head> + <body> + <style> + .snap { + scroll-snap-align: start; + } + .placeholder { + height: 40%; + width: 40%; + position: absolute; + top: 0px; + border: solid 1px black; + } + .right { + left: 50%; + } + .container { + position: relative; + border: solid 1px blue; + overflow: scroll; + scroll-snap-type: y mandatory; + } + .bigcontainer { + height: 1000px; + width: 1000px; + } + .smallcontainer { + height: 400px; + width: 400px; + position: absolute; + border: solid 1px blue; + top: 500px; + overflow: scroll; + scroll-snap-type: y mandatory; + } + .large-space { + height: 300vh; + width: 300vw; + position: absolute; + } + .target { + top: 50%; + width: 40%; + height: 40%; + position: absolute; + border: solid 1px red; + } + .target:focus { + border:solid 2px green; + } + </style> + <div class="bigcontainer container" id="outercontainer"> + <div class="large-space"></div> + <div id="leftplaceholder" class="snap placeholder">LPH (outer)</div> + <div id="rightplaceholder" class="snap placeholder right">RPH (outer)</div> + <div id="leftcontainer" class="snap smallcontainer"> + <div class="large-space"></div> + <div class="snap placeholder"></div> + <div class="snap placeholder right"></div> + <div id="lefttarget1" tabindex="1" class="snap target"></div> + <div id="lefttarget2" tabindex="1" class="snap target right"></div> + </div> + <div id="rightcontainer" class="snap smallcontainer right"> + <div class="large-space"></div> + <div class="snap placeholder"></div> + <div class="snap placeholder right"></div> + <div id="righttarget1" tabindex="1" class="snap target"></div> + <div id="righttarget2" tabindex="1" class="snap target right"></div> + </div> + </div> + <script> + // This test verifies that a snap container (outer) which contains another + // snap container (inner) snaps with awareness of focus on children of the + // inner container, i.e. outer should prefer to select the snap area whose + // child has focus even if there is an intermediate snap container between + // the child and outer. + window.onload = () => { + const lefttarget1 = document.getElementById("lefttarget1"); + const righttarget1 = document.getElementById("righttarget1"); + const leftcontainer = document.getElementById("leftcontainer"); + const rightcontainer = document.getElementById("rightcontainer"); + const outercontainer = document.getElementById("outercontainer"); + + promise_test(async (t) => { + await waitForCompositorCommit(); + + focusAndAssert(lefttarget1, /*preventScroll=*/true); + await runScrollSnapSelectionVerificationTest(t, outercontainer, + [leftcontainer, rightcontainer], leftcontainer, "y"); + + focusAndAssert(righttarget1, /*preventScroll=*/true); + await runScrollSnapSelectionVerificationTest(t, outercontainer, + [leftcontainer, rightcontainer], rightcontainer, "y"); + }, "Snap container prefers focused nested snap target."); + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/resources/common.js b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/resources/common.js new file mode 100644 index 0000000000..6ceec9118c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/resources/common.js @@ -0,0 +1,148 @@ +// Utility functions for scroll snap tests which verify User-Agents' snap point +// selection logic when multiple snap targets are aligned. +// It depends on methods in /resources/testdriver-actions.js and +// /dom/event/scrolling/scroll_support.js so html files using these functions +// should include those files as <script>s. + +// This function should be used by scroll snap WPTs wanting to test snap target +// selection when scrolling to multiple aligned targets. +// It assumes scroll-snap-align: start alignment and tries to align to the list +// of snap targets provided, |elements|, which are all expected to be at the +// same offset. +async function scrollToAlignedElementsInAxis(scroller, elements, axis) { + let target_offset_y = null; + let target_offset_x = null; + if (axis == "y") { + for (const e of elements) { + if (target_offset_y) { + assert_equals(e.offsetTop, target_offset_y, + `${e.id} is at y offset ${target_offset_y}`); + } else { + target_offset_y = e.offsetTop; + } + } + assert_equals(); + } else { + for (const e of elements) { + if (target_offset_x) { + assert_equals(e.offsetLeft, target_offset_x, + `${e.id} is at x offset ${target_offset_x}`); + } else { + target_offset_x = e.offsetLeft; + } + } + } + assert_not_equals(target_offset_x || target_offset_y, null); + + const scrollend_promise = waitForScrollendEventNoTimeout(scroller); + await new test_driver.Actions().scroll(0, 0, + (target_offset_x || 0) - scroller.scrollLeft, + (target_offset_y || 0) - scroller.scrollTop, + { origin: scroller }) + .send(); + await scrollend_promise; + if (axis == "y") { + assert_equals(scroller.scrollTop, target_offset_y, "vertical scroll done"); + } else { + assert_equals(scroller.scrollLeft,target_offset_x, "horizontal scroll done"); + } +} + +// This function verifies the snap target that a scroller picked by triggerring +// a layout change and observing which target is followed. Tests using this +// method should ensure that there is at least 100px of room to scroll in the +// desired axis. +// It assumes scroll-snap-align: start alignment. +function verifySelectedSnapTarget(scroller, expected_snap_target, axis) { + // Save initial style. + const initial_left = getComputedStyle(expected_snap_target).left; + const initial_top = getComputedStyle(expected_snap_target).top; + if (axis == "y") { + // Move the expected snap target along the y axis. + const initial_scroll_top = scroller.scrollTop; + const target_top = expected_snap_target.offsetTop + 100; + expected_snap_target.style.top = `${target_top}px`; + assert_equals(scroller.scrollTop, expected_snap_target.offsetTop, + `scroller followed ${expected_snap_target.id} after layout change`); + assert_not_equals(scroller.scrollTop, initial_scroll_top, + "scroller actually scrolled in y axis"); + } else { + // Move the expected snap target along the y axis. + const initial_scroll_left = scroller.scrollLeft; + const target_left = expected_snap_target.offsetLeft + 100; + expected_snap_target.style.left = `${target_left}px`; + assert_equals(scroller.scrollLeft, expected_snap_target.offsetLeft, + `scroller followed ${expected_snap_target.id} after layout change`); + assert_not_equals(scroller.scrollLeft, initial_scroll_left, + "scroller actually scrolled in x axis"); + } + // Undo style changes. + expected_snap_target.style.top = initial_top; + expected_snap_target.style.left = initial_left; +} + +// This is a utility function for tests which verify that the correct element +// is snapped to when snapping at the end of a scroll. +async function runScrollSnapSelectionVerificationTest(t, scroller, aligned_elements, + expected_target, axis) { + // Save initial scroll offset. + const initial_scroll_left = scroller.scrollLeft; + const initial_scroll_top = scroller.scrollTop; + await scrollToAlignedElementsInAxis(scroller, aligned_elements, axis); + verifySelectedSnapTarget(scroller, expected_target, axis); + // Restore initial scroll offsets. + const scrollend_promise = new Promise((resolve) => { + scroller.addEventListener("scrollend", resolve); + }); + scroller.scrollTo(initial_scroll_left, initial_scroll_top); + await scrollend_promise; +} + +// This is a utility function for tests verifying that a layout shift does not +// cause a scroller to change its selected snap target. +// It assumes the element to be aligned have scroll-snap-align: start. +// It tries to align the list of snap targets provided, |elements| with the +// current snap target. +function shiftLayoutToAlignElements(elements, target, axis) { + for (let element of elements) { + if (axis == "y") { + element.style.top = `${target.offsetTop}px`; + } else { + element.style.left = `${target.offsetLeft}px`; + } + } +} + +// This is a utility function for tests verifying that a layout shift does not +// cause a scroller to change its selected snap target. +// It assumes scroll-snap-align: start alignment. +async function runLayoutSnapSeletionVerificationTest(t, scroller, elements_to_align, + expected_target, axis) { + // Save initial scroll offsets and position. + const initial_scroll_left = scroller.scrollLeft; + const initial_scroll_top = scroller.scrollTop; + let initial_tops = []; + for (const element of elements_to_align) { + initial_tops.push(getComputedStyle(element).top); + } + + shiftLayoutToAlignElements(elements_to_align, expected_target, axis); + verifySelectedSnapTarget(scroller, expected_target, axis); + + // Restore initial scroll offset and position states. + let num_elements = initial_tops.length; + for (let i = 0; i < num_elements; i++) { + elements_to_align[i].style.top = initial_tops[i]; + } + // Restore initial scroll offsets. + const scrollend_promise = new Promise((resolve) => { + scroller.addEventListener("scrollend", resolve); + }); + scroller.scrollTo(initial_scroll_left, initial_scroll_top); + await scrollend_promise; +} + +function focusAndAssert(element, preventScroll=false) { + element.focus({preventScroll: preventScroll}); + assert_equals(document.activeElement, element); +} diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/not-resnap-outside-proximity-threshold.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/not-resnap-outside-proximity-threshold.html new file mode 100644 index 0000000000..b2c5720efb --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/not-resnap-outside-proximity-threshold.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title> + Not re-snap once after a scroll operation has finished without snapping + because the scroll destination was outside of the snap proximity threshold. +</title> +<!-- This test assumes that all major browsers' default scroll-snap proximity + thresholds are greater than 200px. --> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1780154"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 600px; + width: 600px; + overflow: hidden; + scroll-snap-type: y proximity; +} + +.snap { + width: 300px; + height: 300px; + background-color: green; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div class="snap" style="top: 0px;"></div> + <div class="snap" style="top: 500px;"></div> +</div> + +<script> +test(() => { + // The initial snap position is at (0, 0). + assert_equals(scroller.scrollTop, 0); + assert_equals(scroller.scrollLeft, 0); + + // Scroll to a position where it's outside of the scroll-snap proximity + // threshold, so that it won't trigger snapping. + scroller.scrollTo(0, 250); + + assert_equals(scroller.scrollTop, 250); + assert_equals(scroller.scrollLeft, 0); + + // Changing the initial snap target position, but still it's outside of the + // threshold. + document.querySelectorAll(".snap")[0].style.top = "10px"; + + // Not re-snap to the last snap point. + assert_equals(scroller.scrollTop, 250); + assert_equals(scroller.scrollLeft, 0); +}, "Not re-snap once after a scroll operation has finished without snapping " + + "because the scroll destination was outside of the snap proximity threshold."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/remove-current-target.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/remove-current-target.html new file mode 100644 index 0000000000..82bddf8074 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/remove-current-target.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<title> + Removing the current snap target should make the scroller snap to a new target. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#initial-target { + width: 300px; + height: 300px; + top: 100px; + left: 100px; + background-color: red; + scroll-snap-align: start; +} + +#other-target { + width: 300px; + height: 300px; + top: 300px; + left: 300px; + background-color: green; + scroll-snap-align: start; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="initial-target"></div> + <div id="other-target"></div> +</div> + +<script> +const initial_target = document.getElementById("initial-target"); +const other_target = document.getElementById("other-target"); +const scroller = document.getElementById("scroller"); + +test(() => { + scroller.scrollTo(0,0); + assert_equals(scroller.scrollTop, 100); + assert_equals(scroller.scrollLeft, 100); + + scroller.removeChild(initial_target); + assert_equals(scroller.scrollTop, 300); + assert_equals(scroller.scrollLeft, 300); +}, "Removing the current snap target should make the scroller snap to a new target."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/resnap-to-focused.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/resnap-to-focused.html new file mode 100644 index 0000000000..637c578a85 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/resnap-to-focused.html @@ -0,0 +1,82 @@ +<!doctype html> +<title>Resnap to focused element after relayout</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + +#snapper { + counter-reset: child 0; + width: 200px; + scroll-snap-type: block mandatory; + overflow:hidden; + height: 100px; +} +.child { + width: 100px; + height: 100px; + background:red; + text-align: center; + box-sizing: border-box; + counter-increment: child; + float: left; +} +.child.f { + background: green; + scroll-snap-align: center; +} +.child::before { + content: counter(child); +} + +</style> + +<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap"> +<!-- +When re-snapping after a layout change, +if multiple elements were capable of being the snap target previously, +and one of them is focused, +you must resnap to the focused one. +--> +<div id=snapper> + <div class="child no-snap" tabindex=-1></div> + <div class=child></div> + <div class="child f" tabindex=-1></div> + <div class="child f" tabindex=-1></div> + <div class=child></div> + <div class=child></div> +</div> + +<script> + +var container = document.querySelector("#snapper"); +var [one,two] = document.querySelectorAll(".child.f"); +var unsnappable = document.querySelector(".child.no-snap"); + +async_test(t=>{ + requestAnimationFrame(()=>{ + testSnap(t, one, 3); + requestAnimationFrame(()=>{ + testSnap(t, two, 4); + requestAnimationFrame(()=>{ + testSnap(t, one, 3); + t.done(); + }); + }); + }); +}); + +function testSnap(t, child, expectedRow) { + t.step(()=>{ + unsnappable.focus(); + container.style.width = "200px"; + var startingRow = container.scrollTop/100 + 1; + assert_equals(startingRow, 2, "Initially snapped to row 2"); + child.focus(); + container.style.width = "100px"; + var endingRow = container.scrollTop/100 + 1; + assert_equals(endingRow, expectedRow, `After resize, should snap to row ${expectedRow}.`); + }); +} + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/snap-to-different-targets.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/snap-to-different-targets.html new file mode 100644 index 0000000000..fb9469ba73 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/snap-to-different-targets.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<title> + The scroller should try to resnap to targets for both axes if possible. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#x-axis-target { + scroll-snap-align: none start; + background-color: blue; + width: 100px; + height: 100px; + top: 400px; + left: 200px; +} + +#y-axis-target { + scroll-snap-align: start none; + background-color: green; + width: 100px; + height: 100px; + top: 200px; + left: 400px; +} + +#far-x-axis-target { + scroll-snap-align: none start; + background-color: blue; + width: 100px; + height: 100px; + top: 1200px; + left: 300px; +} + +#far-y-axis-target { + scroll-snap-align: start none; + background-color: green; + width: 100px; + height: 100px; + top: 300px; + left: 1200px; +} + +.area { + width: 2000px; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div id="x-axis-target"></div> + <div id="y-axis-target"></div> + <div id="far-x-axis-target"></div> + <div id="far-y-axis-target"></div> +</div> + +<script> + +const x_target = document.getElementById("x-axis-target"); +const y_target = document.getElementById("y-axis-target"); +const scroller = document.getElementById("scroller"); + +test(t => { + // The scroller should be snapped to the two closest points on first layout. + assert_equals(scroller.scrollTop, 200); + assert_equals(scroller.scrollLeft, 200); + x_target.style.setProperty("left", "1000px"); + y_target.style.setProperty("top", "1000px"); + + // The style change makes it impossible for the scroller to snap to both + // targets, but at least one of the targets should be preserved. The scroller + // should then re-evaluate the snap point for the other axis. + const snapped_to_x = scroller.scrollLeft == 1000 && scroller.scrollTop == 300; + const snapped_to_y = scroller.scrollTop == 1000 && scroller.scrollLeft == 300; + assert_true(snapped_to_x || snapped_to_y); +}, "Scroller should snap to at least one of the targets if unable to snap to both after a layout change."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-add-scroll-container.html b/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-add-scroll-container.html new file mode 100644 index 0000000000..66fa96b745 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-add-scroll-container.html @@ -0,0 +1,154 @@ +<!DOCTYPE html> +<title> + Adding a scrollable element should make it start capturing snap points. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#captures-snap-positions"/> +<meta name="viewport" content="user-scalable=no"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +html { + scroll-snap-type: y mandatory; +} + +body { + margin: 0px; +} + +#middle-scroller { + top: 100px; + height: 500px; + width: 500px; + overflow: visible; + background-color: rgb(12, 61, 2); + scroll-snap-type: none; +} + +#inner-scroller { + top: 200px; + height: 400px; + width: 400px; + overflow: visible; + background-color: rgb(65, 139, 50); + scroll-snap-type: y mandatory; +} + +.space { + width: 2000px; + height: 2000px; +} + +#inner-snap-area { + top: 300px; + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#document-snap-area { + top: 500px; + width: 200px; + height: 200px; + background-color: lightblue; + scroll-snap-align: start; +} + +#inserted-snap-container { + top: 400px; + height: 600px; + width: 400px; + overflow: scroll; + scroll-snap-type: y mandatory; +} +</style> + +<div class="space"></div> + <div id="middle-scroller"> + <div class="space"></div> + <div id="inner-scroller"> + <div class="space"></div> + <div id="inner-snap-area"></div> + </div> + </div> +</div> +<div id="document-snap-area"></div> +<script> + +const inner_scroller = document.getElementById("inner-scroller"); +const middle_scroller = document.getElementById("middle-scroller"); +const document_scroller = document.scrollingElement; + +// This tests that making an element scrollable will reassign the correct snap +// areas to itself, per spec [1]. +// [1] https://drafts.csswg.org/css-scroll-snap/#captures-snap-positions +test(() => { + // Confirm that the document-level scroller is the snap container for all of + // the snap areas. + document_scroller.scrollTo(0, 10); + assert_equals(document_scroller.scrollTop, 500); + // Snaps to the inner snap area. + document_scroller.scrollBy(0, 75); + assert_equals(document_scroller.scrollTop, 600); + + // The middle scroller should now have the inner snap area assigned to it. + // Per spec, even if the snap-type is 'none', it should still capture snap + // points. + middle_scroller.style.setProperty("overflow", "scroll"); + + // The middle scroller has snap-type 'none' so it should not snap. + middle_scroller.scrollBy(0, 10); + assert_equals(middle_scroller.scrollTop, 10); + + // The document scroller should only snap to the document-level snap area. + document_scroller.scrollTo(0, 600); + assert_equals(document_scroller.scrollTop, 500); + + // The inner scroller should now have the innermost snap area assigned to it. + inner_scroller.style.setProperty("overflow", "scroll"); + inner_scroller.scrollBy(0, 10); + assert_equals(inner_scroller.scrollTop, 300); + + document_scroller.scrollTo(0, 600); + assert_equals(document_scroller.scrollTop, 500); + +}, "Making an element scrollable should make it capture the correct descendant\ + snap areas' snap points."); + + // Test that attaching a new snap container also properly assigns snap areas. + test(() => { + // All containers should capture snap areas. + middle_scroller.style.setProperty("overflow", "scroll"); + inner_scroller.style.setProperty("overflow", "scroll"); + + // Sanity check that the scrollers still snap to the snap areas. + document_scroller.scrollTo(0, 10); + inner_scroller.scrollTo(0,10); + assert_equals(inner_scroller.scrollTop, 300); + assert_equals(document_scroller.scrollTop, 500); + + // Create new snap container and append thedocument-level snap area as its + // child. + const inserted_scroller = document.createElement("div"); + inserted_scroller.id = "inserted-snap-container"; + const space = document.createElement("div"); + space.classList.add("space"); + inserted_scroller.appendChild(space); + inserted_scroller.appendChild(document.getElementById("document-snap-area")); + document_scroller.appendChild(inserted_scroller); + + // Document scroller no longer snaps. + document_scroller.scrollTo(0, 400); + assert_equals(document_scroller.scrollTop, 400); + + // Inserted scroller snaps. + inserted_scroller.scrollTo(0, 10); + assert_equals(inserted_scroller.scrollTop, 500); + }, "Attaching a new element that is scrollable should assign the correct snap\ + areas to it."); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-remove-scroll-container.html b/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-remove-scroll-container.html new file mode 100644 index 0000000000..e3798cc73f --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-area-capturing-remove-scroll-container.html @@ -0,0 +1,128 @@ +<!DOCTYPE html> +<title> + When an element no longer captures snap positions (e.g., no longer + scrollable), then its currently captured snap areas must be reassigned. +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#captures-snap-positions"/> +<meta name="viewport" content="user-scalable=no"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +html { + scroll-snap-type: y mandatory; +} + +body { + margin: 0px; +} + +#middle-scroller { + top: 100px; + height: 500px; + width: 500px; + overflow: scroll; + background-color: rgb(12, 61, 2); + scroll-snap-type: none; +} + +#inner-scroller { + top: 200px; + height: 400px; + width: 400px; + overflow: scroll; + background-color: rgb(65, 139, 50); + scroll-snap-type: y mandatory; +} + +.space { + width: 2000px; + height: 2000px; +} + +#inner-snap-area { + top: 300px; + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#document-snap-area { + top: 500px; + width: 200px; + height: 200px; + background-color: lightblue; + scroll-snap-align: start; +} + +</style> +<div class="space"></div> + <div id="middle-scroller"> + <div class="space"></div> + <div id="inner-scroller"> + <div class="space"></div> + <div id="inner-snap-area"></div> + </div> + </div> +</div> +<div id="document-snap-area"></div> +<script> + +// This tests that making a snap container no longer scrollable will reassign +// its snap areas to the next scrollable ancestor, per spec [1]. +// [1] https://drafts.csswg.org/css-scroll-snap/#captures-snap-positions +test(() => { + const inner_scroller = document.getElementById("inner-scroller"); + const middle_scroller = document.getElementById("middle-scroller"); + const document_scroller = document.scrollingElement; + + // Inner scroller should snap to its captured area. + // Middle scroller doesn't snap. + // Document scroller should snap to its only captured area. + inner_scroller.scrollBy(0,10); + middle_scroller.scrollBy(0, 10); + // Scroll to (0,600), where we would expect the inner snap area to be relative + // to the document scroller. + document_scroller.scrollTo(0, 600); + assert_equals(inner_scroller.scrollTop, 300); + assert_equals(middle_scroller.scrollTop, 10); + assert_equals(document_scroller.scrollTop, 500); + + // Inner scroller is no longer a scroll container. + inner_scroller.style.setProperty("overflow", "visible"); + assert_equals(inner_scroller.scrollTop, 0); + assert_equals(middle_scroller.scrollTop, 10); + assert_equals(document_scroller.scrollTop, 500); + + // The new snap container is the middle scroller, which has snap-type 'none'. + // Per spec, the scroll container should capture snap positions even if it has + // snap-type 'none'. + // The middle scroller should not snap. + // The document scroller should still only snap to its captured snap area. + document_scroller.scrollBy(0, 100); + middle_scroller.scrollBy(0, 10); + assert_equals(inner_scroller.scrollTop, 0); + assert_equals(middle_scroller.scrollTop, 20); + assert_equals(document_scroller.scrollTop, 500); + + // The scroll container should now be at the document level. + middle_scroller.style.setProperty("overflow", "visible"); + document_scroller.scrollBy(0, -10); + assert_equals(inner_scroller.scrollTop, 0); + assert_equals(middle_scroller.scrollTop, 0); + + // Check that the existing snap area did not get removed when reassigning + // the inner snap area. + assert_equals(document_scroller.scrollTop, 500); + + // Check that the inner snap area got reassigned to the document. + document_scroller.scrollBy(0, 150); + assert_equals(document_scroller.scrollTop, 600); +}, 'Making a snap container not scrollable should promote the next scrollable\ + ancestor to become a snap container.'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-at-user-scroll-end.html b/testing/web-platform/tests/css/css-scroll-snap/snap-at-user-scroll-end.html new file mode 100644 index 0000000000..8643b3c148 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-at-user-scroll-end.html @@ -0,0 +1,111 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<title>Tests that window should snap at user scroll end.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="support/common.js"></script> +<style> +html { + margin: 0px; + scroll-snap-type: both mandatory; +} +#content { + width: 2000px; + height: 2000px; + padding: 0px; + margin: 0px; +} +#target { + position: relative; + left: 400px; + top: 400px; + width: 400px; + height: 400px; + background-color: lightblue; + overflow: hidden; + scroll-snap-align: start; +} +#i1 { + color: red; + font-weight: bold; +} +</style> + +<div id="content"> + <div id="target"> + <h1>CSSScrollSnap</h1> + <h4>Tests that the window can snap at user scroll end.</h4> + <ol> + <li id="i1" style="color: red"> + Scroll the page vertically and horizontally. + Keep scrolling until background has turned yellow.</li> + <li id="i2"> Press the button "Done"</li> + </ol> + <input type="button" id="btn" value="DONE" style="width: 100px; height: 50px;"/> + </div> +</div> + +<script> +var snap_test = async_test('Tests that window should snap at user scroll end.'); +var body = document.body; +var button = document.getElementById("btn"); +var target = document.getElementById("target"); +var instruction1 = document.getElementById("i1"); +var instruction2 = document.getElementById("i2"); +var scrolled_x = false; +var scrolled_y = false; +var start_x = window.scrollX; +var start_y = window.scrollY; +var actions_promise; + +scrollTop = () => window.scrollY; + +window.onscroll = function() { + if (scrolled_x && scrolled_y) { + body.style.backgroundColor = "yellow"; + instruction1.style.color = "black"; + instruction1.style.fontWeight = "normal"; + instruction2.style.color = "red"; + instruction2.style.fontWeight = "bold"; + return; + } + + scrolled_x |= window.scrollX != start_x; + scrolled_y |= window.scrollY != start_y; +} + +button.onclick = function() { + if (!scrolled_x || !scrolled_y) + return; + + snap_test.step(() => { + assert_equals(window.scrollX, target.offsetLeft, + "window.scrollX should be at snapped position."); + assert_equals(window.scrollY, target.offsetTop, + "window.scrollY should be at snapped position."); + }); + + // To make the test result visible. + var content = document.getElementById("content"); + body.removeChild(content); + actions_promise.then( () => { + snap_test.done(); + }); +} + +// Inject scroll actions. +const pos_x = 20; +const pos_y = 20; +const scroll_delta_x = 100; +const scroll_delta_y = 100; +actions_promise = new test_driver.Actions() + .scroll(pos_x, pos_y, scroll_delta_x, scroll_delta_y) + .send().then(() => { + return waitForAnimationEnd(scrollTop); +}).then(() => { + return test_driver.click(button); +}); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-inline-block.html b/testing/web-platform/tests/css/css-scroll-snap/snap-inline-block.html new file mode 100644 index 0000000000..9606023e16 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-inline-block.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + width: 400px; + height: 350px; + overflow: scroll; + scroll-snap-type: both mandatory; +} +#space { + width: 1000px; + height: 1000px; +} +#target { + width: 200px; + height: 200px; + left: 300px; + top: 300px; +} +</style> + +<div id="scroller"> + <div id="space"></div> + <div id="target"></div> +</div> + +<script> +const scroller_width = scroller.clientWidth; +const scroller_height = scroller.clientHeight; +[ + ["horizontal-tb", 300, 500 - scroller_height], + ["vertical-lr", 500 - scroller_width, 300], + ["vertical-rl", scroller_width - 700, 300] +].forEach(([writing_mode, left, top]) => { + test(() => { + const target_left = getComputedStyle(target).left; + scroller.style.writingMode = writing_mode; + target.style.scrollSnapAlign = "end start"; + if (writing_mode == "vertical-rl") { + target.style.left = (scroller_width - 700) + "px"; + scroller.scrollTo(-500, 0); + } else { + scroller.scrollTo(0, 0); + } + assert_equals(scroller.scrollLeft, left, "aligns correctly on x"); + assert_equals(scroller.scrollTop, top, "aligns correctly on y"); + target.style.left = target_left; + scroller.style.writingMode = ""; + }, "Snaps correctly for " + writing_mode + + " writing mode with 'scroll-snap-align: end start' alignment"); +}); + +[ + ["horizontal-tb", 500 - scroller_width, 300], + ["vertical-lr", 300, 500 - scroller_height], + ["vertical-rl", target.clientWidth - 700, 500 - scroller_height] +].forEach(([writing_mode, left, top]) => { + test(() => { + const target_left = getComputedStyle(target).left; + scroller.style.writingMode = writing_mode; + target.style.scrollSnapAlign = "start end"; + if (writing_mode == "vertical-rl") { + target.style.left = (scroller_width - 700) + "px"; + scroller.scrollTo(-500, 0); + } else { + scroller.scrollTo(0, 0); + } + assert_equals(scroller.scrollLeft, left, "aligns correctly on x"); + assert_equals(scroller.scrollTop, top, "aligns correctly on y"); + target.style.left = target_left; + scroller.style.writingMode = ""; + }, "Snaps correctly for " + writing_mode + + " writing mode with 'scroll-snap-align: start end' alignment"); +}); + +test(() => { + const target_left = getComputedStyle(target).left; + scroller.style.direction = "rtl"; + target.style.scrollSnapAlign = "end start"; + target.style.left = (scroller_width - 700) + "px"; + + scroller.scrollTo(-500, 0); + assert_equals(scroller.scrollLeft, target.clientWidth - 700, + "aligns correctly on x"); + assert_equals(scroller.scrollTop, 500 - scroller_height, + "aligns correctly on y"); + + target.style.left = target_left; + scroller.style.direction = ""; +}, "Snaps correctly for 'direction: rtl' with 'scroll-snap-align: end start' " + + "alignment"); + +test(() => { + const target_left = getComputedStyle(target).left; + scroller.style.direction = "rtl"; + target.style.scrollSnapAlign = "start end"; + target.style.left = (scroller_width - 700) + "px"; + + scroller.scrollTo(-500, 0); + assert_equals(scroller.scrollLeft, scroller_width - 700, + "aligns correctly on x"); + assert_equals(scroller.scrollTop, 300, "aligns correctly on y"); + + target.style.left = target_left; + scroller.style.direction = ""; +}, "Snaps correctly for 'direction: rtl' with 'scroll-snap-align: start end' " + + "alignment"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-intended-direction.html b/testing/web-platform/tests/css/css-scroll-snap/snap-intended-direction.html new file mode 100644 index 0000000000..4a1b56d251 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-intended-direction.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title>`intended direction` scroll snaps only at points ahead of the scroll direction</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1766805"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + width: 200px; + height: 100px; + overflow: scroll; + scroll-snap-type: x mandatory; +} +.snap { + scroll-snap-align: start; + background: green; +} +</style> + +<div id="scroller"> + <div style="width: 2000px; height: 100px;"></div> + <div class="snap" style="left: 0px; width: 20px; height: 100px;">1</div> + <div class="snap" style="left: 100px; width: 20px; height: 100px;">2</div> + <div class="snap" style="left: 120px; width: 20px; height: 100px;">3</div> + <div class="snap" style="left: 300px; width: 20px; height: 100px;">4</div> + <div class="snap" style="left: 400px; width: 20px; height: 100px;">5</div> +</div> + +<script> +test(() => { + scroller.scrollBy(10, 0); + assert_equals(scroller.scrollLeft, 100); + + scroller.scrollBy(10, 0); + assert_equals(scroller.scrollLeft, 120); + + scroller.scrollBy(10, 0); + // Snaps to the next snap point even if the previous snap point is closer to + // the current position. + assert_equals(scroller.scrollLeft, 300); +}, "`intended direction` scroll snaps only at points ahead of the scroll " + + "direction"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-into-covering-area.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/snap-into-covering-area.tentative.html new file mode 100644 index 0000000000..2cc8741f5e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-into-covering-area.tentative.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html> + +<head> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> +</head> + +<body> + <style> + #scroller { + overflow: scroll; + height: 500px; + width: 500px; + background-color: blue; + scroll-snap-type: y mandatory; + position: absolute; + } + + .snap_point { + scroll-snap-align: start; + width: 40%; + position: relative; + left: 30%; + } + + .big { + height: 1000%; + background-color: pink; + border: solid 1px red; + } + + .small { + height: 50%; + background-color: purple; + border: solid 1px black; + } + </style> + <div id="scroller"> + <div class="big snap_point" id="big_snap_point"></div> + <div class="small snap_point"> + <button id="scrollerButton">scrollerButton</button> + </div> + </div> + <script> + promise_test(async(t) => { + const x = scroller.clientWidth / 2; + const y = scroller.clientHeight / 2; + + // Scroll all the way down to the smaller snap area which doesn't cover + // the snapport. + let scrollend_promise = new Promise((resolve) => { + scroller.addEventListener("scrollend", resolve); + }); + scroller.scrollTop = scroller.scrollHeight; + await scrollend_promise; + + // Scroll up with one press of the arrow-up button. + scrollend_promise = new Promise((resolve) => { + scroller.addEventListener("scrollend", resolve); + }); + const arrowUp = '\uE013'; + await test_driver.send_keys(scrollerButton, arrowUp); + + await scrollend_promise; + assert_equals(scroller.scrollTop, big_snap_point.offsetHeight - scroller.clientHeight, + "scroller is snapped to the bottom of the larger snap area, not the top"); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-on-focus.html b/testing/web-platform/tests/css/css-scroll-snap/snap-on-focus.html new file mode 100644 index 0000000000..13709d2747 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-on-focus.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<title>Scroll snap on Element.focus()</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0; +} + +#scroller { + height: 500px; + width: 500px; + overflow-y: scroll; + scroll-snap-type: y mandatory; +} + +.snap { + width: 100%; + height: 300px; + top: 100px; + left: 0; + background-color: green; + scroll-snap-align: start none; +} + +.no-snap { + width: 100%; + height: 300px; + top: 100px; + left: 0; + background-color: red; +} + +.area { + width: 100%; + height: 2000px; +} +</style> + +<div id="scroller"> + <div class="area"></div> + <div class="snap" style="top: 0px;"></div> + <div class="no-snap" style="top: 1000px;" tabindex=-1></div> + <div class="snap" style="top: 1200px;"></div> +</div> + +<script> +promise_test(async () => { + document.querySelector(".no-snap").focus(); + await new Promise(resolve => step_timeout(resolve, 0)); + assert_equals(scroller.scrollTop, 1200); +}, "scroll snap should happens on Element.focus()"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-1.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-1.html new file mode 100644 index 0000000000..985227bb53 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-1.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<meta name="viewport" content="width=device-width"> +<title> + Snap to points of combinations of two different elements +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +#left-top { + /* + * the scroll position of this `scroll-snap-align: start start` is (200, 200) + */ + left: 200px; + top: 200px; + height: 450px; + width: 450px; + background-color: rgb(255, 0, 0); + opacity: 0.5; + scroll-snap-align: start start; +} + +#right-bottom { + /* + * the scroll position of this `scroll-snap-align: end end` is (50, 50), + * i.e, (`left` - scroll container's `width` + this element's `width, + `top` - scroll container's `height` + this element's `height) + */ + left: 600px; + top: 600px; + width: 50px; + height: 50px; + background-color: rgb(0, 255, 0); + opacity: 0.5; + scroll-snap-align: end end; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-top"></div> + <div id="right-bottom"></div> +</div> +<script> +test(t => { + const scroller = document.getElementById("scroller"); + assert_equals(scroller.scrollLeft, 50); + assert_equals(scroller.scrollTop, 50); + + scroller.scrollTo(100, 150); + assert_equals(scroller.scrollLeft, 50); + assert_equals(scroller.scrollTop, 200); + + scroller.scrollTo(300, 300); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); + + scroller.scrollTo(150, 100); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 50); +}, 'Snap to points of combinations of two different elements'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-2.tentative.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-2.tentative.html new file mode 100644 index 0000000000..8d06eb91d0 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-combination-of-two-elements-2.tentative.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<meta name="viewport" content="width=device-width"> +<title> + Snap to points of combinations of two different elements +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: hidden; + scroll-snap-type: both mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +#left-top { + /* + * the scroll position of this `scroll-snap-align: start start` is (200, 200) + */ + left: 200px; + top: 200px; + height: 450px; + width: 450px; + background-color: rgb(255, 0, 0); + opacity: 0.5; + scroll-snap-align: start start; +} + +#right-bottom { + /* + * the scroll position of this `scroll-snap-align: end end` is (50, 250), + * i.e, (`left` - scroll container's `width` + this element's `width, + `top` - scroll container's `height` + this element's `height) + */ + left: 600px; + top: 800px; + width: 50px; + height: 50px; + background-color: rgb(0, 255, 0); + opacity: 0.5; + scroll-snap-align: end end; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-top"></div> + <div id="right-bottom"></div> +</div> +<script> +test(t => { + // There are four combinations of snap positions defined by #top-left and + // #right-bottom elements. + // (200, 200), (50, 250), (200, 250) and (50, 200). + // But snapping to (50, 200) leaves the snap area of #right-bottom element + // outside of the snapport, thus it won't be a valid snap position. + const scroller = document.getElementById("scroller"); + + // The nearest valid snap position from (0, 0) is (50, 250). + assert_equals(scroller.scrollLeft, 50); + assert_equals(scroller.scrollTop, 250); + + // (50, 200) is not valid, thus the nearest one from (100, 150) is (200, 200). + scroller.scrollTo(100, 150); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); + + scroller.scrollTo(300, 300); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 250); + + scroller.scrollTo(200, 100); + assert_equals(scroller.scrollLeft, 200); + assert_equals(scroller.scrollTop, 200); +}, 'Snap to points of combinations of two different elements'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-transformed-target.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-transformed-target.html new file mode 100644 index 0000000000..b8604269b4 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-transformed-target.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; +} +#scroller { + overflow: hidden; /* TODO: Use scrollbar-width: none */ + scroll-snap-type: x mandatory; + width: 500px; + height: 500px; +} +.space { + width: 2000px; + height: 2000px; +} +#target { + height: 200px; + width: 200px; + left: 50px; + background-color: blue; +} +</style> +<div id="scroller"> + <div class="space"></div> + <div id="target"></div> +</div> +<script> +test(() => { + target.style.scrollSnapAlign = "start"; + target.style.transform = "translateX(300px)"; + scroller.scrollTo(10, 0); + assert_equals(scroller.scrollLeft, 350 /* left + translateX(300px) */); + assert_equals(scroller.scrollTop, 0); +}, "Snaps to the transformed snap start position"); + +test(() => { + target.style.scrollSnapAlign = "end"; + target.style.transform = "translateX(300px)"; + scroller.scrollTo(10, 0); + assert_equals(scroller.scrollLeft, + 50 /* left + width + translateX(300px) - scroller.width */); + assert_equals(scroller.scrollTop, 0); +}, "Snaps to the transformed snap end position"); + +test(() => { + target.style.scrollSnapAlign = "start"; + target.style.transform = "translateX(-100px)"; + scroller.scrollTo(10, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Snaps to visible top left position of the transformed box"); + +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-both.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-both.html new file mode 100644 index 0000000000..cde329bcc3 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-both.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<title> + Snap to a visible area only even when there is a closer snap point for an area + that is closer but not visible (using both axes snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: both mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#left-top { + left: 0px; + top: 0px; +} + +#left-bottom { + left: 0px; + top: 800px; +} + +#right-top { + left: 800px; + top: 0px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-top" class="snap"></div> + <div id="left-bottom" class="snap"></div> + <div id="right-top" class="snap"></div> +</div> +<script> +test(t => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + scroller.scrollTo(500, 600); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 800); + scroller.scrollTo(600, 500); + assert_equals(scroller.scrollLeft, 800); + assert_equals(scroller.scrollTop, 0); +}, 'Only snap to visible areas in the case where taking the closest snap point of \ + each axis does not snap to a visible area'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-both.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-both.html new file mode 100644 index 0000000000..4c3c3c11a6 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-both.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<title> + Snap to an area where the element's scroll-margin is visible but not the + element itself (using both axes snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: both mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#left-top { + left: 0px; + top: 0px; +} + +#left-bottom { + left: 0px; + top: 800px; + /* 800px scroll-margin makes the snap area span to the right end of the + right-top area */ + scroll-margin-right: 800px; +} + +#right-top { + left: 800px; + top: 0px; + /* 800px scroll-margin makes the snap area span to the bottom end of the + left-bottom area */ + scroll-margin-bottom: 800px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-top" class="snap"></div> + <div id="left-bottom" class="snap"></div> + <div id="right-top" class="snap"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + // 750 and 650 are picked as those are closer to top left of the intersection + // (800, 800) of the snap areas where the browser should snap. This makes the + // intersection a closer snap option than a covering option that the browser + // might choose where the snapport is aligned on the bottom and right. + scroller.scrollTo(650, 750); + assert_equals(scroller.scrollLeft, 800); + assert_equals(scroller.scrollTop, 800); + scroller.scrollTo(750, 650); + assert_equals(scroller.scrollLeft, 800); + assert_equals(scroller.scrollTop, 800); +}, 'Snap to area such that only the scroll margin from both axes\' areas are \ +visible'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-x-axis.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-x-axis.html new file mode 100644 index 0000000000..dea9225a47 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-x-axis.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title> + Snap to an area where the element's scroll-margin is visible but not the + element itself (using x-axis snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: x mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#left-visible { + left: 0px; + top: 0px; +} + +#right-visible { + left: 800px; + top: 0px; +} + +#middle-margin-visible { + left: 700px; + top: 800px; + /* 300px makes snap area visible with margin but non-visible without it */ + scroll-margin-top: 300px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-visible" class="snap"></div> + <div id="middle-margin-visible" class="snap"></div> + <div id="right-visible" class="snap"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + scroller.scrollTo(500, 0); + assert_equals(scroller.scrollLeft, 700); + assert_equals(scroller.scrollTop, 0); +}, 'Scroll margin should be considered when calculating snap area visibilty \ +while snapping on the x-axis'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-y-axis.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-y-axis.html new file mode 100644 index 0000000000..60c5488445 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-margin-y-axis.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title> + Snap to an area where the element's scroll-margin is visible but not the + element itself (using y-axis snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: y mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#top-visible { + left: 0px; + top: 0px; +} + +#bottom-visible { + left: 0px; + top: 800px; +} + +#middle-margin-visible { + left: 800px; + top: 700px; + /* 300px makes snap area visible with margin but non-visible without it */ + scroll-margin-left: 300px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="top-visible" class="snap"></div> + <div id="middle-margin-visible" class="snap"></div> + <div id="bottom-visible" class="snap"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + scroller.scrollTo(0, 500); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 700); +}, 'Scroll margin should be considered when calculating snap area visibilty \ +while snapping on the y-axis'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-x-axis.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-x-axis.html new file mode 100644 index 0000000000..b8b226f1af --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-x-axis.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title> + Snap to a visible area only even when there is a closer snap point for an area + that is closer but not visible (using x-axis snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: x mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#left-visible { + left: 0px; + top: 0px; +} + +#right-visible { + left: 800px; + top: 0px; +} + +#middle-not-visible { + left: 700px; + top: 800px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="left-visible" class="snap"></div> + <div id="middle-not-visible" class="snap"></div> + <div id="right-visible" class="snap"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + scroller.scrollTo(500, 0); + assert_equals(scroller.scrollLeft, 800); + assert_equals(scroller.scrollTop, 0); +}, 'Only snap to visible area on X axis, even when the non-visible ones are closer'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-y-axis.html b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-y-axis.html new file mode 100644 index 0000000000..80d2e9946d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-to-visible-areas-y-axis.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title> + Snap to a visible area only even when there is a closer snap point for an area + that is closer but not visible (using y-axis snap type) +</title> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#snap-scope"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} + +#scroller { + height: 600px; + width: 600px; + overflow: scroll; + scroll-snap-type: y mandatory; +} + +#space { + width: 2000px; + height: 2000px; +} + +.snap { + width: 200px; + height: 200px; + background-color: blue; + scroll-snap-align: start; +} + +#top-visible { + left: 0px; + top: 0px; +} + +#bottom-visible { + left: 0px; + top: 800px; +} + +#middle-not-visible { + left: 800px; + top: 700px; +} + +</style> +<div id="scroller"> + <div id="space"></div> + <div id="top-visible" class="snap"></div> + <div id="middle-not-visible" class="snap"></div> + <div id="bottom-visible" class="snap"></div> +</div> +<script> +test(() => { + const scroller = document.getElementById("scroller"); + scroller.scrollTo(0, 0); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); + scroller.scrollTo(0, 500); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 800); +}, 'Only snap to visible area on Y axis, even when the non-visible ones are closer'); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/common.css b/testing/web-platform/tests/css/css-scroll-snap/support/common.css new file mode 100644 index 0000000000..f49c7cbacd --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/common.css @@ -0,0 +1,44 @@ +body { + margin: 0; +} + +#scroller { + position: absolute; + width: 400px; + height: 400px; + overflow: scroll; + padding: 0; + + scroll-snap-type: both mandatory; +} + +.snap { + position: absolute; + width: 200px; + height: 200px; + background-color: blue; + + scroll-snap-align: start; +} + +#space { + position: absolute; + width: 1000px; + height: 1000px; +} + +.left { + left: 0; +} + +.top { + top: 0; +} + +.right { + left: 400px; +} + +.bottom { + top: 400px; +}
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/common.js b/testing/web-platform/tests/css/css-scroll-snap/support/common.js new file mode 100644 index 0000000000..c7800b95f1 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/common.js @@ -0,0 +1,131 @@ +const KEY_CODE_MAP = { + 'ArrowLeft': '\uE012', + 'ArrowUp': '\uE013', + 'ArrowRight': '\uE014', + 'ArrowDown': '\uE015', + 'PageUp': '\uE00E', + 'PageDown': '\uE00F', + 'End': '\uE010', + 'Home': '\uE011', + 'Space': ' ', +}; + +// Send key event to the target element using test driver. Supports human +// friendly key names for common keyboard scroll operations e.g., arrow keys, +// page keys, etc. +async function keyPress(target, key) { + let code = key; + if (KEY_CODE_MAP.hasOwnProperty(key)) + code = KEY_CODE_MAP[key]; + + // First move pointer on target and click to ensure it receives the key. + return test_driver.send_keys(target, code); +} + +// Use rAF to wait for the value of the getter function passed to not change for +// at least 15 frames or timeout after 1 second. +// +// Example usage: +// await waitForAnimationEnd(() => scroller.scrollTop); +function waitForAnimationEnd(getValue) { + const TIMEOUT = 1000; // milliseconds + const MAX_UNCHANGED_FRAMES = 15; + + const start_time = performance.now(); + let last_changed_frame = 0; + let last_value = getValue(); + + return new Promise((resolve, reject) => { + function tick(frames, time) { + // We requestAnimationFrame either for TIMEOUT milliseconds or until + // MAX_UNCHANGED_FRAMES with no change have been observed. + if (time - start_time > TIMEOUT || + frames - last_changed_frame >= MAX_UNCHANGED_FRAMES) { + resolve(time); + } else { + current_value = getValue(); + if (last_value != current_value) { + last_changed_frame = frames; + last_value = current_value; + } + requestAnimationFrame(tick.bind(this, frames + 1)); + } + } + tick(0, start_time); + }); +} + + +function waitForEvent(eventTarget, type) { + return new Promise(resolve => { + eventTarget.addEventListener(type, resolve, { once: true }); + }); +} + +function waitForScrollEvent(eventTarget) { + return waitForEvent(eventTarget, 'scroll'); +} + +function waitForWheelEvent(eventTarget) { + return waitForEvent(eventTarget, 'wheel'); +} + +function waitForScrollStop(eventTarget) { + const TIMEOUT_IN_MS = 200; + + return new Promise(resolve => { + let lastScrollEventTime = performance.now(); + + const scrollListener = () => { + lastScrollEventTime = performance.now(); + }; + eventTarget.addEventListener('scroll', scrollListener); + + const tick = () => { + if (performance.now() - lastScrollEventTime > TIMEOUT_IN_MS) { + eventTarget.removeEventListener('scroll', scrollListener); + resolve(); + return; + } + requestAnimationFrame(tick); // wait another frame + } + requestAnimationFrame(tick); + }); +} + +function waitForScrollEnd(eventTarget) { + if (window.onscrollend !== undefined) { + return waitForScrollendEventNoTimeout(eventTarget); + } + return waitForScrollEvent(eventTarget).then(() => { + return waitForScrollStop(eventTarget); + }); +} + +function waitForScrollTo(eventTarget, getValue, targetValue) { + return new Promise((resolve, reject) => { + const scrollListener = (evt) => { + if (getValue() == targetValue) { + eventTarget.removeEventListener('scroll', scrollListener); + resolve(evt); + } + }; + if (getValue() == targetValue) + resolve(); + else + eventTarget.addEventListener('scroll', scrollListener); + }); +} + +function waitForNextFrame() { + return new Promise(resolve => { + const start = performance.now(); + requestAnimationFrame(frameTime => { + if (frameTime < start) { + requestAnimationFrame(resolve); + } else { + resolve(); + } + }); + }); +} diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-align-001-iframe.html b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-align-001-iframe.html new file mode 100644 index 0000000000..d86a5e86d0 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-align-001-iframe.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<title>iframe for #target and snap position with snapping off</title> +<style type='text/css'> + html, body { + margin: 0; padding: 0; + } + html { + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x fixed; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + div { + height: 1em; + } + html { scroll-padding: .5em 0 0; } /* set up a snap position */ + #target { scroll-margin: .5em 0 0; + scroll-snap-align: center; } + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div></div> +<div></div> +<div></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div id="stripe"></div> +<div id="target"></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div></div> +<div></div> +<div></div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-margin-001-iframe.html b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-margin-001-iframe.html new file mode 100644 index 0000000000..2b2c1d2d8c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-margin-001-iframe.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>iframe for #target and scroll-margin with snapping off (y</title> +<style type='text/css'> + html, body { + margin: 0; padding: 0; + } + html { + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x fixed; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + div { + height: 1em; + } + #target { scroll-margin: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div></div> +<div></div> +<div></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div id="stripe"></div> +<div id="target"></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div></div> +<div></div> +<div></div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-padding-001-iframe.html b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-padding-001-iframe.html new file mode 100644 index 0000000000..9260c81b1c --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-padding-001-iframe.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>iframe for #target and scroll-snap-padding with snapping off (y</title> +<style type='text/css'> + html, body { + margin: 0; padding: 0; + } + html { + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x fixed; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + } + div { + height: 1em; + } + html { scroll-padding: 2em 0 1em; } /* snap area is exact fit for snapport */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div></div> +<div></div> +<div></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div id="stripe"></div> +<div id="target"></div> +<div></div> +<div class="fail">FAIL</div> +<div></div> +<div></div> +<div></div> +<div></div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-snap-001-iframe.html b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-snap-001-iframe.html new file mode 100644 index 0000000000..b67c3f8d3e --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/support/scroll-target-snap-001-iframe.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>iframe for #target and snap position with snapping on</title> +<style type='text/css'> + html, body { + margin: 0; padding: 0; + } + html { + /* to make failing more obvious */ + background: 0 1em / 100% 1em linear-gradient(red, red) repeat-x fixed; + /* avoid anti-aliasing issues */ + font: 20px/1 sans-serif; + scrollbar-width: none; + + /* turn on snapping */ + scroll-snap-type: block; + } + div { + height: 1em; + } + /* Note: we use "start" for #target to avoid spec ambiguity where + * scroll-snap-align conflicts with default ScrollIntoViewOptions. + * See https://github.com/w3c/csswg-drafts/issues/9576. + */ + #target { scroll-margin: 2em 0 0; + scroll-snap-align: start; } /* set up a snap position */ + #stripe { background: green; } /* color part of the snap area */ + .fail { color: red; } /* make failing more obvious */ + + /* Try to foil the UA */ + .foilup { margin-bottom: -1em; scroll-snap-align: start; } + .foildn { margin-top: -1em; scroll-snap-align: end; } + + /* emulate `scrollbar-width: none` for browsers that don't support it yet */ + ::-webkit-scrollbar { display: none; } +</style> + +<div></div> +<div></div> +<div></div> +<div></div> +<div class="foilup"></div> +<div class="fail">FAIL</div> +<div></div> +<div id="stripe"></div> +<div class="foilup"></div> +<div id="target"></div> +<div class="foildn"></div> +<div></div> +<div class="fail">FAIL</div> +<div class="foildn"></div> +<div></div> +<div class="foildn"></div> +<div></div> +<div></div> +<div></div> diff --git a/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-001.html b/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-001.html new file mode 100644 index 0000000000..ca4f6033ce --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-001.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#unreachable" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + position: absolute; + margin: 0px; +} +#scroller { + height: 500px; + width: 500px; + overflow: hidden; + scroll-snap-type: both mandatory; +} +#unreachable { + width: 300px; + height: 300px; + top: -100px; + left: -100px; + background-color: blue; + scroll-snap-align: start; +} +#reachable { + width: 300px; + height: 300px; + top: 400px; + left: 400px; + background-color: blue; + scroll-snap-align: start; +} +</style> + +<div id="scroller"> + <div style="width: 2000px; height: 2000px;"></div> + <div id="unreachable"></div> + <div id="reachable"></div> +</div> + +<script> +test(() => { + // Firstly move to the reachable snap position. + scroller.scrollTo(400, 400); + assert_equals(scroller.scrollLeft, 400); + assert_equals(scroller.scrollTop, 400); + + // Then move to a position between the unreachable snap position and the + // reachable position but closer to the unreachable one. + scroller.scrollTo(100, 100); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Snaps to the positions defined by the element as much as possible"); +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-002.html b/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-002.html new file mode 100644 index 0000000000..a3726d71b3 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/unreachable-snap-positions-002.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#unreachable" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.scroller { + width: 100vw; + height: 100px; + display: flex; + scroll-snap-type: x mandatory; + overflow-x: auto; +} +.scroller.rtl { + direction: rtl; +} +.scroller.end > span { + scroll-snap-align: end; +} +.scroller.center > span { + scroll-snap-align: end; +} +</style> +<div class="scroller end"> + <span style="min-width: 25px;"></span> + <span style="min-width: 100vw;"></span> +</div> +<div class="scroller center"> + <span style="min-width: 25px;"></span> + <span style="min-width: 100vw;"></span> +</div> +<div class="scroller end rtl"> + <span style="min-width: 25px;"></span> + <span style="min-width: 100vw;"></span> +</div> +<div class="scroller center rtl"> + <span style="min-width: 25px;"></span> + <span style="min-width: 100vw;"></span> +</div> +<script> + +test(() => { + const scroller = document.querySelector(".scroller.end"); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Unreachable snap point with `scroll-snap-align: end`"); + +test(() => { + const scroller = document.querySelector(".scroller.center"); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Unreachable snap point with `scroll-snap-align: center`"); + +test(() => { + const scroller = document.querySelector(".scroller.end.rtl"); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Unreachable snap point with `scroll-snap-align: end` in RTL"); + +test(() => { + const scroller = document.querySelector(".scroller.center.rtl"); + assert_equals(scroller.scrollLeft, 0); + assert_equals(scroller.scrollTop, 0); +}, "Unreachable snap point with `scroll-snap-align: center` in RTL"); + +</script> +</html> |