Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,4 @@
features:
- name: scrollend
files:
- scrollend-*

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
<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>
<style>
body { margin: 0; padding: 10px; }
.space { height: 2000px; }
#scroller {
border: 3px solid green;
position: absolute;
z-index: 0;
overflow: auto;
padding: 10px;
width: 250px;
height: 150px;
}
.ifr {
border: 3px solid blue;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div id=scroller>
<iframe srcdoc="SCROLL ME" class=ifr></iframe>
<div class=space></div>
</div>
<div class=space></div>
<script>
promise_test(async t => {
await new test_driver.Actions().scroll(50, 50, 0, 50).send();
// Allow the possibility the scroll is not fully synchronous
await t.step_wait(() => scroller.scrollTop === 50);
}, "Wheel scroll in iframe chains to containing element.");
</script>
</body>
</html>

View File

@ -0,0 +1,71 @@
<!doctype html>
<html>
<head>
<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>
</head>
<body onload=runTest()>
<p>Moving the cursor using the arrow keys into an
input element fires scroll events when text has to scroll into view.
Uses arrow keys to move forward and backwards in the input
element.</p>
<input type="text" style='width: 50px'
value="Fooooooooooooooooooooooooooooooooooooooooooooooooo"/>
<textarea rows="4" cols="4">
Fooooooooooooooooooooooooooooooooooooooooooooooooo
</textarea>
<script>
async function moveCursorRightInsideElement(element, value){
var arrowRight = '\uE014';
for(var i=0;i<value;i++){
await test_driver.send_keys(element, arrowRight);
}
}
function runTest(){
promise_test(async(t) => { return new Promise(async (resolve, reject) => {
var input = document.getElementsByTagName('input')[0];
function handleScroll(){
resolve("Scroll Event successfully fired!");
}
input.addEventListener('scroll', handleScroll, false);
// move cursor to the right until the text scrolls
while(input.scrollLeft === 0){
await moveCursorRightInsideElement(input, 1);
}
// if there is no scroll event fired then test will fail by timeout
})},
/*
Moving the cursor using the arrow keys into an input element
fires scroll events when text has to scroll into view.
Uses arrow keys to move right in the input element.
*/
"Scroll event fired for <input> element.");
promise_test(async(t) => { return new Promise(async (resolve, reject) => {
var textarea = document.getElementsByTagName('textarea')[0];
function handleScroll(){
resolve("Scroll Event successfully fired!");
}
textarea.addEventListener('scroll', handleScroll, false);
// move cursor to the right until the text scrolls
while(textarea.scrollLeft === 0){
await moveCursorRightInsideElement(textarea, 1);
}
// if there is no scroll event fired then test will fail by timeout
})},
/*
Moving the cursor using the arrow keys into a textarea element
fires scroll events when text has to scroll into view.
Uses arrow keys to move right in the textarea element.
*/
"Scroll event fired for <textarea> element.");
}
</script>
</body>
</html>

View File

@ -0,0 +1,157 @@
<!DOCTYPE HTML>
<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="scroll_support.js"></script>
<style>
#hspacer {
height: 100px;
width: 100vw;
top: 0;
/* on the right edge of targetDiv */
left: 200px;
position: absolute;
}
#vspacer {
height: 100vh;
width: 100px;
position: absolute;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv"></div>
</div>
<div id="hspacer"></div>
<div id="vspacer"></div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
var overscrolled_x_deltas = [];
var overscrolled_y_deltas = [];
var scrollend_received = false;
function onOverscroll(event) {
overscrolled_x_deltas.push(event.deltaX);
overscrolled_y_deltas.push(event.deltaY);
}
async function resetScrollers(test) {
await waitForScrollReset(test, target_div);
await waitForScrollReset(test, document.scrollingElement);
}
function resetOverScrollDeltas() {
overscrolled_x_deltas = [];
overscrolled_y_deltas = [];
}
function waitForOverscrollEventWithMinDelta(target, min_x = 0, min_y = 0) {
return new Promise((resolve) => {
target.addEventListener("overscroll", (evt) => {
if (evt.deltaX >= min_x && evt.deltaY >= min_y) {
resolve();
}
});
});
}
function unreachedScrollendListener() {
assert_unreached('Unexpected scrollend event');
}
document.addEventListener("overscroll", onOverscroll);
function runTest() {
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
resetOverScrollDeltas();
assert_equals(document.scrollingElement.scrollTop, 0,
"document should not be scrolled");
let scrollend_promise = waitForScrollendEvent(t, target_div);
let max_target_div_scroll_top = target_div.scrollHeight - target_div.clientHeight;
target_div.scrollTop = target_div.scrollHeight;
await scrollend_promise;
assert_equals(target_div.scrollTop, max_target_div_scroll_top,
"target_div should be fully scrolled down");
// Even though we request 300 extra pixels of scroll, the API above doesn't
// guarantee how much scroll delta will be generated - different browsers
// can consume different amounts for "touch slop" (for example). Ensure the
// overscroll reaches at least 250 pixels which is a fairly conservative
// value.
let overscroll_promise = waitForOverscrollEventWithMinDelta(document,
/*min_x*/0, /*min_y*/250);
scrollend_promise = waitForScrollendEvent(t, document, 2000);
target_div.addEventListener("scrollend", unreachedScrollendListener);
// Scroll target div vertically and wait for the doc to get scrollend event.
await scrollElementDown(target_div, target_div.clientHeight + 300);
await waitForCompositorCommit();
await overscroll_promise;
await scrollend_promise;
target_div.removeEventListener("scrollend", unreachedScrollendListener);
assert_greater_than(overscrolled_y_deltas.length, 0, "There should be at least one overscroll events when overscrolling.");
assert_equals(overscrolled_x_deltas.filter(function (x) { return x != 0; }).length, 0, "The deltaX attribute must be 0 when there is no scrolling in x direction.");
assert_less_than_equal(Math.max(...overscrolled_y_deltas), 300, "The deltaY attribute must be <= the number of pixels overscrolled (300)");
assert_greater_than(document.scrollingElement.scrollTop, target_div.clientHeight - 1,
"document is scrolled by the height of target_div");
}, "testing, vertical");
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
resetOverScrollDeltas();
assert_equals(document.scrollingElement.scrollLeft, 0,
"document should not be scrolled");
let scrollend_promise = waitForScrollendEvent(t, target_div);
let max_target_div_scroll_left = target_div.scrollWidth - target_div.clientWidth;
target_div.scrollLeft = target_div.scrollWidth;
await scrollend_promise;
assert_equals(target_div.scrollLeft, max_target_div_scroll_left,
"target_div should be fully scrolled right");
let overscroll_promise = waitForOverscrollEventWithMinDelta(document,
/*min_x*/250, /*min_y*/ 0);
scrollend_promise = waitForScrollendEvent(t, document, 2000);
target_div.addEventListener("scrollend", unreachedScrollendListener);
// Scroll target div horizontally and wait for the doc to get scrollend event.
await scrollElementLeft(target_div, target_div.clientWidth + 300);
await waitForCompositorCommit();
await overscroll_promise;
await scrollend_promise;
target_div.removeEventListener("scrollend", unreachedScrollendListener);
assert_greater_than(document.scrollingElement.scrollLeft, target_div.clientWidth - 1,
"document is scrolled by the height of target_div");
// TODO(bokan): It looks like Chrome inappropriately filters some scroll
// events despite |overscroll-behavior| being set to none. The overscroll
// amount here has been loosened but this should be fixed in Chrome.
// https://crbug.com/1112183.
assert_greater_than(overscrolled_x_deltas.length, 0, "There should be at least one overscroll events when overscrolling.");
assert_equals(overscrolled_y_deltas.filter(function(x){ return x!=0; }).length, 0, "The deltaY attribute must be 0 when there is no scrolling in y direction.");
assert_less_than_equal(Math.max(...overscrolled_x_deltas), 300, "The deltaX attribute must be <= number of pixels overscrolled (300)");
}, "testing, horizontal");
}
</script>

View File

@ -0,0 +1,62 @@
<!DOCTYPE HTML>
<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="scroll_support.js"></script>
<style>
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
var overscrolled_x_delta = 0;
var overscrolled_y_delta = 0;
function onOverscroll(event) {
assert_false(event.cancelable);
// overscroll events are bubbled when the target node is document.
assert_true(event.bubbles);
overscrolled_x_delta = event.deltaX;
overscrolled_y_delta = event.deltaY;
}
document.addEventListener("overscroll", onOverscroll);
function runTest() {
promise_test (async (t) => {
// Make sure that no overscroll event is sent to target_div.
target_div.addEventListener("overscroll",
t.unreached_func("target_div got unexpected overscroll event."));
await waitForCompositorCommit();
// Scroll up on target div and wait for the doc to get overscroll event.
await touchScrollInTarget(300, target_div, 'up');
await waitFor(() => { return overscrolled_y_delta < 0; },
'Document did not receive overscroll event after scroll up on target.');
assert_equals(target_div.scrollTop, 0);
// Scroll left on target div and wait for the doc to get overscroll event.
await touchScrollInTarget(300, target_div, 'left');
await waitFor(() => { return overscrolled_x_delta < 0; },
'Document did not receive overscroll event after scroll left on target.');
assert_equals(target_div.scrollLeft, 0);
}, 'Tests that the document gets overscroll event when no element scrolls ' +
'after touch scrolling.');
}
</script>

