script: Fire scroll event whenever JS scrolled (#38321)

Implement JS scroll event firing compliant to
https://drafts.csswg.org/cssom-view/#scrolling-events. Basically
whenever, the an element or the viewport is scrolled, we will fire a
scroll event. The changes push a scroll event whenever an API causes a
scroll position to change.

Testing: New WPT tests for basic APIs.
Part of: https://github.com/servo/servo/issues/31665

---------

Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
This commit is contained in:
Jo Steven Novaryo 2025-08-01 15:30:22 +08:00 committed by GitHub
parent 372e5eae59
commit a063b5e78a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 407 additions and 74 deletions

View file

@ -467655,6 +467655,10 @@
"5df4fa279363798f2f471467ed674c092c7a1749",
[]
],
"large-dimension-document.sub.html": [
"5d29f7be0bbb77e6303598aef833a810e84b6e80",
[]
],
"prefixed-animation-event-tests.js": [
"021b6bb9dfdc422d1a6c3c9c4a1a039f89a901d5",
[]
@ -633745,6 +633749,20 @@
}
]
],
"scroll-event-fired-to-element.html": [
"3484a2ec92b2bd8faa1d5147568f13a83b8c59d9",
[
null,
{}
]
],
"scroll-event-fired-to-iframe.html": [
"2ff3de79d33fa0a676a0eda4f109b6d0e634cc9d",
[
null,
{}
]
],
"scrollend-event-fired-after-sequence-of-scrolls.tentative.html": [
"dab6dcc9bd8d67a514ca95daf3e1d64b10a45188",
[

View file

@ -1,4 +0,0 @@
[new-scroll-event-dispatched-at-next-updating-rendering-time.html]
expected: TIMEOUT
[new-scroll-event-dispatched-at-next-updating-rendering-time]
expected: TIMEOUT

View file

@ -0,0 +1,5 @@
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<div style="width: 10000px; height: 10000px;"></div>
</body>

View file

@ -0,0 +1,100 @@
<!doctype html>
<meta charset=utf-8>
<title>Scroll event should behave correctly for Element.offsetTop and Element.offsetLeft</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="scroll_support.js"></script>
<link rel="author" title="Jo Steven Novaryo" href="mailto:jo.steven.novaryo@huawei.com">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-events">
<div id=log></div>
<div id="container">
</div>
<script>
function setupTarget() {
var container = document.getElementById("container");
container.innerHTML = "";
var target = document.createElement("div");
var overflowing_child = document.createElement("div");
target.style = "overflow:scroll; height: 100px; width: 100px; scrollbar-width: none";
overflowing_child.style = "height: 200px; width: 200px;";
target.appendChild(overflowing_child);
container.appendChild(target);
return target;
}
promise_test(async (t) => {
var target = setupTarget();
assert_equals(target.scrollTop, 0);
var promiseForScrollTop = waitForEvent("scroll", t, target);
target.scrollTop = 10;
await promiseForScrollTop;
assert_equals(target.scrollTop, 10);
assert_equals(target.scrollLeft, 0);
var promiseForScrollLeft = waitForEvent("scroll", t, target);
target.scrollLeft = 10;
await promiseForScrollLeft;
assert_equals(target.scrollLeft, 10);
}, "scrollTop and scrollLeft should fire scroll event.");
promise_test(async (t) => {
var target = setupTarget();
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
assert_equals(target.scrollTop, 0);
target.scrollTop = 0;
assert_equals(target.scrollLeft, 0);
target.scrollLeft = 0;
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollTop and scrollLeft being set with the same value.");
promise_test(async (t) => {
var target = setupTarget();
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
target.scrollTop = -100;
target.scrollLeft = -100;
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollTop and scrollLeft being set with invalid scroll offset.");
promise_test(async (t) => {
var target = setupTarget();
assert_equals(target.scrollTop, 0);
var promiseForScrollTop = waitForEvent("scroll", t, target);
target.scrollTop = 1000;
await promiseForScrollTop;
assert_equals(target.scrollTop, 100);
assert_equals(target.scrollLeft, 0);
var promiseForScrollLeft = waitForEvent("scroll", t, target);
target.scrollLeft = 1000;
await promiseForScrollLeft;
assert_equals(target.scrollLeft, 100);
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
target.scrollTop = 1000;
target.scrollLeft = 1000;
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollTop and scrollLeft when scrolling above maximum offset.");
</script>

View file

@ -0,0 +1,116 @@
<!doctype html>
<meta charset=utf-8>
<title>Scroll event should behave correctly for Element.ScrollX and Element.ScrollLeft</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="scroll_support.js"></script>
<link rel="author" title="Jo Steven Novaryo" href="mailto:jo.steven.novaryo@huawei.com">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-window-interface">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-events">
<div id=log></div>
<div id="container">
</div>
<script>
function promiseForFrameLoad(frame) {
return new Promise(async (resolve) => {
frame.addEventListener("load", () => resolve(frame), { once: true });
});
}
async function promiseForSetupTargetFrame() {
const target = document.createElement("iframe");
target.src = "../resources/large-dimension-document.sub.html";
target.width = "200";
target.height = "200";
var container = document.getElementById("container");
container.innerHTML = "";
container.appendChild(target);
return promiseForFrameLoad(target);
}
promise_test(async (t) => {
var frame = await promiseForSetupTargetFrame();
var target = frame.contentWindow;
assert_equals(target.scrollX, 0);
var promiseForScrollX = waitForEvent("scroll", t, target);
target.scrollTo({left: 10});
await promiseForScrollX;
assert_equals(target.scrollX, 10);
assert_equals(target.scrollY, 0);
var promiseForScrollY = waitForEvent("scroll", t, target);
target.scrollTo({top: 10});
await promiseForScrollY;
assert_equals(target.scrollY, 10);
}, "scrollX and scrollY should fire scroll event.");
promise_test(async (t) => {
var frame = await promiseForSetupTargetFrame();
var target = frame.contentWindow;
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
assert_equals(target.scrollX, 0);
target.scrollTo({left: 0});
assert_equals(target.scrollY, 0);
target.scrollTo({top: 0});
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollX and scrollY being set with the same value.");
promise_test(async (t) => {
var frame = await promiseForSetupTargetFrame();
var target = frame.contentWindow;
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
target.scrollTo({left: -100});
target.scrollTo({top: -100});
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollX and scrollY being set with invalid scroll Scroll.");
promise_test(async (t) => {
var frame = await promiseForSetupTargetFrame();
var target = frame.contentWindow;
var frameDocEl = frame.contentDocument.documentElement;
assert_equals(target.scrollX, 0);
var promiseForScrollX = waitForEvent("scroll", t, target);
target.scrollTo({left: frameDocEl.scrollWidth});
await promiseForScrollX;
assert_greater_than(target.scrollX, 0);
assert_equals(target.scrollY, 0);
var promiseForScrollY = waitForEvent("scroll", t, target);
target.scrollTo({
top: frameDocEl.scrollHeight,
left: target.scrollX
});
await promiseForScrollY;
assert_greater_than(target.scrollY, 0);
target.addEventListener("scroll", () => assert_unreached("Any scroll event should not be observed"));
target.scrollTo({
left: target.scrollX + 10,
top: target.scrollY + 10,
});
// Ensure all scroll event is flushed
await waitForNextFrame();
await waitForNextFrame();
}, "scrollX and scrollY when scrolling above maximum Scroll.");
</script>