mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
test: update DOM events web platform tests
PR-URL: https://github.com/nodejs/node/pull/54642 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Mattias Buelens <mattias@buelens.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
0de1cf004c
commit
b470e2fcb2
2
test/fixtures/wpt/README.md
vendored
2
test/fixtures/wpt/README.md
vendored
@ -14,7 +14,7 @@ Last update:
|
||||
- compression: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/compression
|
||||
- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console
|
||||
- dom/abort: https://github.com/web-platform-tests/wpt/tree/d1f1ecbd52/dom/abort
|
||||
- dom/events: https://github.com/web-platform-tests/wpt/tree/ab8999891c/dom/events
|
||||
- dom/events: https://github.com/web-platform-tests/wpt/tree/0a811c5161/dom/events
|
||||
- encoding: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/encoding
|
||||
- fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources
|
||||
- FileAPI: https://github.com/web-platform-tests/wpt/tree/cceaf3628d/FileAPI
|
||||
|
@ -87,6 +87,22 @@ async_test(function(t) { // as above with <a>
|
||||
child.dispatchEvent(new MouseEvent("click", {bubbles:true}))
|
||||
}, "pick the first with activation behavior <a href>")
|
||||
|
||||
async_test(function(t) {
|
||||
var input = document.createElement("input")
|
||||
input.type = "radio"
|
||||
dump.appendChild(input)
|
||||
input.onclick = t.step_func(function() {
|
||||
assert_false(input.checked, "input pre-click must not be triggered")
|
||||
})
|
||||
var child = input.appendChild(document.createElement("input"))
|
||||
child.type = "radio"
|
||||
child.onclick = t.step_func(function() {
|
||||
assert_true(child.checked, "child pre-click must be triggered")
|
||||
})
|
||||
child.dispatchEvent(new MouseEvent("click", {bubbles:true}))
|
||||
t.done()
|
||||
}, "pick the first with activation behavior <input type=radio>")
|
||||
|
||||
async_test(function(t) {
|
||||
var input = document.createElement("input")
|
||||
input.type = "checkbox"
|
||||
@ -173,6 +189,46 @@ async_test(function(t) {
|
||||
t.done()
|
||||
}, "disabled checkbox still has activation behavior, part 2")
|
||||
|
||||
async_test(function(t) {
|
||||
var state = "start"
|
||||
|
||||
var form = document.createElement("form")
|
||||
form.onsubmit = t.step_func(() => {
|
||||
if(state == "start" || state == "radio") {
|
||||
state = "failure"
|
||||
} else if(state == "form") {
|
||||
state = "done"
|
||||
}
|
||||
return false
|
||||
})
|
||||
dump.appendChild(form)
|
||||
var button = form.appendChild(document.createElement("button"))
|
||||
button.type = "submit"
|
||||
var radio = button.appendChild(document.createElement("input"))
|
||||
radio.type = "radio"
|
||||
radio.onclick = t.step_func(() => {
|
||||
if(state == "start") {
|
||||
assert_unreached()
|
||||
} else if(state == "radio") {
|
||||
assert_true(radio.checked)
|
||||
}
|
||||
})
|
||||
radio.disabled = true
|
||||
radio.click()
|
||||
assert_equals(state, "start")
|
||||
|
||||
state = "radio"
|
||||
radio.disabled = false
|
||||
radio.click()
|
||||
assert_equals(state, "radio")
|
||||
|
||||
state = "form"
|
||||
button.click()
|
||||
assert_equals(state, "done")
|
||||
|
||||
t.done()
|
||||
}, "disabled radio still has activation behavior")
|
||||
|
||||
async_test(function(t) {
|
||||
var input = document.createElement("input")
|
||||
input.type = "checkbox"
|
||||
|
@ -19,7 +19,7 @@
|
||||
<body>
|
||||
<script>
|
||||
// HTML elements that can be disabled
|
||||
const formElements = ["button", "fieldset", "input", "select", "textarea"];
|
||||
const formElements = ["button", "input", "select", "textarea"];
|
||||
|
||||
test(() => {
|
||||
for (const localName of formElements) {
|
||||
|
164
test/fixtures/wpt/dom/events/Event-dispatch-single-activation-behavior.html
vendored
Normal file
164
test/fixtures/wpt/dom/events/Event-dispatch-single-activation-behavior.html
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title> Only one activation behavior is executed during dispatch</title>
|
||||
<link rel="author" title="Vincent Hilla" href="mailto:vhilla@mozilla.com">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#eventtarget-activation-behavior">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-event-dispatch">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
|
||||
<div id=test_container></div>
|
||||
|
||||
<!--
|
||||
Three classes:
|
||||
click
|
||||
Element to be clicked to cause activation behavior
|
||||
activates
|
||||
Element that registers the activation behavior
|
||||
container
|
||||
Element in which other elements with activation behavior are placed.
|
||||
We test that those won't be activated too.
|
||||
-->
|
||||
<template>
|
||||
<!--input, change event bubble, so have to check if checked is true-->
|
||||
<input class="click activates container" type="checkbox" oninput="this.checked ? activated(this) : null">
|
||||
<input class="click activates container" type="radio" oninput="this.checked ? activated(this) : null">
|
||||
<form onsubmit="activated(this); return false" class="activates">
|
||||
<input class="click container" type="submit">
|
||||
</form>
|
||||
<form onsubmit="activated(this); return false" class="activates">
|
||||
<input class="click container" type="image">
|
||||
</form>
|
||||
<form onreset="activated(this)" class="activates">
|
||||
<input class="click container" type="reset">
|
||||
</form>
|
||||
<form onsubmit="activated(this); return false" class="activates">
|
||||
<button class="click container" type="submit"></button>
|
||||
</form>
|
||||
<form onreset="activated(this)" class="activates">
|
||||
<button class="click container" type="reset"></button>
|
||||
</form>
|
||||
<a href="#link" class="click container activates"></a>
|
||||
<area href="#link" class="click container activates">
|
||||
<details ontoggle="activated(this)" class="activates">
|
||||
<summary class="click container"></summary>
|
||||
</details>
|
||||
<label>
|
||||
<input type=checkbox onclick="this.checked ? activated(this) : null" class="activates">
|
||||
<span class="click container">label</span>
|
||||
</label>
|
||||
<!--activation behavior of label for event targeted at interactive content descendant is to do nothing-->
|
||||
<label class="container">
|
||||
<button class="click" type="button"></button>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let activations = [];
|
||||
function activated(e) {
|
||||
activations.push(e);
|
||||
}
|
||||
|
||||
function getActivations(testidx) {
|
||||
return activations.filter(a =>
|
||||
(a.endsWith && a.endsWith("test"+testidx+"_link"))
|
||||
|| (a.classList && a.classList.contains("test"+testidx))
|
||||
);
|
||||
}
|
||||
|
||||
// for a and area elements
|
||||
window.onhashchange = function(e) {
|
||||
if (e.newURL.endsWith("link")) {
|
||||
activated(e.newURL);
|
||||
}
|
||||
window.location.hash = "";
|
||||
};
|
||||
|
||||
function getElementsByClassNameInclusive(e, clsname) {
|
||||
let ls = Array.from(e.getElementsByClassName(clsname));
|
||||
if (e.classList.contains(clsname)) ls.push(e);
|
||||
return ls;
|
||||
}
|
||||
|
||||
function getClickTarget(e) {
|
||||
return getElementsByClassNameInclusive(e, "click")[0];
|
||||
}
|
||||
|
||||
function getContainer(e) {
|
||||
return getElementsByClassNameInclusive(e, "container")[0];
|
||||
}
|
||||
|
||||
function getExpectedActivations(e) {
|
||||
let ls = getElementsByClassNameInclusive(e, "activates");
|
||||
|
||||
// special case, for a and area the window registers the activation
|
||||
// have to use string, as testrunner cannot stringify the window object
|
||||
ls = ls.map(e => e.tagName === "A" || e.tagName === "AREA" ? e.href : e);
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
function toString(e) {
|
||||
const children = Array.from(e.children);
|
||||
const childstr = (children.map(toString)).join("");
|
||||
const tag = e.tagName;
|
||||
const typestr = e.type ? " type="+e.type : "";
|
||||
return `<${tag}${typestr}>${childstr}</${tag}>`;
|
||||
}
|
||||
|
||||
// generate O(n^2) test combinations
|
||||
const template = document.querySelector("template");
|
||||
const elements = Array.from(template.content.children);
|
||||
const tests = []
|
||||
for (const target of elements) {
|
||||
for (const parent of elements) {
|
||||
if (target === parent) continue;
|
||||
tests.push([target.cloneNode(true), parent.cloneNode(true)])
|
||||
}
|
||||
}
|
||||
|
||||
const test_container = document.getElementById("test_container");
|
||||
|
||||
/**
|
||||
* Test that if two elements in an event target chain have activation behavior,
|
||||
* only one of them will be activated.
|
||||
*
|
||||
* Each child of <template> represents one case of activation behavior.
|
||||
* The behavior should be triggered by clicking the element of class click
|
||||
* and will manifest as a call to activated().
|
||||
*
|
||||
* For each [target, parent] in tests, we make target a descendant of parent
|
||||
* and test that only target gets activated when dispatching a click.
|
||||
*/
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
let [target, parent] = tests[i];
|
||||
async_test(function(t) {
|
||||
let test = document.createElement("div");
|
||||
test_container.appendChild(test);
|
||||
test.appendChild(parent);
|
||||
getContainer(parent).appendChild(target);
|
||||
|
||||
// for later filtering out the activations belonging to this test
|
||||
for (let e of test.getElementsByClassName("activates")) {
|
||||
e.classList.add("test"+i);
|
||||
}
|
||||
for (let e of test.querySelectorAll("a, area")) {
|
||||
e.href = "#test"+i+"_link";
|
||||
}
|
||||
|
||||
getClickTarget(target).click();
|
||||
|
||||
// Need to spin event loop twice, as some clicks might dispatch another task
|
||||
t.step_timeout(() => {
|
||||
t.step_timeout(t.step_func_done(() => {
|
||||
assert_array_equals(getActivations(i), getExpectedActivations(target));
|
||||
}), 0);
|
||||
}, 0);
|
||||
|
||||
t.add_cleanup(function() {
|
||||
test_container.removeChild(test);
|
||||
});
|
||||
}, `When clicking child ${toString(target)} of parent ${toString(parent)}, only child should be activated.`);
|
||||
}
|
||||
</script>
|
69
test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html
vendored
Normal file
69
test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
function createIframe(t, srcdoc = '') {
|
||||
let iframe = document.createElement('iframe');
|
||||
iframe.srcdoc = srcdoc;
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
return new Promise((resolve, reject) => {
|
||||
iframe.addEventListener('load', () => resolve(iframe.contentWindow));
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise which will resolve with the next error event fired at any
|
||||
// of `windows`, after the invocation of this function. Once one does, this
|
||||
// function removes its listeners and produces that error event so that it can
|
||||
// be examined (most notably for which global proxy it was targeted at).
|
||||
async function nextErrorEvent(windows) {
|
||||
let listener;
|
||||
let p = new Promise((resolve, reject) => {
|
||||
listener = (event) => { resolve(event); event.preventDefault(); };
|
||||
});
|
||||
for (let w of windows) {
|
||||
w.addEventListener('error', listener);
|
||||
}
|
||||
try {
|
||||
return await p;
|
||||
} finally {
|
||||
for (let w of windows) {
|
||||
w.removeEventListener('error', listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
let w = await createIframe(t, `<script>function listener() { throw new Error(); }<`+`/script>`);
|
||||
let w2 = await createIframe(t);
|
||||
|
||||
let target = new w2.EventTarget();
|
||||
target.addEventListener('party', w.listener);
|
||||
let nextErrorPromise = nextErrorEvent([self, w, w2]);
|
||||
target.dispatchEvent(new Event('party'));
|
||||
let errorEvent = await nextErrorPromise;
|
||||
if (errorEvent.error) {
|
||||
assert_true(errorEvent.error instanceof w.Error, 'error should be an instance created inside the listener function');
|
||||
}
|
||||
assert_equals(errorEvent.target, w, `error event should target listener's global but instead targets ${event.currentTarget === w2 ? 'target\'s global' : 'test harness global'}`);
|
||||
}, 'exception thrown in event listener function should result in error event on listener\'s global');
|
||||
|
||||
promise_test(async t => {
|
||||
let w = await createIframe(t, `<script>listener = {};<`+`/script>`);
|
||||
let w2 = await createIframe(t, `<script>handleEvent = () => { throw new Error; };<`+`/script>`);
|
||||
let w3 = await createIframe(t);
|
||||
w.listener.handleEvent = w2.handleEvent;
|
||||
|
||||
let target = new w3.EventTarget();
|
||||
target.addEventListener('party', w.listener);
|
||||
let nextErrorPromise = nextErrorEvent([self, w, w2, w3]);
|
||||
target.dispatchEvent(new Event('party'));
|
||||
let errorEvent = await nextErrorPromise;
|
||||
if (errorEvent.error) {
|
||||
assert_true(errorEvent.error instanceof w2.Error, 'error should be an instance created inside the listener function');
|
||||
}
|
||||
assert_equals(errorEvent.target, w, `error event should target listener's global but instead targets ${event.currentTarget === w2 ? 'target\'s global' : event.currentTarget === w3 ? 'function\'s global' : 'test harness global'}`);
|
||||
}, 'exception thrown in event listener interface object should result in error event on listener\'s global');
|
||||
</script>
|
||||
</body>
|
@ -23,6 +23,23 @@ test(() => {
|
||||
assert_equals(callCount, 2);
|
||||
}, "A constructed EventTarget can be used as expected");
|
||||
|
||||
test(() => {
|
||||
const target = new EventTarget();
|
||||
const event = new Event("foo");
|
||||
|
||||
function listener(e) {
|
||||
assert_equals(e, event);
|
||||
assert_equals(e.target, target);
|
||||
assert_equals(e.currentTarget, target);
|
||||
assert_array_equals(e.composedPath(), [target]);
|
||||
}
|
||||
target.addEventListener("foo", listener, { once: true });
|
||||
target.dispatchEvent(event);
|
||||
assert_equals(event.target, target);
|
||||
assert_equals(event.currentTarget, null);
|
||||
assert_array_equals(event.composedPath(), []);
|
||||
}, "A constructed EventTarget implements dispatch correctly");
|
||||
|
||||
test(() => {
|
||||
class NicerEventTarget extends EventTarget {
|
||||
on(...args) {
|
||||
|
10
test/fixtures/wpt/dom/events/event-global.html
vendored
10
test/fixtures/wpt/dom/events/event-global.html
vendored
@ -114,4 +114,14 @@ async_test(t => {
|
||||
|
||||
target.dispatchEvent(new Event("click"));
|
||||
}, "window.event is set to the current event, which is the event passed to dispatch");
|
||||
|
||||
async_test(t => {
|
||||
let target = new XMLHttpRequest();
|
||||
|
||||
target.onload = t.step_func_done(e => {
|
||||
assert_equals(e, window.event);
|
||||
});
|
||||
|
||||
target.dispatchEvent(new Event("load"));
|
||||
}, "window.event is set to the current event, which is the event passed to dispatch (2)");
|
||||
</script>
|
||||
|
29
test/fixtures/wpt/dom/events/pointer-event-document-move.html
vendored
Normal file
29
test/fixtures/wpt/dom/events/pointer-event-document-move.html
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://crbug.com/341104769">
|
||||
<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>
|
||||
|
||||
<template>
|
||||
<p>TEST</p>
|
||||
</template>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
const clone = document.querySelector("template").content.cloneNode(true);
|
||||
const p = clone.querySelector("p");
|
||||
|
||||
let gotEvent = false;
|
||||
p.addEventListener("pointerup", () => {
|
||||
gotEvent = true;
|
||||
});
|
||||
|
||||
document.body.append(clone);
|
||||
|
||||
promise_test(async () => {
|
||||
await test_driver.click(document.querySelector("p"));
|
||||
assert_true(gotEvent);
|
||||
}, "Moving a node to new document should move the registered event listeners together");
|
||||
</script>
|
53
test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html
vendored
Normal file
53
test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<title>preventDefault during activation behavior</title>
|
||||
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1197032">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/C#the-button-element">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#dispatching-events">
|
||||
<link rel="author" title="L. David Baron" href="https://dbaron.org/">
|
||||
<link rel="author" title="Google" href="http://www.google.com/">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
|
||||
<form id="f">
|
||||
<input type="submit" id="b">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
promise_test(async () => {
|
||||
let form = document.getElementById("f");
|
||||
let button = document.getElementById("b");
|
||||
|
||||
let cached_event;
|
||||
let submit_fired = false;
|
||||
|
||||
let click_promise = new Promise((resolve, reject) => {
|
||||
button.addEventListener("click", event => {
|
||||
assert_false(submit_fired);
|
||||
cached_event = event;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
form.addEventListener("submit", event => {
|
||||
assert_false(submit_fired);
|
||||
assert_true(!!cached_event, "click should have fired");
|
||||
|
||||
// Call preventDefault on the click event during its activation
|
||||
// behavior, to test the bug that we're trying to test.
|
||||
cached_event.preventDefault();
|
||||
|
||||
// prevent the form submission from navigating the page
|
||||
event.preventDefault();
|
||||
|
||||
submit_fired = true;
|
||||
});
|
||||
|
||||
assert_false(submit_fired);
|
||||
button.click();
|
||||
await click_promise;
|
||||
assert_true(submit_fired);
|
||||
}, "behavior of preventDefault during activation behavior");
|
||||
|
||||
</script>
|
95
test/fixtures/wpt/dom/events/remove-all-listeners.html
vendored
Normal file
95
test/fixtures/wpt/dom/events/remove-all-listeners.html
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<!doctype html>
|
||||
<title>Various edge cases where listeners are removed during iteration</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test(function() {
|
||||
var type = "foo";
|
||||
var target = document.createElement("div");
|
||||
|
||||
var listener1CallCount = 0;
|
||||
var listener2CallCount = 0;
|
||||
var listener3CallCount = 0;
|
||||
function listener1() {
|
||||
listener1CallCount++;
|
||||
target.removeEventListener(type, listener1);
|
||||
target.removeEventListener(type, listener2);
|
||||
target.addEventListener(type, listener3);
|
||||
}
|
||||
function listener2() {
|
||||
listener2CallCount++;
|
||||
}
|
||||
function listener3() {
|
||||
listener3CallCount++;
|
||||
}
|
||||
|
||||
target.addEventListener(type, listener1);
|
||||
target.addEventListener(type, listener2);
|
||||
|
||||
// Dispatch the event. Only listener1 should be called because
|
||||
// it removes listener2. And listener3 is added when we've already
|
||||
// started iterating, so it shouldn't be called either.
|
||||
target.dispatchEvent(new Event(type));
|
||||
assert_equals(listener1CallCount, 1);
|
||||
assert_equals(listener2CallCount, 0);
|
||||
assert_equals(listener3CallCount, 0);
|
||||
|
||||
// Now that only listener3 is set, dispatch another event. Only
|
||||
// listener3 should be called.
|
||||
target.dispatchEvent(new Event(type));
|
||||
assert_equals(listener1CallCount, 1);
|
||||
assert_equals(listener2CallCount, 0);
|
||||
assert_equals(listener3CallCount, 1);
|
||||
}, "Removing all listeners and then adding a new one should work.");
|
||||
|
||||
test(function() {
|
||||
var type = "foo";
|
||||
var target = document.createElement("div");
|
||||
|
||||
var listener1CallCount = 0;
|
||||
var listener2CallCount = 0;
|
||||
var listener3CallCount = 0;
|
||||
function listener1() {
|
||||
listener1CallCount++;
|
||||
// Recursively dispatch another event from this listener.
|
||||
// This will only call listener2 because listener1 is a "once" listener.
|
||||
target.dispatchEvent(new Event(type));
|
||||
assert_equals(listener1CallCount, 1);
|
||||
assert_equals(listener2CallCount, 1);
|
||||
assert_equals(listener3CallCount, 0);
|
||||
|
||||
// Now all listeners are removed - the two "once" listeners have already both
|
||||
// been called once. Add another listener.
|
||||
target.addEventListener(type, listener3);
|
||||
}
|
||||
function listener2() {
|
||||
listener2CallCount++;
|
||||
}
|
||||
function listener3() {
|
||||
listener3CallCount++;
|
||||
}
|
||||
|
||||
// Add two "once" listeners.
|
||||
target.addEventListener(type, listener1, { once: true });
|
||||
target.addEventListener(type, listener2, { once: true });
|
||||
|
||||
// Dispatch the event.
|
||||
target.dispatchEvent(new Event(type));
|
||||
|
||||
// The listener call counts should still match what they were
|
||||
// at the end of listener1.
|
||||
assert_equals(listener1CallCount, 1);
|
||||
assert_equals(listener2CallCount, 1);
|
||||
assert_equals(listener3CallCount, 0);
|
||||
|
||||
// Now that only listener3 is set, dispatch another event. Only
|
||||
// listener3 should be called.
|
||||
target.dispatchEvent(new Event(type));
|
||||
assert_equals(listener1CallCount, 1);
|
||||
assert_equals(listener2CallCount, 1);
|
||||
assert_equals(listener3CallCount, 1);
|
||||
}, "Nested usage of once listeners should work.");
|
||||
|
||||
</script>
|
4
test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml
vendored
Normal file
4
test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
features:
|
||||
- name: scrollend
|
||||
files:
|
||||
- scrollend-*
|
@ -6,80 +6,152 @@
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="scroll_support.js"></script>
|
||||
<style>
|
||||
#targetDiv {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: red;
|
||||
}
|
||||
html, body {
|
||||
/* Prevent any built-in browser overscroll features from consuming the scroll
|
||||
* deltas */
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
#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>
|
||||
<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;
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
var overscrolled_x_deltas = [];
|
||||
var overscrolled_y_deltas = [];
|
||||
var scrollend_received = false;
|
||||
|
||||
function onOverscroll(event) {
|
||||
function onOverscroll(event) {
|
||||
overscrolled_x_deltas.push(event.deltaX);
|
||||
overscrolled_y_deltas.push(event.deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
function onScrollend(event) {
|
||||
scrollend_received = true;
|
||||
}
|
||||
async function resetScrollers(test) {
|
||||
await waitForScrollReset(test, target_div);
|
||||
await waitForScrollReset(test, document.scrollingElement);
|
||||
}
|
||||
|
||||
document.addEventListener("overscroll", onOverscroll);
|
||||
document.addEventListener("scrollend", onScrollend);
|
||||
|
||||
function runTest() {
|
||||
promise_test (async (t) => {
|
||||
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 scrollend_received; },
|
||||
'Document did not receive scrollend event.');
|
||||
|
||||
// Even though we request 300 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 100 pixels which is a fairly conservative
|
||||
// value.
|
||||
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), 0, "The deltaY attribute must be <= 0 when there is overscrolling in up direction.");
|
||||
assert_less_than_equal(Math.min(...overscrolled_y_deltas),-100, "The deltaY attribute must be the number of pixels overscrolled.");
|
||||
|
||||
await waitForCompositorCommit();
|
||||
function resetOverScrollDeltas() {
|
||||
overscrolled_x_deltas = [];
|
||||
overscrolled_y_deltas = [];
|
||||
scrollend_received = false;
|
||||
}
|
||||
|
||||
// Scroll left on target div and wait for the doc to get overscroll event.
|
||||
await touchScrollInTarget(300, target_div, 'left');
|
||||
await waitFor(() => { return scrollend_received; },
|
||||
'Document did not receive scrollend event.');
|
||||
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_y_deltas.length, 0, "There should be at least one overscroll events when overscrolling.");
|
||||
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), 0, "The deltaX attribute must be <= 0 when there is overscrolling in left direction.");
|
||||
assert_less_than_equal(Math.min(...overscrolled_x_deltas),-50, "The deltaX attribute must be the number of pixels overscrolled.");
|
||||
|
||||
}, 'Tests that the document gets overscroll event with right deltaX/Y attributes.');
|
||||
}
|
||||
assert_less_than_equal(Math.max(...overscrolled_x_deltas), 300, "The deltaX attribute must be <= number of pixels overscrolled (300)");
|
||||
}, "testing, horizontal");
|
||||
}
|
||||
</script>
|
||||
|
@ -10,11 +10,14 @@
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
position: absolute;
|
||||
left: 150px;
|
||||
top: 150px;
|
||||
}
|
||||
|
||||
#innerDiv {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -45,7 +48,7 @@ function runTest() {
|
||||
await waitForCompositorCommit();
|
||||
|
||||
// Do a horizontal scroll and wait for overscroll event.
|
||||
await touchScrollInTarget(300, scrolling_div , 'right');
|
||||
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,
|
||||
@ -55,7 +58,7 @@ function runTest() {
|
||||
overscrolled_y_delta = 0;
|
||||
|
||||
// Do a vertical scroll and wait for overscroll event.
|
||||
await touchScrollInTarget(300, scrolling_div, 'down');
|
||||
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,
|
||||
|
@ -1,15 +1,83 @@
|
||||
async function waitForScrollendEvent(test, target, timeoutMs = 500) {
|
||||
async function waitForEvent(eventName, test, target, timeoutMs = 500) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutCallback = test.step_timeout(() => {
|
||||
reject(`No Scrollend event received for target ${target}`);
|
||||
reject(`No ${eventName} event received for target ${target}`);
|
||||
}, timeoutMs);
|
||||
target.addEventListener('scrollend', (evt) => {
|
||||
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;
|
||||
|
||||
@ -45,6 +113,29 @@ function waitForCompositorCommit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -70,6 +161,10 @@ function waitForAnimationEnd(getValue) {
|
||||
}
|
||||
|
||||
// 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")
|
||||
@ -88,7 +183,7 @@ function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_
|
||||
y += step_y;
|
||||
test_driver_actions.pointerMove(x, y, {origin: target});
|
||||
}
|
||||
test_driver_actions.pause(pause_time_in_ms);
|
||||
test_driver_actions.pause(pause_time_in_ms); // To prevent inertial scroll
|
||||
}
|
||||
|
||||
return test_driver_actions.pointerUp().send();
|
||||
@ -125,7 +220,7 @@ function touchScrollInTarget(pixels_to_scroll, target, direction, pause_time_in_
|
||||
|
||||
// Trigger fling by doing pointerUp right after pointerMoves.
|
||||
function touchFlingInTarget(pixels_to_scroll, target, direction) {
|
||||
touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */);
|
||||
return touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */);
|
||||
}
|
||||
|
||||
function mouseActionsInTarget(target, origin, delta, pause_time_in_ms = 100) {
|
||||
@ -161,3 +256,23 @@ function conditionHolds(condition, error_message = 'Condition is not true anymor
|
||||
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();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
#innerDiv {
|
||||
width: 500px;
|
||||
width: 4000px;
|
||||
height: 4000px;
|
||||
}
|
||||
</style>
|
||||
@ -28,36 +28,64 @@
|
||||
|
||||
<script>
|
||||
const target_div = document.getElementById('targetDiv');
|
||||
let scrollend_arrived = false;
|
||||
let scrollend_event_count = 0;
|
||||
|
||||
function onScrollEnd(event) {
|
||||
assert_false(event.cancelable);
|
||||
assert_false(event.bubbles);
|
||||
scrollend_arrived = true;
|
||||
scrollend_event_count += 1;
|
||||
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) => {
|
||||
// Make sure that no scrollend event is sent to document.
|
||||
document.addEventListener("scrollend",
|
||||
t.unreached_func("document got unexpected scrollend event."));
|
||||
await waitForCompositorCommit();
|
||||
|
||||
// Scroll down & up & down on target div and wait for the target_div to get scrollend event.
|
||||
target_div.addEventListener("scrollend", onScrollEnd);
|
||||
const move_path = [
|
||||
{ x: 0, y: -300}, // down
|
||||
{ x: 0, y: -100}, // up
|
||||
{ x: 0, y: -400}, // down
|
||||
{ x: 0, y: -200}, // up
|
||||
{ x: 0, y: -80 }, // Scroll down
|
||||
{ x: 0, y: -40 }, // Scroll up
|
||||
{ x: 0, y: -80 }, // Scroll down
|
||||
];
|
||||
await touchScrollInTargetSequentiallyWithPause(target_div, move_path, 150);
|
||||
|
||||
await waitFor(() => {return scrollend_arrived;},
|
||||
'target_div did not receive scrollend event after sequence of scrolls on target.');
|
||||
assert_equals(scrollend_event_count, 1);
|
||||
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>
|
||||
|
@ -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>
|
@ -1,11 +1,19 @@
|
||||
<!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="/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 {
|
||||
@ -31,8 +39,8 @@ html {
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
var element_scrollend_arrived = false;
|
||||
var document_scrollend_arrived = false;
|
||||
let element_scrollend_arrived = false;
|
||||
let document_scrollend_arrived = false;
|
||||
|
||||
function onElementScrollEnd(event) {
|
||||
assert_false(event.cancelable);
|
||||
@ -47,89 +55,95 @@ function onDocumentScrollEnd(event) {
|
||||
document_scrollend_arrived = true;
|
||||
}
|
||||
|
||||
function callScrollFunction([scrollTarget, scrollFunction, args]) {
|
||||
scrollTarget[scrollFunction](args);
|
||||
}
|
||||
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() {
|
||||
let root_element = document.scrollingElement;
|
||||
let target_div = document.getElementById("targetDiv");
|
||||
|
||||
promise_test (async (t) => {
|
||||
async function testScrollFn(testInfo, t) {
|
||||
await waitForCompositorCommit();
|
||||
target_div.addEventListener("scrollend", onElementScrollEnd);
|
||||
|
||||
targetDiv.addEventListener("scrollend", onElementScrollEnd);
|
||||
document.addEventListener("scrollend", onDocumentScrollEnd);
|
||||
|
||||
let test_cases = [
|
||||
[target_div, 200, 200, [target_div, "scrollTo", { top: 200, left: 200, behavior: "auto" }]],
|
||||
[target_div, 0, 0, [target_div, "scrollTo", { top: 0, left: 0, behavior: "smooth" }]],
|
||||
[root_element, 200, 200, [root_element, "scrollTo", { top: 200, left: 200, behavior: "auto" }]],
|
||||
[root_element, 0, 0, [root_element, "scrollTo", { top: 0, left: 0, behavior: "smooth" }]],
|
||||
[target_div, 200, 200, [target_div, "scrollBy", { top: 200, left: 200, behavior: "auto" }]],
|
||||
[target_div, 0, 0, [target_div, "scrollBy", { top: -200, left: -200, behavior: "smooth" }]],
|
||||
[root_element, 200, 200, [root_element, "scrollBy", { top: 200, left: 200, behavior: "auto" }]],
|
||||
[root_element, 0, 0, [root_element, "scrollBy", { top: -200, left: -200, behavior: "smooth" }]]
|
||||
];
|
||||
testInfo.target[testInfo.fn]({ top: 200, left: 200,
|
||||
behavior: testInfo.behavior });
|
||||
|
||||
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 + "." + scroll_datas[1] + " did not receive scrollend event.");
|
||||
if (target == root_element)
|
||||
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
|
||||
} else {
|
||||
assert_false(document_scrollend_arrived);
|
||||
assert_equals(target.scrollLeft, expected_x, target.tagName + "." + scroll_datas[1] + " scrollLeft");
|
||||
assert_equals(target.scrollTop, expected_y, target.tagName + "." + scroll_datas[1] + " scrollTop");
|
||||
|
||||
element_scrollend_arrived = false;
|
||||
document_scrollend_arrived = false;
|
||||
}
|
||||
}, "Tests scrollend event for calling scroll functions.");
|
||||
|
||||
promise_test(async (t) => {
|
||||
await waitForCompositorCommit();
|
||||
|
||||
let test_cases = [
|
||||
[target_div, "scrollTop"],
|
||||
[target_div, "scrollLeft"],
|
||||
[root_element, "scrollTop"],
|
||||
[root_element, "scrollLeft"]
|
||||
];
|
||||
for (i = 0; i < test_cases.length; i++) {
|
||||
let t = test_cases[i];
|
||||
let target = t[0];
|
||||
let attribute = t[1];
|
||||
let position = 200;
|
||||
|
||||
target.style.scrollBehavior = "smooth";
|
||||
target[attribute] = position;
|
||||
await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + "." + attribute + " did not receive scrollend event.");
|
||||
if (target == root_element)
|
||||
assert_false(element_scrollend_arrived);
|
||||
else
|
||||
assert_false(document_scrollend_arrived);
|
||||
assert_equals(target[attribute], position, target.tagName + "." + attribute + " ");
|
||||
element_scrollend_arrived = false;
|
||||
document_scrollend_arrived = false;
|
||||
|
||||
await waitForCompositorCommit();
|
||||
target.style.scrollBehavior = "auto";
|
||||
target[attribute] = 0;
|
||||
await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + "." + attribute + " did not receive scrollend event.");
|
||||
if (target == root_element)
|
||||
assert_false(element_scrollend_arrived);
|
||||
else
|
||||
assert_false(document_scrollend_arrived);
|
||||
assert_equals(target[attribute], 0, target.tagName + "." + attribute + " ");
|
||||
element_scrollend_arrived = false;
|
||||
document_scrollend_arrived = false;
|
||||
assert_equals(testInfo.target.scrollLeft, 200,
|
||||
testInfo.target.tagName + "." + testInfo.fn + " scrollLeft");
|
||||
assert_equals(testInfo.target.scrollTop, 200,
|
||||
testInfo.target.tagName + "." + testInfo.fn + " scrollTop");
|
||||
}
|
||||
}, "Tests scrollend event for changing scroll attributes.");
|
||||
|
||||
scroll_fn_variants.forEach((testInfo) => {
|
||||
subsetTestByKey(testInfo.key, promise_test,
|
||||
async (t) => testScrollFn(testInfo, t), testInfo.title);
|
||||
});
|
||||
|
||||
}
|
||||
</script>
|
||||
|
147
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scroll-attr-change.html
vendored
Normal file
147
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scroll-attr-change.html
vendored
Normal 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>
|
@ -108,10 +108,12 @@ function runTest() {
|
||||
document.body.appendChild(out_div);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
element_scrollend_arrived = false;
|
||||
document_scrollend_arrived = false;
|
||||
inner_div.scrollIntoView({ inline: "end", block: "end", behavior: "auto" });
|
||||
await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, "Nested scrollIntoView did not receive scrollend event.");
|
||||
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");
|
||||
|
@ -7,64 +7,100 @@
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="scroll_support.js"></script>
|
||||
<style>
|
||||
#targetDiv {
|
||||
#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 {
|
||||
#innerDiv {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body style="margin:0" onload=runTest()>
|
||||
<div id="targetDiv">
|
||||
<div id="innerDiv">
|
||||
<div id="targetDiv">
|
||||
<div id="innerDiv"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hspacer"></div>
|
||||
<div id="vspacer"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
var horizontal_scrollend_arrived = false;
|
||||
var vertical_scrollend_arrived = false;
|
||||
function onHorizontalScrollEnd(event) {
|
||||
assert_false(event.cancelable);
|
||||
// scrollend events are bubbled when the target node is document.
|
||||
assert_true(event.bubbles);
|
||||
horizontal_scrollend_arrived = true;
|
||||
}
|
||||
function onVerticalScrollEnd(event) {
|
||||
assert_false(event.cancelable);
|
||||
// scrollend events are bubbled when the target node is document.
|
||||
assert_true(event.bubbles);
|
||||
vertical_scrollend_arrived = true;
|
||||
}
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
async function resetScrollers(test) {
|
||||
await waitForScrollReset(test, target_div);
|
||||
await waitForScrollReset(test, document.scrollingElement);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
promise_test (async (t) => {
|
||||
// Make sure that no scrollend event is sent to target_div.
|
||||
target_div.addEventListener("scrollend",
|
||||
t.unreached_func("target_div got unexpected scrollend event."));
|
||||
function fail() {
|
||||
assert_true(false);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
promise_test(async (t) => {
|
||||
await resetScrollers(t);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
// Scroll left on target div and wait for the doc to get scrollend event.
|
||||
document.addEventListener("scrollend", onHorizontalScrollEnd);
|
||||
await touchScrollInTarget(300, target_div, 'left');
|
||||
await waitFor(() => { return horizontal_scrollend_arrived; },
|
||||
'Document did not receive scrollend event after scroll left on target.');
|
||||
assert_equals(target_div.scrollLeft, 0);
|
||||
document.removeEventListener("scrollend", onHorizontalScrollEnd);
|
||||
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.
|
||||
document.addEventListener("scrollend", onVerticalScrollEnd);
|
||||
await touchScrollInTarget(300, target_div, 'up');
|
||||
await waitFor(() => { return vertical_scrollend_arrived; },
|
||||
'Document did not receive scrollend event after scroll up on target.');
|
||||
assert_equals(target_div.scrollTop, 0);
|
||||
}, 'Tests that the document gets scrollend event when no element scrolls by ' +
|
||||
'touch.');
|
||||
}
|
||||
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>
|
||||
|
@ -7,96 +7,167 @@
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="scroll_support.js"></script>
|
||||
<style>
|
||||
#overscrollXDiv {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
#overscrollXDiv {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
#overscrollYDiv {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
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;
|
||||
}
|
||||
#targetDiv {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
border: solid 1px black;
|
||||
}
|
||||
|
||||
#targetXDiv {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
overflow: scroll;
|
||||
}
|
||||
.content {
|
||||
width:800px;
|
||||
height:800px;
|
||||
}
|
||||
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 id="overscrollYDiv">
|
||||
<div class=content>
|
||||
<div id="targetDiv">
|
||||
<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>
|
||||
<div id="spacer"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
var horizontal_scrollend_arrived = false;
|
||||
var vertical_scrollend_arrived = false;
|
||||
function onHorizontalScrollEnd(event) {
|
||||
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) {
|
||||
}
|
||||
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').
|
||||
}
|
||||
// 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) => {
|
||||
// Make sure that no scrollend event is sent to document or target_div.
|
||||
document.addEventListener("scrollend",
|
||||
t.unreached_func("Document got unexpected scrollend event."));
|
||||
target_div.addEventListener("scrollend",
|
||||
t.unreached_func("target_div got unexpected scrollend event."));
|
||||
function runTest() {
|
||||
promise_test(async (t) => {
|
||||
await resetScrollers(t);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
// Scroll left on target div and wait for the element with overscroll-x to
|
||||
// get scrollend event.
|
||||
await touchScrollInTarget(300, target_div, 'left');
|
||||
await waitFor(() => { return horizontal_scrollend_arrived; },
|
||||
'Expected element did not receive scrollend event after scroll left ' +
|
||||
'on target.');
|
||||
assert_equals(target_div.scrollLeft, 0);
|
||||
// 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) => {
|
||||
target_div.addEventListener("touchend", resolve);
|
||||
targetXDiv.addEventListener("touchend", resolve);
|
||||
});
|
||||
await touchScrollInTarget(300, target_div, 'up');
|
||||
await touchScrollInTarget(100, targetXDiv, 'right');
|
||||
// The scrollend event should never be fired before the gesture has
|
||||
// completed.
|
||||
await touchEndPromise;
|
||||
|
||||
// The scrollend event should never be fired before the gesture has completed.
|
||||
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(vertical_scrollend_arrived, false);
|
||||
assert_equals(target_div.scrollTop, 0);
|
||||
}, 'Tests that the last element in the cut scroll chain gets scrollend ' +
|
||||
'event when no element scrolls by touch.');
|
||||
}
|
||||
// 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>
|
||||
|
@ -1,68 +0,0 @@
|
||||
<!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>
|
||||
#scrollableDiv {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#innerDiv {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body style="margin:0" onload=runTest()>
|
||||
<div id="scrollableDiv">
|
||||
<div id="innerDiv">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var scrolling_div = document.getElementById('scrollableDiv');
|
||||
var horizontal_scrollend_arrived = false;
|
||||
var vertical_scrollend_arrived = false;
|
||||
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;
|
||||
}
|
||||
scrolling_div.addEventListener("scrollend", onHorizontalScrollEnd);
|
||||
scrolling_div.addEventListener("scrollend", onVerticalScrollEnd);
|
||||
|
||||
function runTest() {
|
||||
promise_test (async (t) => {
|
||||
// Make sure that no scrollend event is sent to document.
|
||||
document.addEventListener("scrollend",
|
||||
t.unreached_func("Document got unexpected scrollend event."));
|
||||
await waitForCompositorCommit();
|
||||
|
||||
// Do a horizontal scroll and wait for scrollend event.
|
||||
await touchScrollInTarget(300, scrolling_div, 'right');
|
||||
await waitFor(() => { return horizontal_scrollend_arrived; },
|
||||
'Scroller did not receive scrollend event after horizontal scroll.');
|
||||
assert_equals(scrolling_div.scrollWidth - scrolling_div.scrollLeft,
|
||||
scrolling_div.clientWidth);
|
||||
|
||||
// Do a vertical scroll and wait for scrollend event.
|
||||
await touchScrollInTarget(300, scrolling_div, 'down');
|
||||
await waitFor(() => { return vertical_scrollend_arrived; },
|
||||
'Scroller did not receive scrollend event after vertical scroll.');
|
||||
assert_equals(scrolling_div.scrollHeight - scrolling_div.scrollTop,
|
||||
scrolling_div.clientHeight);
|
||||
}, 'Tests that the scrolled element gets scrollend event at the end of touch scrolling.');
|
||||
}
|
||||
</script>
|
@ -7,49 +7,63 @@
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="scroll_support.js"></script>
|
||||
<style>
|
||||
#targetDiv {
|
||||
#spacer {
|
||||
height: 100vh;
|
||||
width: 100px;
|
||||
}
|
||||
#targetDiv {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#innerDiv {
|
||||
}
|
||||
#innerDiv {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body style="margin:0" onload=runTest()>
|
||||
<div id="targetDiv">
|
||||
<div id="innerDiv">
|
||||
<div id="targetDiv">
|
||||
<div id="innerDiv"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="spacer"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
var scrollend_arrived = false;
|
||||
function onScrollEnd(event) {
|
||||
assert_false(event.cancelable);
|
||||
// scrollend events targetting document are bubbled to the window.
|
||||
assert_true(event.bubbles);
|
||||
scrollend_arrived = true;
|
||||
}
|
||||
window.addEventListener("scrollend", onScrollEnd);
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
async function resetScrollers(test) {
|
||||
await waitForScrollReset(test, target_div);
|
||||
await waitForScrollReset(test, document.scrollingElement);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
promise_test (async (t) => {
|
||||
// Make sure that no scrollend event is sent to target_div.
|
||||
target_div.addEventListener("scrollend",
|
||||
t.unreached_func("target_div got unexpected scrollend event."));
|
||||
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 touchScrollInTarget(300, target_div, 'up');
|
||||
await waitFor(() => { return scrollend_arrived; },
|
||||
'Window did not receive scrollend event after scroll up on target.');
|
||||
assert_equals(target_div.scrollTop, 0);
|
||||
}, 'Tests that the window gets scrollend event when no element scrolls ' +
|
||||
'after touch scrolling.');
|
||||
}
|
||||
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>
|
||||
|
68
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html
vendored
Normal file
68
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html
vendored
Normal 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>
|
30
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html
vendored
Normal file
30
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html
vendored
Normal 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>
|
77
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html
vendored
Normal file
77
test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html
vendored
Normal 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>
|
@ -8,6 +8,7 @@
|
||||
<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;
|
||||
@ -31,169 +32,33 @@
|
||||
<script>
|
||||
var target_div = document.getElementById('targetDiv');
|
||||
|
||||
async function resetTargetScrollState(test) {
|
||||
if (target_div.scrollTop != 0 || target_div.scrollLeft != 0) {
|
||||
target_div.scrollTop = 0;
|
||||
target_div.scrollLeft = 0;
|
||||
return waitForScrollendEvent(test, target_div);
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyScrollStopped(test) {
|
||||
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(x, target_div.scrollLeft);
|
||||
assert_equals(y, target_div.scrollTop);
|
||||
resolve();
|
||||
}, unscaled_pause_time_in_ms);
|
||||
});
|
||||
}
|
||||
|
||||
async function verifyNoScrollendOnDocument(test) {
|
||||
const callback =
|
||||
test.unreached_func("window got unexpected scrollend event.");
|
||||
window.addEventListener('scrollend', callback);
|
||||
}
|
||||
|
||||
async function createScrollendPromise(test) {
|
||||
return waitForScrollendEvent(test, target_div).then(evt => {
|
||||
assert_false(evt.cancelable, 'Event is not cancelable');
|
||||
assert_false(evt.bubbles, 'Event targeting element does not bubble');
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
promise_test(async (t) => {
|
||||
// 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);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
const targetScrollendPromise = createScrollendPromise(t);
|
||||
verifyNoScrollendOnDocument(t);
|
||||
|
||||
// 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.
|
||||
.pointerUp()
|
||||
.send();
|
||||
|
||||
await targetScrollendPromise;
|
||||
|
||||
assert_true(target_div.scrollTop > 0);
|
||||
await verifyScrollStopped(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) => {
|
||||
// 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);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
const targetScrollendPromise = createScrollendPromise(t);
|
||||
verifyNoScrollendOnDocument(t);
|
||||
|
||||
const bounds = target_div.getBoundingClientRect();
|
||||
const x = bounds.right - scrollbar_width / 2;
|
||||
const y = 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);
|
||||
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) => {
|
||||
// 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;
|
||||
|
||||
resetTargetScrollState(t);
|
||||
const targetScrollendPromise = createScrollendPromise(t);
|
||||
verifyNoScrollendOnDocument(t);
|
||||
|
||||
const bounds = target_div.getBoundingClientRect();
|
||||
const x = bounds.right - scrollbar_width / 2;
|
||||
const y = 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);
|
||||
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) => {
|
||||
resetTargetScrollState(t);
|
||||
const targetScrollendPromise = createScrollendPromise(t);
|
||||
verifyNoScrollendOnDocument(t);
|
||||
|
||||
const x = 0;
|
||||
const y = 0;
|
||||
const dx = 0;
|
||||
const dy = 40;
|
||||
const duration_ms = 10;
|
||||
await new test_driver.Actions()
|
||||
.scroll(x, y, dx, dy, { origin: target_div }, duration_ms)
|
||||
.send();
|
||||
|
||||
await targetScrollendPromise;
|
||||
assert_true(target_div.scrollTop > 0);
|
||||
await verifyScrollStopped(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 resetTargetScrollState(t);
|
||||
await waitForCompositorCommit();
|
||||
|
||||
verifyNoScrollendOnDocument(t);
|
||||
const targetScrollendPromise = createScrollendPromise(t);
|
||||
|
||||
target_div.focus();
|
||||
window.test_driver.send_keys(target_div, '\ue015');
|
||||
|
||||
await targetScrollendPromise;
|
||||
assert_true(target_div.scrollTop > 0);
|
||||
await verifyScrollStopped(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>
|
||||
|
114
test/fixtures/wpt/dom/events/scrolling/scrollend-event-not-fired-on-no-scroll.html
vendored
Normal file
114
test/fixtures/wpt/dom/events/scrolling/scrollend-event-not-fired-on-no-scroll.html
vendored
Normal 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>
|
32
test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html
vendored
Normal file
32
test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html
vendored
Normal 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>
|
150
test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js
vendored
Normal file
150
test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js
vendored
Normal 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);
|
||||
}
|
85
test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html
vendored
Normal file
85
test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html
vendored
Normal 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>
|
140
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html
vendored
Normal file
140
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html
vendored
Normal 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>
|
101
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html
vendored
Normal file
101
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html
vendored
Normal 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>
|
100
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html
vendored
Normal file
100
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html
vendored
Normal 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>
|
74
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html
vendored
Normal file
74
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html
vendored
Normal 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>
|
85
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html
vendored
Normal file
85
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html
vendored
Normal 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>
|
92
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html
vendored
Normal file
92
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html
vendored
Normal 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>
|
97
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html
vendored
Normal file
97
test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html
vendored
Normal 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>
|
2
test/fixtures/wpt/versions.json
vendored
2
test/fixtures/wpt/versions.json
vendored
@ -16,7 +16,7 @@
|
||||
"path": "dom/abort"
|
||||
},
|
||||
"dom/events": {
|
||||
"commit": "ab8999891c6225bef1741c2960033aad620481a8",
|
||||
"commit": "0a811c51619b14f78fec60ba7dd1603795ca6a21",
|
||||
"path": "dom/events"
|
||||
},
|
||||
"encoding": {
|
||||
|
Loading…
Reference in New Issue
Block a user