View File

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<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="scroll_support.js"></script>
<style>
#overscrollXDiv {
width: 600px;
height: 600px;
overflow: scroll;
overscroll-behavior-x: contain;
}
#overscrollYDiv {
width: 500px;
height: 500px;
overflow: scroll;
overscroll-behavior-y: none;
}
#targetDiv {
width: 400px;
height: 400px;
overflow: scroll;
}
.content {
width:800px;
height:800px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="overscrollXDiv">
<div class=content>
<div id="overscrollYDiv">
<div class=content>
<div id="targetDiv">
<div class="content">
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
var overscrolled_x_delta = 0;
var overscrolled_y_delta = 0;
function onOverscrollX(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
overscrolled_x_delta = event.deltaX;
}
function onOverscrollY(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
overscrolled_y_delta = event.deltaY;
}
// Test that both "onoverscroll" and addEventListener("overscroll"... work.
document.getElementById('overscrollXDiv').onoverscroll = onOverscrollX;
document.getElementById('overscrollYDiv').
addEventListener("overscroll", onOverscrollY);
function runTest() {
promise_test (async (t) => {
// Make sure that no overscroll event is sent to document or target_div.
document.addEventListener("overscroll",
t.unreached_func("Document got unexpected overscroll event."));
target_div.addEventListener("overscroll",
t.unreached_func("target_div got unexpected overscroll event."));
await waitForCompositorCommit();
// Scroll up on target div and wait for the element with overscroll-y to get
// overscroll event.
await touchScrollInTarget(300, target_div, 'up');
await waitFor(() => { return overscrolled_y_delta < 0; },
'Expected element did not receive overscroll event after scroll up on ' +
'target.');
assert_equals(target_div.scrollTop, 0);
// Scroll left on target div and wait for the element with overscroll-x to
// get overscroll event.
await touchScrollInTarget(300, target_div, 'left');
await waitFor(() => { return overscrolled_x_delta < 0; },
'Expected element did not receive overscroll event after scroll left ' +
'on target.');
assert_equals(target_div.scrollLeft, 0);
}, 'Tests that the last element in the cut scroll chain gets overscroll ' +
'event when no element scrolls by touch.');
}
</script>

View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<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="scroll_support.js"></script>
<style>
#scrollableDiv {
width: 200px;
height: 200px;
overflow: scroll;
position: absolute;
left: 150px;
top: 150px;
}
#innerDiv {
width: 250px;
height: 250px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="scrollableDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var scrolling_div = document.getElementById('scrollableDiv');
var overscrolled_x_delta = 0;
var overscrolled_y_delta = 0;
function onOverscroll(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
overscrolled_x_delta = event.deltaX;
overscrolled_y_delta = event.deltaY;
}
scrolling_div.addEventListener("overscroll", onOverscroll);
function runTest() {
promise_test (async (t) => {
// Make sure that no overscroll event is sent to document.
document.addEventListener("overscroll",
t.unreached_func("Document got unexpected overscroll event."));
await waitForCompositorCommit();
// Do a horizontal scroll and wait for overscroll event.
await touchScrollInTarget(100, scrolling_div , 'right');
await waitFor(() => { return overscrolled_x_delta > 0; },
'Scroller did not receive overscroll event after horizontal scroll.');
assert_equals(scrolling_div.scrollWidth - scrolling_div.scrollLeft,
scrolling_div.clientWidth);
overscrolled_x_delta = 0;
overscrolled_y_delta = 0;
// Do a vertical scroll and wait for overscroll event.
await touchScrollInTarget(100, scrolling_div, 'down');
await waitFor(() => { return overscrolled_y_delta > 0; },
'Scroller did not receive overscroll event after vertical scroll.');
assert_equals(scrolling_div.scrollHeight - scrolling_div.scrollTop,
scrolling_div.clientHeight);
}, 'Tests that the scrolled element gets overscroll event after fully scrolling by touch.');
}
</script>

View File

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<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="scroll_support.js"></script>
<style>
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
var window_received_overscroll = false;
function onOverscroll(event) {
assert_false(event.cancelable);
// overscroll events targeting document are bubbled to the window.
assert_true(event.bubbles);
window_received_overscroll = true;
}
window.addEventListener("overscroll", onOverscroll);
function runTest() {
promise_test (async (t) => {
// Make sure that no overscroll event is sent to target_div.
target_div.addEventListener("overscroll",
t.unreached_func("target_div got unexpected overscroll event."));
// Scroll up on target div and wait for the window to get overscroll event.
await touchScrollInTarget(300, target_div, 'up');
await waitFor(() => { return window_received_overscroll; },
'Window did not receive overscroll event after scroll up on target.');
}, 'Tests that the window gets overscroll event when no element scrolls' +
'after touch scrolling.');
}
</script>

View File

@ -0,0 +1,278 @@
async function waitForEvent(eventName, test, target, timeoutMs = 500) {
return new Promise((resolve, reject) => {
const timeoutCallback = test.step_timeout(() => {
reject(`No ${eventName} event received for target ${target}`);
}, timeoutMs);
target.addEventListener(eventName, (evt) => {
clearTimeout(timeoutCallback);
resolve(evt);
}, { once: true });
});
}
async function waitForScrollendEvent(test, target, timeoutMs = 500) {
return waitForEvent("scrollend", test, target, timeoutMs);
}
async function waitForScrollendEventNoTimeout(target) {
return new Promise((resolve) => {
target.addEventListener("scrollend", resolve);
});
}
async function waitForPointercancelEvent(test, target, timeoutMs = 500) {
return waitForEvent("pointercancel", test, target, timeoutMs);
}
// Resets the scroll position to (0,0). If a scroll is required, then the
// promise is not resolved until the scrollend event is received.
async function waitForScrollReset(test, scroller, x = 0, y = 0) {
return new Promise(resolve => {
if (scroller.scrollTop == x && scroller.scrollLeft == y) {
resolve();
} else {
const eventTarget =
scroller == document.scrollingElement ? document : scroller;
scroller.scrollTo(x, y);
waitForScrollendEventNoTimeout(eventTarget).then(resolve);
}
});
}
async function createScrollendPromiseForTarget(test,
target_div,
timeoutMs = 500) {
return waitForScrollendEvent(test, target_div, timeoutMs).then(evt => {
assert_false(evt.cancelable, 'Event is not cancelable');
assert_false(evt.bubbles, 'Event targeting element does not bubble');
});
}
function verifyNoScrollendOnDocument(test) {
const callback =
test.unreached_func("window got unexpected scrollend event.");
window.addEventListener('scrollend', callback);
test.add_cleanup(() => {
window.removeEventListener('scrollend', callback);
});
}
async function verifyScrollStopped(test, target_div) {
const unscaled_pause_time_in_ms = 100;
const x = target_div.scrollLeft;
const y = target_div.scrollTop;
return new Promise(resolve => {
test.step_timeout(() => {
assert_equals(target_div.scrollLeft, x);
assert_equals(target_div.scrollTop, y);
resolve();
}, unscaled_pause_time_in_ms);
});
}
async function resetTargetScrollState(test, target_div) {
if (target_div.scrollTop != 0 || target_div.scrollLeft != 0) {
target_div.scrollTop = 0;
target_div.scrollLeft = 0;
return waitForScrollendEvent(test, target_div);
}
}
const MAX_FRAME = 700;
const MAX_UNCHANGED_FRAMES = 20;
// Returns a promise that resolves when the given condition is met or rejects
// after MAX_FRAME animation frames.
// TODO(crbug.com/1400399): deprecate. We should not use frame based waits in
// WPT as frame rates may vary greatly in different testing environments.
function waitFor(condition, error_message = 'Reaches the maximum frames.') {
return new Promise((resolve, reject) => {
function tick(frames) {
// We requestAnimationFrame either for MAX_FRAME frames or until condition
// is met.
if (frames >= MAX_FRAME)
reject(error_message);
else if (condition())
resolve();
else
requestAnimationFrame(tick.bind(this, frames + 1));
}
tick(0);
});
}
// TODO(crbug.com/1400446): Test driver should defer sending events until the
// browser is ready. Also the term compositor-commit is misleading as not all
// user-agents use a compositor process.
function waitForCompositorCommit() {
return new Promise((resolve) => {
// rAF twice.
window.requestAnimationFrame(() => {
window.requestAnimationFrame(resolve);
});
});
}
// Please don't remove this. This is necessary for chromium-based browsers. It
// can be a no-op on user-agents that do not have a separate compositor thread.
// TODO(crbug.com/1509054): This shouldn't be necessary if the test harness
// deferred running the tests until after paint holding.
async function waitForCompositorReady() {
const animation =
document.body.animate({ opacity: [ 0, 1 ] }, {duration: 1 });
return animation.finished;
}
function waitForNextFrame() {
const startTime = performance.now();
return new Promise(resolve => {
window.requestAnimationFrame((frameTime) => {
if (frameTime < startTime) {
window.requestAnimationFrame(resolve);
} else {
resolve();
}
});
});
}
// TODO(crbug.com/1400399): Deprecate as frame rates may vary greatly in
// different test environments.
function waitForAnimationEnd(getValue) {
var last_changed_frame = 0;
var last_position = getValue();
return new Promise((resolve, reject) => {
function tick(frames) {
// We requestAnimationFrame either for MAX_FRAME or until
// MAX_UNCHANGED_FRAMES with no change have been observed.
if (frames >= MAX_FRAME || frames - last_changed_frame > MAX_UNCHANGED_FRAMES) {
resolve();
} else {
current_value = getValue();
if (last_position != current_value) {
last_changed_frame = frames;
last_position = current_value;
}
requestAnimationFrame(tick.bind(this, frames + 1));
}
}
tick(0);
})
}
// Scrolls in target according to move_path with pauses in between
// The move_path should contains coordinates that are within target boundaries.
// Keep in mind that 0,0 is the center of the target element and is also
// the pointerDown position.
// pointerUp() is fired after sequence of moves.
function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_in_ms = 100) {
const test_driver_actions = new test_driver.Actions()
.addPointer("pointer1", "touch")
.pointerMove(0, 0, {origin: target})
.pointerDown();
const substeps = 5;
let x = 0;
let y = 0;
// Do each move in 5 steps
for(let move of move_path) {
let step_x = (move.x - x) / substeps;
let step_y = (move.y - y) / substeps;
for(let step = 0; step < substeps; step++) {
x += step_x;
y += step_y;
test_driver_actions.pointerMove(x, y, {origin: target});
}
test_driver_actions.pause(pause_time_in_ms); // To prevent inertial scroll
}
return test_driver_actions.pointerUp().send();
}
function touchScrollInTarget(pixels_to_scroll, target, direction, pause_time_in_ms = 100) {
var x_delta = 0;
var y_delta = 0;
const num_movs = 5;
if (direction == "down") {
y_delta = -1 * pixels_to_scroll / num_movs;
} else if (direction == "up") {
y_delta = pixels_to_scroll / num_movs;
} else if (direction == "right") {
x_delta = -1 * pixels_to_scroll / num_movs;
} else if (direction == "left") {
x_delta = pixels_to_scroll / num_movs;
} else {
throw("scroll direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
}
return new test_driver.Actions()
.addPointer("pointer1", "touch")
.pointerMove(0, 0, {origin: target})
.pointerDown()
.pointerMove(x_delta, y_delta, {origin: target})
.pointerMove(2 * x_delta, 2 * y_delta, {origin: target})
.pointerMove(3 * x_delta, 3 * y_delta, {origin: target})
.pointerMove(4 * x_delta, 4 * y_delta, {origin: target})
.pointerMove(5 * x_delta, 5 * y_delta, {origin: target})
.pause(pause_time_in_ms)
.pointerUp()
.send();
}
// Trigger fling by doing pointerUp right after pointerMoves.
function touchFlingInTarget(pixels_to_scroll, target, direction) {
return touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */);
}
function mouseActionsInTarget(target, origin, delta, pause_time_in_ms = 100) {
return new test_driver.Actions()
.addPointer("pointer1", "mouse")
.pointerMove(origin.x, origin.y, { origin: target })
.pointerDown()
.pointerMove(origin.x + delta.x, origin.y + delta.y, { origin: target })
.pointerMove(origin.x + delta.x * 2, origin.y + delta.y * 2, { origin: target })
.pause(pause_time_in_ms)
.pointerUp()
.send();
}
// Returns a promise that resolves when the given condition holds for 10
// animation frames or rejects if the condition changes to false within 10
// animation frames.
// TODO(crbug.com/1400399): Deprecate as frame rates may very greatly in
// different test environments.
function conditionHolds(condition, error_message = 'Condition is not true anymore.') {
const MAX_FRAME = 10;
return new Promise((resolve, reject) => {
function tick(frames) {
// We requestAnimationFrame either for 10 frames or until condition is
// violated.
if (frames >= MAX_FRAME)
resolve();
else if (!condition())
reject(error_message);
else
requestAnimationFrame(tick.bind(this, frames + 1));
}
tick(0);
});
}
function scrollElementDown(element, scroll_amount) {
let x = 0;
let y = 0;
let delta_x = 0;
let delta_y = scroll_amount;
let actions = new test_driver.Actions()
.scroll(x, y, delta_x, delta_y, {origin: element});
return actions.send();
}
function scrollElementLeft(element, scroll_amount) {
let x = 0;
let y = 0;
let delta_x = scroll_amount;
let delta_y = 0;
let actions = new test_driver.Actions()
.scroll(x, y, delta_x, delta_y, {origin: element});
return actions.send();
}

View File

@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 4000px;
height: 4000px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
const target_div = document.getElementById('targetDiv');
async function testWithMovePath(t, move_path) {
// Skip the test on a Mac as they do not support touch screens.
const isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
if (isMac)
return;
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
verifyNoScrollendOnDocument(t);
let scrollend_count = 0;
const scrollend_listener = () => { scrollend_count += 1; };
target_div.addEventListener("scrollend", scrollend_listener);
t.add_cleanup(() => { target_div.removeEventListener('scrollend', scrollend_listener); });
const pointercancel_listener = () => {
assert_equals(scrollend_count, 0, 'scrollend should happen after pointercancel.');
};
target_div.addEventListener("pointercancel", pointercancel_listener);
t.add_cleanup(() => { target_div.removeEventListener('pointercancel', pointercancel_listener); });
// Because we have several pointer moves, we choose bigger timeout.
const timeoutMs = 3000;
const targetPointercancelPromise = waitForPointercancelEvent(t, target_div, timeoutMs);
const targetScrollendPromise = createScrollendPromiseForTarget(t, target_div, timeoutMs);
await touchScrollInTargetSequentiallyWithPause(target_div, move_path);
// Because we start scrolling after pointerdown, there is no pointerup, instead the target
// will receive a pointercancel, so we wait for pointercancel, and then continue.
await targetPointercancelPromise;
await targetScrollendPromise;
await verifyScrollStopped(t, target_div);
assert_equals(scrollend_count, 1, 'Only one scrollend event should be fired');
}
function runTest() {
promise_test (async (t) => {
// Scroll down & up & down on target div and wait for the target_div to get scrollend event.
const move_path = [
{ x: 0, y: -80 }, // Scroll down
{ x: 0, y: -40 }, // Scroll up
{ x: 0, y: -80 }, // Scroll down
];
await testWithMovePath(t, move_path);
}, "Move down, up and down again, receive scrollend event only once");
promise_test (async (t) => {
// Scroll right & left & right on target div and wait for the target_div to get scrollend event.
const move_path = [
{ x: -80, y: 0 }, // Scroll right
{ x: -40, y: 0 }, // Scroll left
{ x: -80, y: 0 }, // Scroll right
];
await testWithMovePath(t, move_path);
}, "Move right, left and right again, receive scrollend event only once");
}
</script>

View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
div {
position: absolute;
}
#scroller {
width: 500px;
height: 500px;
overflow: scroll;
scroll-snap-type: both mandatory;
border: solid black 5px;
}
#space {
width: 2000px;
height: 2000px;
}
.target {
width: 200px;
height: 200px;
scroll-snap-align: start;
background-color: blue;
}
</style>
<body style="margin:0" onload=runTests()>
<div id="scroller">
<div id="space"></div>
<div class="target" style="left: 0px; top: 0px;"></div>
<div class="target" style="left: 80px; top: 80px;"></div>
<div class="target" style="left: 200px; top: 200px;"></div>
</div>
</body>
<script>
var scroller = document.getElementById("scroller");
var space = document.getElementById("space");
const MAX_FRAME_COUNT = 700;
const MAX_UNCHANGED_FRAME = 20;
function scrollTop() {
return scroller.scrollTop;
}
var scroll_arrived_after_scroll_end = false;
var scroll_end_arrived = false;
scroller.addEventListener("scroll", () => {
if (scroll_end_arrived)
scroll_arrived_after_scroll_end = true;
});
scroller.addEventListener("scrollend", () => {
scroll_end_arrived = true;
});
function runTests() {
promise_test (async () => {
await waitForCompositorCommit();
await touchScrollInTarget(100, scroller, 'down');
// Wait for the scroll snap animation to finish.
await waitForAnimationEnd(scrollTop);
await waitFor(() => { return scroll_end_arrived; });
// Verify that scroll snap animation has finished before firing scrollend event.
assert_false(scroll_arrived_after_scroll_end);
}, "Tests that scrollend is fired after scroll snap animation completion.");
promise_test (async () => {
// Reset scroll state.
scroller.scrollTo(0, 0);
await waitForCompositorCommit();
scroll_end_arrived = false;
scroll_arrived_after_scroll_end = false;
await touchFlingInTarget(50, scroller, 'down');
// Wait for the scroll snap animation to finish.
await waitForAnimationEnd(scrollTop);
await waitFor(() => { return scroll_end_arrived; });
// Verify that scroll snap animation has finished before firing scrollend event.
assert_false(scroll_arrived_after_scroll_end);
}, "Tests that scrollend is fired after fling snap animation completion.");
}
</script>

View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<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="scroll_support.js"></script>
<title>scrollend + mandatory scroll snap test</title>
<style>
#root {
width: 400px;
height: 400px;
overflow: auto;
scroll-snap-type: y mandatory;
border: 1px solid black;
--page-height: 400px;
}
#scroller {
height: 200px;
width: 200px;
overflow: auto;
border: 1px solid black;
--page-height: 200px;
}
.page {
height: var(--page-height);
scroll-snap-align: start;
}
.hidden {
display: none;
}
</style>
</head>
<body onload="runTests()">
<div id="root" class="hidden">
<h1>scrollend + mandatory scroll snap test</h1>
<div id="scroller">
<div class="page">
<p>Page 1</p>
</div>
<div class="page">
<p>Page 2</p>
</div>
<div class="page">
<p>Page 3</p>
</div>
</div>
<div class="page">
<p>Page A</p>
</div>
<div class="page">
<p>Page B</p>
</div>
<div class="page">
<p>Page C</p>
</div>
</div>
<script>
function runTests() {
const root_div = document.getElementById("root");
promise_test(async (t) => {
const targetScrollendPromise = createScrollendPromiseForTarget(t, root_div);
await waitForNextFrame();
root_div.classList.remove("hidden");
await waitForNextFrame();
await targetScrollendPromise;
await verifyScrollStopped(t, root_div);
}, "scrollend event fired after load for mandatory snap point");
}
</script>
</body>
</html>

View File

@ -0,0 +1,149 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/subset-tests-by-key.js"></script>
<meta name="variant" content="?include=subframe-scrollTo-auto"/>
<meta name="variant" content="?include=subframe-scrollTo-smooth"/>
<meta name="variant" content="?include=subframe-scrollBy-auto"/>
<meta name="variant" content="?include=subframe-scrollBy-smooth"/>
<meta name="variant" content="?include=root-scrollTo-auto"/>
<meta name="variant" content="?include=root-scrollTo-smooth"/>
<meta name="variant" content="?include=root-scrollBy-auto"/>
<meta name="variant" content="?include=root-scrollBy-smooth"/>
<script src="scroll_support.js"></script>
<style>
html {
height: 3000px;
width: 3000px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
let element_scrollend_arrived = false;
let document_scrollend_arrived = false;
function onElementScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
element_scrollend_arrived = true;
}
function onDocumentScrollEnd(event) {
assert_false(event.cancelable);
// scrollend events are bubbled when the target node is document.
assert_true(event.bubbles);
document_scrollend_arrived = true;
}
let scroll_fn_variants = [
{
key: "subframe-scrollTo-auto",
target: targetDiv,
fn: "scrollTo",
behavior: "auto",
title: "Tests scrollend event for calling scrollTo with behavior 'auto' on subframe."
},
{
key: "subframe-scrollTo-smooth",
target: targetDiv,
fn: "scrollTo",
behavior: "smooth",
title: "Tests scrollend event for calling scrollTo with behavior 'smooth' on subframe."
},
{
key: "subframe-scrollBy-auto",
target: targetDiv,
fn: "scrollBy",
behavior: "auto",
title: "Tests scrollend event for calling scrollBy with behavior 'auto' on subframe."
},
{
key: "subframe-scrollBy-smooth",
target: targetDiv,
fn: "scrollBy",
behavior: "smooth",
title: "Tests scrollend event for calling scrollBy with behavior 'smooth' on subframe."
},
{
key: "root-scrollTo-auto",
target: document.scrollingElement,
fn: "scrollTo",
behavior: "auto",
title: "Tests scrollend event for calling scrollTo with behavior 'auto' on root."
},
{
key: "root-scrollTo-smooth",
target: document.scrollingElement,
fn: "scrollTo",
behavior: "smooth",
title: "Tests scrollend event for calling scrollTo with behavior 'smooth' on root."
},
{
key: "root-scrollBy-auto",
target: document.scrollingElement,
fn: "scrollBy",
behavior: "auto",
title: "Tests scrollend event for calling scrollBy with behavior 'auto' on root."
},
{
key: "root-scrollBy-smooth",
target: document.scrollingElement,
fn: "scrollBy",
behavior: "smooth",
title: "Tests scrollend event for calling scrollBy with behavior 'smooth' on root."
},
];
function runTest() {
async function testScrollFn(testInfo, t) {
await waitForCompositorCommit();
targetDiv.addEventListener("scrollend", onElementScrollEnd);
document.addEventListener("scrollend", onDocumentScrollEnd);
testInfo.target[testInfo.fn]({ top: 200, left: 200,
behavior: testInfo.behavior });
await waitFor(() => {
return element_scrollend_arrived || document_scrollend_arrived;
}, testInfo.target.tagName + "." + testInfo.fn + " did not receive scrollend event.");
if (testInfo.target == document.scrollingElement) {
assert_false(element_scrollend_arrived);
} else {
assert_false(document_scrollend_arrived);
}
assert_equals(testInfo.target.scrollLeft, 200,
testInfo.target.tagName + "." + testInfo.fn + " scrollLeft");
assert_equals(testInfo.target.scrollTop, 200,
testInfo.target.tagName + "." + testInfo.fn + " scrollTop");
}
scroll_fn_variants.forEach((testInfo) => {
subsetTestByKey(testInfo.key, promise_test,
async (t) => testScrollFn(testInfo, t), testInfo.title);
});
}
</script>

View File

@ -0,0 +1,147 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/subset-tests-by-key.js"></script>
<meta name="variant" content="?include=subframe-scrollTop-smooth"/>
<meta name="variant" content="?include=subframe-scrollLeft-smooth"/>
<meta name="variant" content="?include=root-scrollTop-smooth"/>
<meta name="variant" content="?include=root-scrollLeft-smooth"/>
<meta name="variant" content="?include=subframe-scrollTop-auto"/>
<meta name="variant" content="?include=subframe-scrollLeft-auto"/>
<meta name="variant" content="?include=root-scrollTop-auto"/>
<meta name="variant" content="?include=root-scrollLeft-auto"/>
<script src="scroll_support.js"></script>
<style>
html {
height: 3000px;
width: 3000px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var element_scrollend_arrived = false;
var document_scrollend_arrived = false;
function onElementScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
element_scrollend_arrived = true;
}
function onDocumentScrollEnd(event) {
assert_false(event.cancelable);
// scrollend events are bubbled when the target node is document.
assert_true(event.bubbles);
document_scrollend_arrived = true;
}
let scroll_attr_change_variants = [
{
key: "subframe-scrollTop-smooth",
target: targetDiv,
behavior: "smooth",
attribute: "scrollTop",
title: "Tests scrollend event for [scrollTop] behavior:'smooth' on subframe."
},
{
key: "subframe-scrollLeft-smooth",
target: targetDiv,
behavior: "smooth",
attribute: "scrollLeft",
title: "Tests scrollend event for [scrollLeft] behavior:'smooth' on subframe."
},
{
key: "root-scrollTop-smooth",
target: document.scrollingElement,
behavior: "smooth",
attribute: "scrollTop",
title: "Tests scrollend event for [scrollTop] behavior:'smooth' on root."
},
{
key: "root-scrollLeft-smooth",
target: document.scrollingElement,
behavior: "smooth",
attribute: "scrollLeft",
title: "Tests scrollend event for [scrollLeft] behavior:'smooth' on root."
},
{
key: "subframe-scrollTop-auto",
target: targetDiv,
behavior: "auto",
attribute: "scrollTop",
title: "Tests scrollend event for [scrollTop] behavior:'auto' on subframe."
},
{
key: "subframe-scrollLeft-auto",
target: targetDiv,
behavior: "auto",
attribute: "scrollLeft",
title: "Tests scrollend event for [scrollLeft] behavior:'auto' on subframe."
},
{
key: "root-scrollTop-auto",
target: document.scrollingElement,
behavior: "auto",
attribute: "scrollTop",
title: "Tests scrollend event for [scrollTop] behavior:'auto' on root."
},
{
key: "root-scrollLeft-auto",
target: document.scrollingElement,
behavior: "auto",
attribute: "scrollLeft",
title: "Tests scrollend event for [scrollLeft] behavior:'auto' on root."
},
];
function runTest() {
async function testScrollAttrChange(testInfo, t) {
targetDiv.addEventListener("scrollend", onElementScrollEnd);
document.addEventListener("scrollend", onDocumentScrollEnd);
testInfo.target.style.scrollBehavior = testInfo.behavior;
await waitForCompositorCommit();
testInfo.target[testInfo.attribute] = 200;
await waitFor(() => {
return element_scrollend_arrived || document_scrollend_arrived;
}, testInfo.target.tagName + "." + testInfo.attribute + " did not receive scrollend event.");
if (testInfo.target == document.scrollingElement) {
assert_false(element_scrollend_arrived);
} else {
assert_false(document_scrollend_arrived);
}
assert_equals(testInfo.target[testInfo.attribute], 200,
testInfo.target.tagName + "." + testInfo.attribute + " " + testInfo.behavior);
}
scroll_attr_change_variants.forEach((testInfo) => {
subsetTestByKey(testInfo.key, promise_test,
async (t) => testScrollAttrChange(testInfo, t), testInfo.title);
})
}
</script>

View File

@ -0,0 +1,126 @@
<!DOCTYPE HTML>
<meta name="timeout" content="long">
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
html {
height: 3000px;
width: 3000px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var element_scrollend_arrived = false;
var document_scrollend_arrived = false;
function onElementScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
element_scrollend_arrived = true;
}
function onDocumentScrollEnd(event) {
assert_false(event.cancelable);
// scrollend events are bubbled when the target node is document.
assert_true(event.bubbles);
document_scrollend_arrived = true;
}
function callScrollFunction([scrollTarget, scrollFunction, args]) {
scrollTarget[scrollFunction](args);
}
function runTest() {
let root_element = document.scrollingElement;
let target_div = document.getElementById("targetDiv");
let inner_div = document.getElementById("innerDiv");
// Get expected position for root_element scrollIntoView.
root_element.scrollTo(10000, 10000);
let max_root_x = root_element.scrollLeft;
let max_root_y = root_element.scrollTop;
root_element.scrollTo(0, 0);
target_div.scrollTo(10000, 10000);
let max_element_x = target_div.scrollLeft;
let max_element_y = target_div.scrollTop;
target_div.scrollTo(0, 0);
promise_test (async (t) => {
await waitForCompositorCommit();
target_div.addEventListener("scrollend", onElementScrollEnd);
document.addEventListener("scrollend", onDocumentScrollEnd);
let test_cases = [
[target_div, max_element_x, max_element_y, [inner_div, "scrollIntoView", { inline: "end", block: "end", behavior: "auto" }]],
[target_div, 0, 0, [inner_div, "scrollIntoView", { inline: "start", block: "start", behavior: "smooth" }]],
[root_element, max_root_x, max_root_y, [root_element, "scrollIntoView", { inline: "end", block: "end", behavior: "smooth" }]],
[root_element, 0, 0, [root_element, "scrollIntoView", { inline: "start", block: "start", behavior: "smooth" }]]
];
for(i = 0; i < test_cases.length; i++) {
let t = test_cases[i];
let target = t[0];
let expected_x = t[1];
let expected_y = t[2];
let scroll_datas = t[3];
callScrollFunction(scroll_datas);
await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + ".scrollIntoView did not receive scrollend event.");
if (target == root_element)
assert_false(element_scrollend_arrived);
else
assert_false(document_scrollend_arrived);
assert_equals(target.scrollLeft, expected_x, target.tagName + ".scrollIntoView scrollLeft");
assert_equals(target.scrollTop, expected_y, target.tagName + ".scrollIntoView scrollTop");
element_scrollend_arrived = false;
document_scrollend_arrived = false;
}
}, "Tests scrollend event for scrollIntoView.");
promise_test(async (t) => {
document.body.removeChild(target_div);
let out_div = document.createElement("div");
out_div.style = "width: 100px; height:100px; overflow:scroll; scroll-behavior:smooth;";
out_div.appendChild(target_div);
document.body.appendChild(out_div);
await waitForCompositorCommit();
inner_div.scrollIntoView({ inline: "end", block: "end", behavior: "auto" });
const scrollend_events = [
waitForScrollendEventNoTimeout(out_div),
waitForScrollendEventNoTimeout(target_div)
];
await Promise.all(scrollend_events);
assert_equals(root_element.scrollLeft, 0, "Nested scrollIntoView root_element scrollLeft");
assert_equals(root_element.scrollTop, 0, "Nested scrollIntoView root_element scrollTop");
assert_equals(out_div.scrollLeft, 100, "Nested scrollIntoView out_div scrollLeft");
assert_equals(out_div.scrollTop, 100, "Nested scrollIntoView out_div scrollTop");
assert_equals(target_div.scrollLeft, max_element_x, "Nested scrollIntoView target_div scrollLeft");
assert_equals(target_div.scrollTop, max_element_y, "Nested scrollIntoView target_div scrollTop");
assert_false(document_scrollend_arrived);
}, "Tests scrollend event for nested scrollIntoView.");
}
</script>

View File

@ -0,0 +1,106 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#hspacer {
height: 100px;
width: 100vw;
top: 0;
left: 200px;
/* on the right edge od targetDiv */
position: absolute;
}
#vspacer {
height: 100vh;
width: 100px;
position: absolute;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv"></div>
</div>
<div id="hspacer"></div>
<div id="vspacer"></div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
async function resetScrollers(test) {
await waitForScrollReset(test, target_div);
await waitForScrollReset(test, document.scrollingElement);
}
function fail() {
assert_true(false);
}
function runTest() {
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
assert_equals(document.scrollingElement.scrollTop, 0,
"document should not be scrolled");
let scrollend_promise = waitForScrollendEvent(t, target_div);
let max_target_div_scroll_top = target_div.scrollHeight - target_div.clientHeight;
target_div.scrollTo({ top: target_div.scrollHeight, left: 0 });
await scrollend_promise;
assert_approx_equals(target_div.scrollTop, max_target_div_scroll_top, 1,
"target_div should be fully scrolled down");
scrollend_promise = waitForScrollendEvent(t, document, 2000);
target_div.addEventListener("scrollend", fail);
// Scroll up on target div and wait for the doc to get scrollend event.
await scrollElementDown(target_div, target_div.clientHeight + 25);
await scrollend_promise;
target_div.removeEventListener("scrollend", fail);
assert_greater_than(document.scrollingElement.scrollTop, target_div.clientHeight - 1,
"document is scrolled by the height of target_div");
}, "testing, vertical");
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
assert_equals(document.scrollingElement.scrollLeft, 0,
"document should not be scrolled");
let scrollend_promise = waitForScrollendEvent(t, target_div);
let max_target_div_scroll_left = target_div.scrollWidth - target_div.clientWidth;
target_div.scrollTo({ left: target_div.scrollWidth, top: 0 });
await scrollend_promise;
assert_approx_equals(target_div.scrollLeft, max_target_div_scroll_left, 1,
"target_div should be fully scrolled right");
scrollend_promise = waitForScrollendEvent(t, document, 2000);
target_div.addEventListener("scrollend", fail);
await scrollElementLeft(target_div, target_div.clientWidth + 25);
await scrollend_promise;
target_div.removeEventListener("scrollend", fail);
assert_greater_than(document.scrollingElement.scrollLeft, target_div.clientWidth - 1,
"document is scrolled by the height of target_div");
}, "testing, horizontal");
}
</script>

View File

@ -0,0 +1,173 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#overscrollXDiv {
width: 200px;
height: 200px;
overflow: scroll;
overscroll-behavior-x: contain;
border: solid 1px black;
display: grid;
/* Places content and targetXDiv beside each other. */
grid-template-columns: 500px 100px;
}
#overscrollYDiv {
width: 200px;
height: 200px;
overflow: scroll;
overscroll-behavior-y: none;
border: solid 1px black;
}
#targetXDiv {
width: 100px;
height: 100px;
overflow: scroll;
border: solid 1px black;
}
#targetYDiv {
width: 100px;
height: 100px;
overflow: scroll;
border: solid 1px black;
}
.content {
width: 500px;
height: 500px;
}
#spacer {
height: 200vh;
width: 200vw;
border: solid 1px black;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="overscrollXDiv">
<div class="content"></div>
<div id="targetXDiv">
<div class="content">
</div>
</div>
</div>
<div id="overscrollYDiv">
<div class="content"></div>
<!-- Place targetYDiv below content so that is in view when
overscrollYDiv is scrolled all the way down -->
<div id="targetYDiv">
<div class="content">
</div>
</div>
</div>
<div id="spacer"></div>
</body>
<script>
var horizontal_scrollend_arrived = false;
var vertical_scrollend_arrived = false;
let scrollers = [document.scrollingElement, targetXDiv, targetYDiv,
overscrollXDiv, overscrollYDiv];
async function resetScrollers(test) {
for (const scroller of scrollers) {
await resetTargetScrollState(test, scroller);
}
}
function onHorizontalScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
horizontal_scrollend_arrived = true;
}
function onVerticalScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
vertical_scrollend_arrived = true;
}
// Test that both "onscrollend" and addEventListener("scrollend"... work.
document.getElementById('overscrollXDiv').onscrollend = onHorizontalScrollEnd;
document.getElementById('overscrollYDiv').
addEventListener("scrollend", onVerticalScrollEnd);
function runTest() {
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
// Make sure that no scrollend event is sent to document.
document.addEventListener("scrollend",
t.unreached_func("Document got unexpected scrollend event."));
let scrollend_promise;
scrollend_promise = waitForScrollendEvent(t, targetXDiv, 2000);
targetXDiv.scrollLeft = targetXDiv.scrollWidth;
await scrollend_promise;
scrollend_promise = waitForScrollendEvent(t, overscrollXDiv, 2000);
overscrollXDiv.scrollLeft = overscrollXDiv.scrollWidth;
await scrollend_promise;
horizontal_scrollend_arrived = false;
assert_equals(targetXDiv.scrollLeft,
targetXDiv.scrollWidth - targetXDiv.clientWidth);
assert_equals(overscrollXDiv.scrollLeft,
overscrollXDiv.scrollWidth - overscrollXDiv.clientWidth);
// Attempt to scroll targetXDiv further to the right.
// targetXDiv and overscrollXDiv are already fully scrolled right but the
// scroll should not propagate to the document because of
// overscroll-behavior-x: contain on overscrollXDiv.
let touchEndPromise = new Promise((resolve) => {
targetXDiv.addEventListener("touchend", resolve);
});
await touchScrollInTarget(100, targetXDiv, 'right');
// The scrollend event should never be fired before the gesture has
// completed.
await touchEndPromise;
scrollend_promise = waitForScrollendEvent(t, targetYDiv, 2000);
targetYDiv.scrollTop = targetXDiv.scrollHeight;
await scrollend_promise;
scrollend_promise = waitForScrollendEvent(t, overscrollYDiv, 2000);
overscrollYDiv.scrollTop = overscrollYDiv.scrollHeight;
await scrollend_promise;
vertical_scrollend_arrived = false;
assert_equals(targetYDiv.scrollTop,
targetYDiv.scrollHeight - targetYDiv.clientHeight);
assert_equals(overscrollYDiv.scrollTop,
overscrollYDiv.scrollHeight - overscrollYDiv.clientHeight);
// Attempt to scroll targetYDiv further down.
// targetYDiv and overscrollYDiv are already fully scrolled down but the
// scroll should not propagate to the document because of
// overscroll-behavior-y: none on overscrollYDiv.
touchEndPromise = new Promise((resolve) => {
targetYDiv.addEventListener("touchend", resolve);
});
await touchScrollInTarget(50, targetYDiv, 'down');
// The scrollend event should never be fired before the gesture has
// completed.
await touchEndPromise;
// Ensure we wait at least a tick after the touch end.
await waitForCompositorCommit();
// We should not trigger a scrollend event for a scroll that did not
// change the scroll position.
assert_equals(horizontal_scrollend_arrived, false,
"overscrollXDiv should not receive scrollend");
assert_equals(vertical_scrollend_arrived, false,
"overscrollYDiv should not receive scrollend");
}, "Tests that the scroll is not propagated beyond div with non-auto " +
"overscroll-behavior.");
}
</script>

View File

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#spacer {
height: 100vh;
width: 100px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv"></div>
</div>
<div id="spacer"></div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
async function resetScrollers(test) {
await waitForScrollReset(test, target_div);
await waitForScrollReset(test, document.scrollingElement);
}
function fail() {
assert_true(false);
}
function runTest() {
promise_test(async (t) => {
await resetScrollers(t);
await waitForCompositorCommit();
assert_equals(document.scrollingElement.scrollTop, 0,
"document should not be scrolled");
let scrollend_promise = waitForScrollendEventNoTimeout(target_div);
let max_target_div_scroll_top = target_div.scrollHeight - target_div.clientHeight;
target_div.scrollTo({ top: target_div.scrollHeight, left: 0 });
await scrollend_promise;
assert_approx_equals(target_div.scrollTop, max_target_div_scroll_top, 1,
"target_div should be fully scrolled down");
scrollend_promise = waitForScrollendEventNoTimeout(window);
target_div.addEventListener("scrollend", fail);
// Scroll up on target div and wait for the doc to get scrollend event.
await scrollElementDown(target_div, target_div.clientHeight + 25);
await scrollend_promise;
target_div.removeEventListener("scrollend", fail);
assert_greater_than(document.scrollingElement.scrollTop, target_div.clientHeight - 1,
"document is scrolled by the height of target_div");
}, "testing, vertical");
}
</script>

View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html>
<head>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/visual-viewport/viewport_support.js"></script>
<script src="/dom/events/scrolling/scroll_support.js"></script>
</head>
<body>
<style>
.large {
height: 200vh;
width: 200vw;
border: solid 1px black;
}
</style>
<div class="large"></div>
<script>
window.onload = async () => {
async function pan_viewport_test(add_event_listener_func) {
const preScrollVisualViewportOffsetTop = visualViewport.offsetTop;
const preScrollWindowScrollOffset = window.scrollY;
const scrollend_promise = add_event_listener_func();
const scrollAmount = 50;
await touchScrollInTarget(scrollAmount, document.documentElement, "up");
await scrollend_promise;
assert_less_than(visualViewport.offsetTop,
preScrollVisualViewportOffsetTop,
`visualViewport should be scrolled.`);
assert_equals(window.scrollY, preScrollWindowScrollOffset,
"the window should not scroll.");
// No need to undo scroll; subsequent test has room to scroll further.
}
await waitForCompositorCommit();
await pinchZoomIn();
assert_greater_than(visualViewport.scale, 1, "page should be zoomed in.");
promise_test(async (t) => {
await pan_viewport_test(() => {
return new Promise((resolve) => {
visualViewport.addEventListener("scrollend", resolve, { once: true});
});
});
}, "scrollend listener added via addEventlistener fires when the visual " +
"viewport is panned.");
promise_test(async (t) => {
await pan_viewport_test((t) => {
return new Promise((resolve) => {
visualViewport.onscrollend = () => {
visualViewport.onscrollend = undefined;
resolve();
}
});
});
}, "visualviewport.onscrollend fires when the visual viewport is panned.");
}
</script>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<style>
body {
margin: 0px;
}
#spacer {
height: 120vh;
width: 120vw;
border: solid 1px black;
}
#scroller {
height: 200px;
width: 200px;
overflow: scroll;
border: solid 1px red;
}
#inner-spacer {
height: 400px;
width: 400px;
border: solid 1px black;
}
</style>
<body>
<div id="scroller" tabindex=0>
<div id="inner-spacer"></div>
</div>
<div id="spacer"></div>
</body>
</html>

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<script src="scrollend-user-scroll-common.js"></script>
<style>
iframe {
height: 300px;
width: 300px;
}
</style>
</head>
<body style="margin:0" onload=runTest()>
<iframe id="frame" src="scrollend-event-fires-to-iframe-inner-frame.html"></iframe>
</body>
<script>
function runTest() {
let target_div = frame.contentDocument.getElementById("scroller");
//Tests for scrollend events on an element within an iframe.
promise_test(async (t) => {
await test_scrollend_on_touch_drag(t, target_div);
}, 'Tests that the target_div within iframe gets scrollend event when touch ' +
'dragging.');
promise_test(async (t) => {
await test_scrollend_on_scrollbar_gutter_click(t, target_div);
}, 'Tests that the target_div within iframe gets scrollend event when ' +
'clicking scrollbar.');
// Same issue as previous test.
promise_test(async (t) => {
await test_scrollend_on_scrollbar_thumb_drag(t, target_div);
}, 'Tests that the target_div within iframe gets scrollend event when ' +
'dragging the scrollbar thumb.');
promise_test(async (t) => {
await test_scrollend_on_mousewheel_scroll(t, target_div, frame);
}, 'Tests that the target_div within iframe gets scrollend event when mouse ' +
'wheel scrolling.');
promise_test(async (t) => {
await test_scrollend_on_keyboard_scroll(t, target_div);
}, 'Tests that the target_div within iframe gets scrollend event when ' +
'sending DOWN key to the target.');
// Test for scrollend events on the iframe's window.
// TODO: add similar tests with different input modes.
promise_test(async (t) => {
let scroller = frame.contentDocument.scrollingElement;
await waitForScrollReset(t, scroller);
await waitForCompositorReady();
const targetScrollendPromise = waitForScrollendEventNoTimeout(frame.contentDocument);
verifyNoScrollendOnDocument(t);
let x = target_div.getBoundingClientRect().width + 20;
let y = 20;
let dy = 30;
await new test_driver.Actions().scroll(x, y, 0, dy).send();
await targetScrollendPromise;
assert_equals(scroller.scrollTop, dy, 'window scrolled by mousewheel');
}, 'scrollend fires to iframe window on mousewheelscroll');
}
</script>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<script src="scrollend-user-scroll-common.js"></script>
<style>
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
</head>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
function runTest() {
promise_test(async (t) => {
await test_scrollend_on_touch_drag(t, target_div);
}, 'Tests that the target_div gets scrollend event when touch dragging.');
promise_test(async (t) => {
await test_scrollend_on_scrollbar_gutter_click(t, target_div);
}, 'Tests that the target_div gets scrollend event when clicking ' +
'scrollbar.');
// Same issue as previous test.
promise_test(async (t) => {
await test_scrollend_on_scrollbar_thumb_drag(t, target_div);
}, 'Tests that the target_div gets scrollend event when dragging the ' +
'scrollbar thumb.');
promise_test(async (t) => {
await test_scrollend_on_mousewheel_scroll(t, target_div);
}, 'Tests that the target_div gets scrollend event when mouse wheel ' +
'scrolling.');
promise_test(async (t) => {
await test_scrollend_on_keyboard_scroll(t, target_div);
}, 'Tests that the target_div gets scrollend event when sending DOWN key ' +
'to the target.');
}
</script>
</html>

View File

@ -0,0 +1,108 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
html, body {
margin: 0
}
body {
height: 3000px;
width: 3000px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body onload=runTest() onscrollend="failOnScrollEnd(event)">
<div id="targetDiv" onscrollend="onElementScrollEnd(event)">
<div id="innerDiv">
</div>
</div>
</body>
<script>
let element_scrollend_arrived = false;
function onElementScrollEnd(event) {
assert_false(event.cancelable);
assert_false(event.bubbles);
element_scrollend_arrived = true;
}
function failOnScrollEnd(event) {
assert_true(false, "Scrollend should not be called on: " + event.target);
}
function runTest() {
let target_div = document.getElementById("targetDiv");
promise_test (async (t) => {
await waitForCompositorCommit();
target_div.scrollTo({top: 200, left: 200});
await waitFor(() => { return element_scrollend_arrived; },
target_div.tagName + " did not receive scrollend event.");
assert_equals(target_div.scrollLeft, 200, target_div.tagName + " scrollLeft");
assert_equals(target_div.scrollTop, 200, target_div.tagName + " scrollTop");
}, "Tests scrollend event is handled by event handler content attribute.");
promise_test (async (t) => {
await waitForCompositorCommit();
document.scrollingElement.scrollTo({top: 200, left: 200});
// The document body onscrollend event handler content attribute will fail
// here, if it is fired.
await waitForCompositorCommit();
assert_equals(document.scrollingElement.scrollLeft, 200,
"Document scrolled on horizontal axis");
assert_equals(document.scrollingElement.scrollTop, 200,
"Document scrolled on vertical axis");
}, "Tests scrollend event is not fired to document body event handler content attribute.");
promise_test (async (t) => {
await waitForCompositorCommit();
// Reset the scroll position.
document.scrollingElement.scrollTo({top: 0, left: 0});
let scrollend_event = new Promise(resolve => document.onscrollend = resolve);
document.scrollingElement.scrollTo({top: 200, left: 200});
await scrollend_event;
assert_equals(document.scrollingElement.scrollTop, 200,
"Document scrolled on horizontal axis");
assert_equals(document.scrollingElement.scrollLeft, 200,
"Document scrolled on vertical axis");
}, "Tests scrollend event is fired to document event handler property");
promise_test (async (t) => {
await waitForCompositorCommit();
// Reset the scroll position.
target_div.scrollTo({top: 0, left: 0});
let scrollend_event = new Promise(resolve => target_div.onscrollend = resolve);
target_div.scrollTo({top: 200, left: 200});
await scrollend_event;
assert_equals(target_div.scrollLeft, 200,
target_div.tagName + " scrolled on horizontal axis");
assert_equals(target_div.scrollLeft, 200,
target_div.tagName + " scrolled on vertical axis");
}, "Tests scrollend event is fired to element event handler property");
}
</script>

View File

@ -0,0 +1,84 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#rootDiv {
width: 500px;
height: 500px;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 500px;
height: 4000px;
}
</style>
<body style="margin:0" onload=runTest()>
</body>
<script>
let scrollend_arrived = false;
async function setupHtmlAndScrollAndRemoveElement(element_to_remove_id) {
document.body.innerHTML=`
<div id="rootDiv">
<div id="targetDiv">
<div id="innerDiv">
</div>
</div>
</div>
`;
await waitForCompositorCommit();
const target_div = document.getElementById('targetDiv');
const element_to_remove = document.getElementById(element_to_remove_id);
let reached_half_scroll = false;
scrollend_arrived = false;
target_div.addEventListener("scrollend", () => {
scrollend_arrived = true;
});
target_div.onscroll = () => {
// Remove the element after reached half of the scroll offset,
if(target_div.scrollTop >= 1000) {
reached_half_scroll = true;
element_to_remove.remove();
}
};
target_div.scrollTo({top:2000, left:0, behavior:"smooth"});
await waitFor(() => {return reached_half_scroll; },
"target_div never reached scroll offset of 1000");
await waitForCompositorCommit();
}
function runTest() {
promise_test (async (t) => {
await setupHtmlAndScrollAndRemoveElement("rootDiv");
await conditionHolds(() => { return !scrollend_arrived; });
}, "No scrollend is received after removing parent div");
promise_test (async (t) => {
await setupHtmlAndScrollAndRemoveElement("targetDiv");
await conditionHolds(() => { return !scrollend_arrived; });
}, "No scrollend is received after removing scrolling element");
promise_test (async (t) => {
await setupHtmlAndScrollAndRemoveElement("innerDiv");
await waitFor(() => { return scrollend_arrived; },
'target_div did not receive scrollend event after vertical scroll.');
}, "scrollend is received after removing descendant div");
}
</script>

View File

@ -0,0 +1,114 @@
<!DOCTYPE HTML>
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
#spacer {
height: 100vh;
width: 100px;
position: relative;
}
#targetDiv {
width: 200px;
height: 200px;
overflow: scroll;
}
#innerDiv {
width: 400px;
height: 400px;
}
</style>
<body style="margin:0" onload=runTest()>
<div id="targetDiv">
<!-- This test uses button elements as a consistent mechanism for
ensuring that focus is on the correct scrolling element when
scrolling via keys -->
<button id="targetButton">target</button>
<div id="innerDiv"></div>
</div>
<button id="docButton">doc</button>
<div id="spacer"></div>
</body>
<script>
var target_div = document.getElementById('targetDiv');
async function resetScrollers(test) {
await waitForScrollReset(test, target_div);
await waitForScrollReset(test, document.scrollingElement);
}
function getBoundingClientRect(element) {
if (element == document) {
return document.documentElement.getBoundingClientRect();
}
return element.getBoundingClientRect();
}
async function upwardScroll(scrolling_element, button_element, scroll_type) {
if (scroll_type == "wheel") {
let x = 0;
let y = 0;
let delta_x = 0;
let delta_y = -50;
let actions = new test_driver.Actions()
.scroll(x, y, delta_x, delta_y, {origin: scrolling_element});
await actions.send();
} else if (scroll_type == "keys") {
const num_keydowns = 5;
const arrowUp = '\uE013';
for (let i = 0; i < num_keydowns; i++) {
await test_driver.send_keys(button_element, arrowUp);
}
}
}
async function testScrollendNotFiredOnNoScroll(test, scrolling_element,
listening_element,
button_element, scroll_type) {
await resetScrollers(test);
await waitForCompositorCommit();
assert_greater_than(scrolling_element.scrollHeight,
scrolling_element.clientHeight);
assert_equals(scrolling_element.scrollTop, 0);
let scrollend_promise = waitForScrollendEvent(test, listening_element).then(
(/*resolve*/) => {
assert_true(false, "no scroll, so no scrollend expected");
},
(/*reject*/) => { /* Did not see scrollend, which is okay. */ }
);
await upwardScroll(scrolling_element, button_element, scroll_type);
await scrollend_promise;
}
function runTest() {
promise_test(async (t) => {
await testScrollendNotFiredOnNoScroll(t, target_div, target_div,
targetButton, "wheel");
}, "No scroll via wheel on div shouldn't fire scrollend.");
promise_test(async (t) => {
await testScrollendNotFiredOnNoScroll(t, target_div, target_div,
targetButton, "keys");
}, "No scroll via keys on div shouldn't fire scrollend.");
promise_test(async (t) => {
await testScrollendNotFiredOnNoScroll(t, document.scrollingElement,
document, docButton, "wheel");
}, "No scroll via wheel on document shouldn't fire scrollend.");
promise_test(async (t) => {
await testScrollendNotFiredOnNoScroll(t, document.scrollingElement,
document, docButton, "keys");
}, "No scroll via keys on document shouldn't fire scrollend.")
}
</script>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<style>
#inputscroller {
width: 100px;
height: 50px;
}
</style>
<input type="text" id="inputscroller"
value="qwertyuiopasddfghjklzxcvbnmqwertyuiopasddfghjklzxcvbnmqwer">
<script>
promise_test(async() => {
const inputscroller = document.getElementById("inputscroller");
assert_equals(inputscroller.scrollLeft, 0,
"text input field is not initially scrolled.");
const scrollend_promise = new Promise((resolve) => {
inputscroller.addEventListener("scrollend", resolve);
});
inputscroller.scrollLeft = 10;
await scrollend_promise;
assert_equals(inputscroller.scrollLeft, 10,
"text input field is scrolled by the correct amount");
}, "scrolled input field should receive scrollend.");
</script>
</body>
</html>

View File

@ -0,0 +1,150 @@
async function test_scrollend_on_touch_drag(t, target_div) {
// Skip the test on a Mac as they do not support touch screens.
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (isMac)
return;
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div);
verifyNoScrollendOnDocument(t);
let scrollend_count = 0;
const scrollend_listener = () => {
scrollend_count += 1;
};
target_div.addEventListener("scrollend", scrollend_listener);
t.add_cleanup(() => {
target_div.removeEventListener('scrollend', scrollend_listener);
});
// Perform a touch drag on target div and wait for target_div to get
// a scrollend event.
await new test_driver.Actions()
.addPointer('TestPointer', 'touch')
.pointerMove(0, 0, { origin: target_div }) // 0, 0 is center of element.
.pointerDown()
.addTick()
.pointerMove(0, -40, { origin: target_div }) // Drag up to move down.
.addTick()
.pause(200) // Prevent inertial scroll.
.pointerMove(0, -60, { origin: target_div })
.addTick()
.pause(200) // Prevent inertial scroll.
.pointerUp()
.send();
await targetScrollendPromise;
assert_true(target_div.scrollTop > 0);
await verifyScrollStopped(t, target_div);
assert_equals(scrollend_count, 1);
}
async function test_scrollend_on_scrollbar_gutter_click(t, target_div) {
// Skip test on platforms that do not have a visible scrollbar (e.g.
// overlay scrollbar).
const scrollbar_width = target_div.offsetWidth - target_div.clientWidth;
if (scrollbar_width == 0)
return;
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div);
verifyNoScrollendOnDocument(t);
const bounds = target_div.getBoundingClientRect();
// Some versions of webdriver have been known to frown at non-int arguments
// to pointerMove.
const x = Math.round(bounds.right - scrollbar_width / 2);
const y = Math.round(bounds.bottom - 20);
await new test_driver.Actions()
.addPointer('TestPointer', 'mouse')
.pointerMove(x, y, { origin: 'viewport' })
.pointerDown()
.addTick()
.pointerUp()
.send();
await targetScrollendPromise;
assert_true(target_div.scrollTop > 0);
await verifyScrollStopped(t, target_div);
}
// Same issue as previous test.
async function test_scrollend_on_scrollbar_thumb_drag(t, target_div) {
// Skip test on platforms that do not have a visible scrollbar (e.g.
// overlay scrollbar).
const scrollbar_width = target_div.offsetWidth - target_div.clientWidth;
if (scrollbar_width == 0)
return;
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div);
verifyNoScrollendOnDocument(t);
const bounds = target_div.getBoundingClientRect();
// Some versions of webdriver have been known to frown at non-int arguments
// to pointerMove.
const x = Math.round(bounds.right - scrollbar_width / 2);
const y = Math.round(bounds.top + 30);
const dy = 30;
await new test_driver.Actions()
.addPointer('TestPointer', 'mouse')
.pointerMove(x, y, { origin: 'viewport' })
.pointerDown()
.pointerMove(x, y + dy, { origin: 'viewport' })
.addTick()
.pointerUp()
.send();
await targetScrollendPromise;
assert_true(target_div.scrollTop > 0);
await verifyScrollStopped(t, target_div);
}
async function test_scrollend_on_mousewheel_scroll(t, target_div, frame) {
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div);
verifyNoScrollendOnDocument(t);
let scroll_origin = target_div;
if (frame) {
// chromedriver doesn't support passing { origin: element }
// for an element within a subframe. Use the frame element itself.
scroll_origin = frame;
}
const x = 0;
const y = 0;
const dx = 0;
const dy = 40;
await new test_driver.Actions()
.scroll(x, y, dx, dy, { origin: scroll_origin })
.send();
await targetScrollendPromise;
assert_true(target_div.scrollTop > 0);
await verifyScrollStopped(t, target_div);
}
async function test_scrollend_on_keyboard_scroll(t, target_div) {
await resetTargetScrollState(t, target_div);
await waitForCompositorReady();
verifyNoScrollendOnDocument(t);
const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div);
target_div.focus();
window.test_driver.send_keys(target_div, '\ue015');
await targetScrollendPromise;
assert_true(target_div.scrollTop > 0);
await verifyScrollStopped(t, target_div);
}

View File

@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="scroll_support.js"></script>
<body>
<style>
.scroller {
scroll-snap-type: x mandatory;
overflow-x: auto;
overflow-y: hidden;
position: relative;
height: 500px;
width: 500px;
}
.box {
scroll-snap-align: start;
width: 400px;
position: absolute;
top: 200px;
}
#box1 {
background-color: red;
height: 500px;
}
#box2 {
background-color: yellow;
height: 300px;
left: 700.5px;
}
#box3 {
background-color: blue;
height: 100px;
left: 1400px;
}
</style>
<div id="scroller" class="scroller">
<div class="box" id="box1">1</div>
<div class="box" id="box2">2</div>
<div class="box" id="box3">3</div>
</div>
<script>
let scrollendCount = 0;
scroller.addEventListener('scrollend', () => {
scroller.style.maxHeight = null;
scroller.style.maxHeight = `${box2.clientHeight}px`;
scrollendCount += 1;
});
promise_test(async (test) => {
// This test aims to verify that scrollend fires correctly (i.e. once)
// when the target snap position is not a whole number. In this case, we
// expect to snap to the left edge of box2 which is at a fractional
// offset from the scroller's origin (left: 700.5px).
// The scroll offset resulting from the snap may not be fractional
// (e.g. if the browser does not support fractional scroll offsets) so
// we verify the scroll offset with assert_approx_equals.
assert_equals(scroller.scrollLeft, 0,
"test precondition: scroller is not scrolled.");
const expected_scroll_left = box2.offsetLeft;
const target_offset = box2.offsetLeft + box2.clientWidth / 2;
let scrollend_promise = waitForScrollendEvent(test, scroller);
scroller.scrollTo( { left: target_offset });
await scrollend_promise;
// Instead of a time-based wait for errant scrollends, we wait a frame
// and then scroll back to 0.
await waitForCompositorCommit();
assert_approx_equals(scroller.scrollLeft, expected_scroll_left, 1,
"scroller snaps to the left edge of box 2");
scrollend_promise = waitForScrollendEvent(test, scroller);
scroller.scrollTo({ left: 0 });
await scrollend_promise;
assert_equals(scroller.scrollLeft, 0,
"scroller should be scrolled back to 0.");
assert_equals(scrollendCount, 2, "exactly 2 scrollends should be seen");
}, "snap to fractional offset fires scrollend exactly once.");
</script>
</body>
</html>

View File

@ -0,0 +1,140 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<meta name="variant" content="?include=target-basic"/>
<meta name="variant" content="?include=scroll-over-scrollable-child"/>
<meta name="variant" content="?include=transaction-not-bound-to-scroll-frame"/>
<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="/common/subset-tests-by-key.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
#scrollableDiv {
width: 100%;
height: 500px;
overflow: scroll;
background: yellow;
}
#innerDiv {
width: 100%;
height: 4000px;
background: red;
}
</style>
<head>
<body onload=runTest()>
<div id="firstRootSpacer" class="spacer" style="background: cyan"></div>
<div id="secondRootSpacer" class="spacer" style="background: magenta"></div>
<div id="scrollableDiv">
<div id="innerDiv">
<div id="firstInnerSpacer" class="spacer" style="background: green">
</div>
<div id="secondInnerSpacer" class="spacer" style="background: blue">
</div>
</div>
</div>
</body>
<script>
let variants = [
// Ensure that the wheel transaction fires all wheel events to the initial
// target.
{
key: 'target-basic',
origin: "viewport",
scrollX: 40,
scrollY: 2,
scrollingElement: document.scrollingElement,
targetElement: firstRootSpacer,
title: 'Wheel events should be captured to one target #1',
},
// Ensure that the wheel transaction fires all wheel events to the initial
// target, even when another scroll frame is under the mouse cursor.
{
key: 'scroll-over-scrollable-child',
origin: "viewport",
scrollX: 40,
scrollY: 27,
scrollingElement: document.scrollingElement,
targetElement: secondRootSpacer,
title: 'Wheel events should be captured to one target #2',
},
// Ensure that the wheel transaction targets the topmost-element, which is
// not the scrollable element.
{
key: 'transaction-not-bound-to-scroll-frame',
origin: innerDiv,
scrollX: 40,
scrollY: 2,
scrollingElement: scrollableDiv,
targetElement: innerDiv,
title: 'The wheel event transactions target may not be a scroll frame',
},
];
function runTest() {
async function testBasic(testInfo, t) {
await waitForCompositorReady();
await waitForCompositorCommit();
let wheelEventTargets = [];
function pushTargetToWheelEventTargets(e) {
wheelEventTargets.push(e.target)
}
window.addEventListener("wheel", pushTargetToWheelEventTargets, {passive: true});
// Scroll past the boundary of the original element to ensure all events in
// transaction have the same target.
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(testInfo.scrollX, testInfo.scrollY, 0, 30, {origin: testInfo.origin})
.pause(1)
.scroll(testInfo.scrollX, testInfo.scrollY, 0, 30, {origin: testInfo.origin})
.pause(1)
.scroll(testInfo.scrollX, testInfo.scrollY, 0, 30, {origin: testInfo.origin})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return testInfo.scrollingElement.scrollTop; });
await waitForCompositorCommit();
// Ensure that all the captured wheel events are the expected target.
wheelEventTargets.forEach((wheelEventTarget, i) => {
assert_equals(wheelEventTarget, testInfo.targetElement,
"Wheel event at index `" + i + "` does not have the expected target");
});
assert_greater_than(testInfo.scrollingElement.scrollTop, 0,
"The scrolling element has scrolled");
}
variants.forEach((testInfo) => {
subsetTestByKey(testInfo.key, promise_test, t => testBasic(testInfo, t), testInfo.title);
});
}
</script>
</html>

View File

@ -0,0 +1,101 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
#initial {
background: red;
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
#second {
background: green;
width: 100%;
height: 200vh;
padding: 0;
margin: 0;
}
</style>
<head>
<body>
<div id="initial"></div>
<div id="second"></div>
</body>
<script>
promise_test(async (t) => {
await new Promise(resolve => addEventListener("load", resolve, {once: true}));
await waitForCompositorReady();
await waitForCompositorCommit();
let firstEventTargets = [];
let secondEventTargets = [];
let isFirstGroup = true;
window.addEventListener("wheel", (e) => {
if (isFirstGroup) {
firstEventTargets.push(e.target);
} else {
secondEventTargets.push(e.target);
}
}, {passive: true});
await waitForCompositorCommit();
// The first action chain should target the initial element
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(40, 2, 0, 30, {origin: "viewport"})
.send();
// Start logging event targets in the second transaction
isFirstGroup = false;
// The second chain should target the second element and the prior wheel event
// group should have no impact on this action chain.
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(40, 30, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
assert_greater_than(firstEventTargets.length, 0,
"There should be at least one event in the first transaction");
assert_greater_than(secondEventTargets.length, 0,
"There should be at least one event in the second transaction");
firstEventTargets.forEach((wheelEventTarget, i) => {
assert_equals(wheelEventTarget, initial,
"Wheel event at index `" + i + "` did not target the initial element");
});
secondEventTargets.forEach((wheelEventTarget, i) => {
assert_equals(wheelEventTarget, second,
"Wheel event at index `" + i + "` did not target the second element");
});
}, "Two separate webdriver action chains should have different wheel event transactions");
</script>
</html>

View File

@ -0,0 +1,100 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<meta name="variant" content="?include=none"/>
<meta name="variant" content="?include=contents"/>
<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="/common/subset-tests-by-key.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
</style>
<head>
<body onload=runTest()>
<div id="initialTarget" class="spacer" style="background: red"></div>
<div id="firstRootSpacer" class="spacer" style="background: green"></div>
<div id="secondRootSpacer" class="spacer" style="background: blue"></div>
</body>
<script>
let variants = [
"none",
"contents",
]
function runTest() {
async function testDisplayChange(display, t) {
await waitForCompositorReady();
await waitForCompositorCommit();
let wheelEventTargets = [];
let makeInitialTargetNone = false;
// Modify the initial element the wheel event is targetted at to
// display: <variant> once we fire the first wheel event, then log
// the subsequent wheel event targets.
function makeInitialElementNone(e) {
wheelEventTargets.push(e.target);
if (!makeInitialTargetNone) {
makeInitialTargetNone = true;
e.target.style.display = display;
}
}
window.addEventListener("wheel", makeInitialElementNone, {passive: true});
await waitForCompositorCommit();
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(40, 2, 0, 30, {origin: "viewport"})
.pause(1)
.scroll(40, 2, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
// The first wheel event should be targetted at the modified element.
assert_equals(wheelEventTargets.shift(), initialTarget,
"Initial wheel event is has the modified element as the target");
wheelEventTargets.forEach((wheelEventTarget, i) => {
// TODO(dlrobertson): This assertion is pretty weak, but browsers seem to disagree
// on what element the event should target. Find out what the target should be here
// and make this assertion more restrictive.
assert_not_equals(wheelEventTarget, initialTarget,
"Wheel event at index `" + i + "` targetted the initial element");
});
assert_greater_than(document.scrollingElement.scrollTop, 0, "The document has scrolled");
}
variants.forEach((variant) => {
subsetTestByKey(variant, promise_test, t => testDisplayChange(variant, t),
"Modify the initial wheel event target to display:" + variant);
});
}
</script>
</html>

View File

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css"/>
<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="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
font: 25px/1 Ahem;
}
</style>
<head>
<body>
<div id="firstRootSpacer" class="spacer" style="background: green">X</div>
<div id="secondRootSpacer" class="spacer" style="background: blue"></div>
</body>
<script>
promise_test(async (t) => {
// Wheel event transactions target elements, not nodes. A wheel event
// transaction that begins over a text node should have an event target for
// the containing element.
await waitForCompositorReady();
await waitForCompositorCommit();
let wheelEventTargets = [];
window.addEventListener("wheel", (e) => {
wheelEventTargets.push(e.target);
}, {passive: true});
await waitForCompositorCommit();
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(12, 12, 0, 30, {origin: "viewport"})
.pause(1)
.scroll(12, 12, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
// All of the wheel events should have the first div as their target.
wheelEventTargets.forEach((wheelEventTarget, i) => {
assert_equals(wheelEventTarget, firstRootSpacer,
"Wheel event at index `" + i + "` does not have the expected target");
});
assert_greater_than(document.scrollingElement.scrollTop, 0, "The document has scrolled");
}, "Wheel event transactions target elements");
</script>
</html>

View File

@ -0,0 +1,85 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
</style>
<head>
<body>
<div id="initialTarget" class="spacer" style="background: red"></div>
<div id="firstRootSpacer" class="spacer" style="background: green"></div>
<div id="secondRootSpacer" class="spacer" style="background: blue"></div>
</body>
<script>
promise_test(async (t) => {
await new Promise(resolve => addEventListener("load", resolve, {once: true}));
await waitForCompositorReady();
await waitForCompositorCommit();
let wheelEventTargets = [];
let movedInitialTarget = false;
// Move the initial element the wheel event is targetted at once we fire the
// first wheel event, then log the subsequent wheel event targets.
function moveInitialElement(e) {
wheelEventTargets.push(e.target);
if (!movedInitialTarget) {
movedInitialTarget = true;
secondRootSpacer.insertAdjacentElement('afterend', e.target);
}
}
window.addEventListener("wheel", moveInitialElement, {passive: true});
await waitForCompositorCommit();
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(40, 2, 0, 30, {origin: "viewport"})
.pause(1)
.scroll(40, 2, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
// The first wheel event should be targetted at the moved element.
assert_equals(wheelEventTargets.shift(), initialTarget,
"Initial wheel event has the moved element as the target");
wheelEventTargets.forEach((wheelEventTarget, i) => {
// TODO(dlrobertson): This assertion is pretty weak, but browsers seem to disagree
// on what element the event should target. Find out what the target should be here
// and make this assertion more restrictive.
assert_not_equals(wheelEventTarget, initialTarget,
"Wheel event at index `" + i + "` targetted the initial element");
});
assert_greater_than(document.scrollingElement.scrollTop, 0, "The document has scrolled");
}, "Move the initial wheel event target.");
</script>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<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>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
</style>
<head>
<body>
<div id="initialTarget" class="spacer" style="background: red"></div>
<div id="firstRootSpacer" class="spacer" style="background: green"></div>
<div id="secondRootSpacer" class="spacer" style="background: blue"></div>
</body>
<script>
promise_test(async (t) => {
await new Promise(resolve => addEventListener("load", resolve, {once: true}));
await waitForCompositorReady();
await waitForCompositorCommit();
let initialTarget = null;
let wheelEventTargets = [];
let removedInitialTarget = false;
// Remove the initial element the wheel event is targetted at once we fire the
// first wheel event, then log the subsequent wheel event targets.
function removeInitialElement(e) {
wheelEventTargets.push(e.target);
if (!removedInitialTarget) {
initialTarget = e.target;
e.target.remove();
removedInitialTarget = true;
initialTarget.addEventListener("wheel", () => {
assert_true(false, "wheel event should never be fired after the target is removed")
}, {passive: true});
}
}
window.addEventListener("wheel", removeInitialElement, {passive: true});
await waitForCompositorCommit();
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(40, 2, 0, 30, {origin: "viewport"})
.pause(1)
.scroll(40, 2, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
assert_true(removedInitialTarget, "Removed the initial target");
// The first wheel event should be targetted at the removed element.
assert_equals(wheelEventTargets.shift(), initialTarget,
"Initial wheel event has the removed element as the target");
wheelEventTargets.forEach((wheelEventTarget, i) => {
// TODO(dlrobertson): This assertion is pretty weak, but browsers seem to disagree
// on what element the event should target. Find out what the target should be here
// and make this assertion more restrictive.
assert_not_equals(wheelEventTarget, initialTarget,
"Wheel event at index `" + i + "` targetted the initial element");
});
assert_greater_than(document.scrollingElement.scrollTop, 0, "The document has scrolled");
}, "Remove the initial wheel event target.");
</script>
</html>

View File

@ -0,0 +1,97 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<meta name="variant" content="?include=passive-false"/>
<meta name="variant" content="?include=passive-true"/>
<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="/common/subset-tests-by-key.js"></script>
<script src="scroll_support.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 200vh;
}
.spacer {
width: 100%;
height: 25px;
padding: 0;
margin: 0;
}
</style>
<head>
<body onload=runTest()>
<div id="initialTarget" class="spacer" style="background: red"></div>
<div id="firstRootSpacer" class="spacer" style="background: green"></div>
<div id="secondRootSpacer" class="spacer" style="background: blue"></div>
</body>
<script>
let variants = [
{
key: "passive-false",
passive: false,
},
{
key: "passive-true",
passive: true,
},
];
function runTest() {
async function testResizeTarget(passive, t) {
await waitForCompositorReady();
await waitForCompositorCommit();
let wheelEventTargets = [];
let resizedInitialTarget = false;
// Resize the initial element the wheel event is targetted at once we fire the
// first wheel event, then log the subsequent wheel event targets.
function resizeInitialElement(e) {
wheelEventTargets.push(e.target);
if (!resizedInitialTarget) {
resizedInitialTarget = true;
e.target.style.height = '10px';
}
}
window.addEventListener("wheel", resizeInitialElement, {passive: passive});
await waitForCompositorCommit();
await new test_driver.Actions()
.addWheel("wheel1")
.scroll(2, 2, 0, 30, {origin: "viewport"})
.pause(1)
.scroll(2, 2, 0, 30, {origin: "viewport"})
.send();
// TODO(dlrobertson): Use the scrollend event here to wait for the
// wheel scroll to finish instead of waitForAnimationEnd().
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();
wheelEventTargets.forEach((wheelEventTarget, i) => {
assert_equals(wheelEventTarget, initialTarget,
"Wheel event at `" + i + "` does not match the expected target")
});
assert_greater_than(document.scrollingElement.scrollTop, 0, "The document has scrolled");
}
variants.forEach((variant) => {
subsetTestByKey(variant.key, promise_test, t => testResizeTarget(variant.passive, t),
"Resize the initial target and use a passive:" + variant.passive + " listener");
});
}
</script>
</html>