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
|
- compression: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/compression
|
||||||
- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console
|
- 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/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
|
- 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
|
- 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
|
- 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}))
|
child.dispatchEvent(new MouseEvent("click", {bubbles:true}))
|
||||||
}, "pick the first with activation behavior <a href>")
|
}, "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) {
|
async_test(function(t) {
|
||||||
var input = document.createElement("input")
|
var input = document.createElement("input")
|
||||||
input.type = "checkbox"
|
input.type = "checkbox"
|
||||||
@ -173,6 +189,46 @@ async_test(function(t) {
|
|||||||
t.done()
|
t.done()
|
||||||
}, "disabled checkbox still has activation behavior, part 2")
|
}, "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) {
|
async_test(function(t) {
|
||||||
var input = document.createElement("input")
|
var input = document.createElement("input")
|
||||||
input.type = "checkbox"
|
input.type = "checkbox"
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<script>
|
<script>
|
||||||
// HTML elements that can be disabled
|
// HTML elements that can be disabled
|
||||||
const formElements = ["button", "fieldset", "input", "select", "textarea"];
|
const formElements = ["button", "input", "select", "textarea"];
|
||||||
|
|
||||||
test(() => {
|
test(() => {
|
||||||
for (const localName of formElements) {
|
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);
|
assert_equals(callCount, 2);
|
||||||
}, "A constructed EventTarget can be used as expected");
|
}, "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(() => {
|
test(() => {
|
||||||
class NicerEventTarget extends EventTarget {
|
class NicerEventTarget extends EventTarget {
|
||||||
on(...args) {
|
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"));
|
target.dispatchEvent(new Event("click"));
|
||||||
}, "window.event is set to the current event, which is the event passed to dispatch");
|
}, "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>
|
</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="/resources/testdriver-vendor.js"></script>
|
||||||
<script src="scroll_support.js"></script>
|
<script src="scroll_support.js"></script>
|
||||||
<style>
|
<style>
|
||||||
#targetDiv {
|
#hspacer {
|
||||||
width: 500px;
|
height: 100px;
|
||||||
height: 500px;
|
width: 100vw;
|
||||||
background: red;
|
top: 0;
|
||||||
}
|
/* on the right edge of targetDiv */
|
||||||
html, body {
|
left: 200px;
|
||||||
/* Prevent any built-in browser overscroll features from consuming the scroll
|
position: absolute;
|
||||||
* deltas */
|
}
|
||||||
overscroll-behavior: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#vspacer {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
#targetDiv {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#innerDiv {
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body style="margin:0;" onload=runTest()>
|
<body style="margin:0" onload=runTest()>
|
||||||
<div id="targetDiv">
|
<div id="targetDiv">
|
||||||
</div>
|
<div id="innerDiv"></div>
|
||||||
|
</div>
|
||||||
|
<div id="hspacer"></div>
|
||||||
|
<div id="vspacer"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var target_div = document.getElementById('targetDiv');
|
var target_div = document.getElementById('targetDiv');
|
||||||
var overscrolled_x_deltas = [];
|
var overscrolled_x_deltas = [];
|
||||||
var overscrolled_y_deltas = [];
|
var overscrolled_y_deltas = [];
|
||||||
var scrollend_received = false;
|
var scrollend_received = false;
|
||||||
|
|
||||||
function onOverscroll(event) {
|
function onOverscroll(event) {
|
||||||
overscrolled_x_deltas.push(event.deltaX);
|
overscrolled_x_deltas.push(event.deltaX);
|
||||||
overscrolled_y_deltas.push(event.deltaY);
|
overscrolled_y_deltas.push(event.deltaY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrollend(event) {
|
async function resetScrollers(test) {
|
||||||
scrollend_received = true;
|
await waitForScrollReset(test, target_div);
|
||||||
}
|
await waitForScrollReset(test, document.scrollingElement);
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener("overscroll", onOverscroll);
|
function resetOverScrollDeltas() {
|
||||||
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();
|
|
||||||
overscrolled_x_deltas = [];
|
overscrolled_x_deltas = [];
|
||||||
overscrolled_y_deltas = [];
|
overscrolled_y_deltas = [];
|
||||||
scrollend_received = false;
|
}
|
||||||
|
|
||||||
// Scroll left on target div and wait for the doc to get overscroll event.
|
function waitForOverscrollEventWithMinDelta(target, min_x = 0, min_y = 0) {
|
||||||
await touchScrollInTarget(300, target_div, 'left');
|
return new Promise((resolve) => {
|
||||||
await waitFor(() => { return scrollend_received; },
|
target.addEventListener("overscroll", (evt) => {
|
||||||
'Document did not receive scrollend event.');
|
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
|
// TODO(bokan): It looks like Chrome inappropriately filters some scroll
|
||||||
// events despite |overscroll-behavior| being set to none. The overscroll
|
// events despite |overscroll-behavior| being set to none. The overscroll
|
||||||
// amount here has been loosened but this should be fixed in Chrome.
|
// amount here has been loosened but this should be fixed in Chrome.
|
||||||
// https://crbug.com/1112183.
|
// 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_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.max(...overscrolled_x_deltas), 300, "The deltaX attribute must be <= number of pixels overscrolled (300)");
|
||||||
assert_less_than_equal(Math.min(...overscrolled_x_deltas),-50, "The deltaX attribute must be the number of pixels overscrolled.");
|
}, "testing, horizontal");
|
||||||
|
}
|
||||||
}, 'Tests that the document gets overscroll event with right deltaX/Y attributes.');
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,11 +10,14 @@
|
|||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
position: absolute;
|
||||||
|
left: 150px;
|
||||||
|
top: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#innerDiv {
|
#innerDiv {
|
||||||
width: 400px;
|
width: 250px;
|
||||||
height: 400px;
|
height: 250px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -45,7 +48,7 @@ function runTest() {
|
|||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
|
|
||||||
// Do a horizontal scroll and wait for overscroll event.
|
// 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; },
|
await waitFor(() => { return overscrolled_x_delta > 0; },
|
||||||
'Scroller did not receive overscroll event after horizontal scroll.');
|
'Scroller did not receive overscroll event after horizontal scroll.');
|
||||||
assert_equals(scrolling_div.scrollWidth - scrolling_div.scrollLeft,
|
assert_equals(scrolling_div.scrollWidth - scrolling_div.scrollLeft,
|
||||||
@ -55,7 +58,7 @@ function runTest() {
|
|||||||
overscrolled_y_delta = 0;
|
overscrolled_y_delta = 0;
|
||||||
|
|
||||||
// Do a vertical scroll and wait for overscroll event.
|
// 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; },
|
await waitFor(() => { return overscrolled_y_delta > 0; },
|
||||||
'Scroller did not receive overscroll event after vertical scroll.');
|
'Scroller did not receive overscroll event after vertical scroll.');
|
||||||
assert_equals(scrolling_div.scrollHeight - scrolling_div.scrollTop,
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeoutCallback = test.step_timeout(() => {
|
const timeoutCallback = test.step_timeout(() => {
|
||||||
reject(`No Scrollend event received for target ${target}`);
|
reject(`No ${eventName} event received for target ${target}`);
|
||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
target.addEventListener('scrollend', (evt) => {
|
target.addEventListener(eventName, (evt) => {
|
||||||
clearTimeout(timeoutCallback);
|
clearTimeout(timeoutCallback);
|
||||||
resolve(evt);
|
resolve(evt);
|
||||||
}, { once: true });
|
}, { 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_FRAME = 700;
|
||||||
const MAX_UNCHANGED_FRAMES = 20;
|
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
|
// TODO(crbug.com/1400399): Deprecate as frame rates may vary greatly in
|
||||||
// different test environments.
|
// different test environments.
|
||||||
function waitForAnimationEnd(getValue) {
|
function waitForAnimationEnd(getValue) {
|
||||||
@ -70,6 +161,10 @@ function waitForAnimationEnd(getValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scrolls in target according to move_path with pauses in between
|
// 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) {
|
function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_in_ms = 100) {
|
||||||
const test_driver_actions = new test_driver.Actions()
|
const test_driver_actions = new test_driver.Actions()
|
||||||
.addPointer("pointer1", "touch")
|
.addPointer("pointer1", "touch")
|
||||||
@ -88,7 +183,7 @@ function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_
|
|||||||
y += step_y;
|
y += step_y;
|
||||||
test_driver_actions.pointerMove(x, y, {origin: target});
|
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();
|
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.
|
// Trigger fling by doing pointerUp right after pointerMoves.
|
||||||
function touchFlingInTarget(pixels_to_scroll, target, direction) {
|
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) {
|
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);
|
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 {
|
#innerDiv {
|
||||||
width: 500px;
|
width: 4000px;
|
||||||
height: 4000px;
|
height: 4000px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -28,36 +28,64 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const target_div = document.getElementById('targetDiv');
|
const target_div = document.getElementById('targetDiv');
|
||||||
let scrollend_arrived = false;
|
|
||||||
let scrollend_event_count = 0;
|
|
||||||
|
|
||||||
function onScrollEnd(event) {
|
async function testWithMovePath(t, move_path) {
|
||||||
assert_false(event.cancelable);
|
// Skip the test on a Mac as they do not support touch screens.
|
||||||
assert_false(event.bubbles);
|
const isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
|
||||||
scrollend_arrived = true;
|
if (isMac)
|
||||||
scrollend_event_count += 1;
|
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() {
|
function runTest() {
|
||||||
promise_test (async (t) => {
|
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.
|
// 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 = [
|
const move_path = [
|
||||||
{ x: 0, y: -300}, // down
|
{ x: 0, y: -80 }, // Scroll down
|
||||||
{ x: 0, y: -100}, // up
|
{ x: 0, y: -40 }, // Scroll up
|
||||||
{ x: 0, y: -400}, // down
|
{ x: 0, y: -80 }, // Scroll down
|
||||||
{ x: 0, y: -200}, // up
|
|
||||||
];
|
];
|
||||||
await touchScrollInTargetSequentiallyWithPause(target_div, move_path, 150);
|
await testWithMovePath(t, move_path);
|
||||||
|
|
||||||
await waitFor(() => {return scrollend_arrived;},
|
|
||||||
'target_div did not receive scrollend event after sequence of scrolls on target.');
|
|
||||||
assert_equals(scrollend_event_count, 1);
|
|
||||||
}, "Move down, up and down again, receive scrollend event only once");
|
}, "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>
|
</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>
|
<!DOCTYPE HTML>
|
||||||
<meta name="timeout" content="long">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
|
||||||
<script src="/resources/testharness.js"></script>
|
<script src="/resources/testharness.js"></script>
|
||||||
<script src="/resources/testharnessreport.js"></script>
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
<script src="/resources/testdriver.js"></script>
|
<script src="/resources/testdriver.js"></script>
|
||||||
<script src="/resources/testdriver-actions.js"></script>
|
<script src="/resources/testdriver-actions.js"></script>
|
||||||
<script src="/resources/testdriver-vendor.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>
|
<script src="scroll_support.js"></script>
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
@ -31,8 +39,8 @@ html {
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
var element_scrollend_arrived = false;
|
let element_scrollend_arrived = false;
|
||||||
var document_scrollend_arrived = false;
|
let document_scrollend_arrived = false;
|
||||||
|
|
||||||
function onElementScrollEnd(event) {
|
function onElementScrollEnd(event) {
|
||||||
assert_false(event.cancelable);
|
assert_false(event.cancelable);
|
||||||
@ -47,89 +55,95 @@ function onDocumentScrollEnd(event) {
|
|||||||
document_scrollend_arrived = true;
|
document_scrollend_arrived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function callScrollFunction([scrollTarget, scrollFunction, args]) {
|
let scroll_fn_variants = [
|
||||||
scrollTarget[scrollFunction](args);
|
{
|
||||||
}
|
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() {
|
function runTest() {
|
||||||
let root_element = document.scrollingElement;
|
|
||||||
let target_div = document.getElementById("targetDiv");
|
|
||||||
|
|
||||||
promise_test (async (t) => {
|
async function testScrollFn(testInfo, t) {
|
||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
target_div.addEventListener("scrollend", onElementScrollEnd);
|
|
||||||
|
targetDiv.addEventListener("scrollend", onElementScrollEnd);
|
||||||
document.addEventListener("scrollend", onDocumentScrollEnd);
|
document.addEventListener("scrollend", onDocumentScrollEnd);
|
||||||
|
|
||||||
let test_cases = [
|
testInfo.target[testInfo.fn]({ top: 200, left: 200,
|
||||||
[target_div, 200, 200, [target_div, "scrollTo", { top: 200, left: 200, behavior: "auto" }]],
|
behavior: testInfo.behavior });
|
||||||
[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" }]]
|
|
||||||
];
|
|
||||||
|
|
||||||
for(i = 0; i < test_cases.length; i++) {
|
await waitFor(() => {
|
||||||
let t = test_cases[i];
|
return element_scrollend_arrived || document_scrollend_arrived;
|
||||||
let target = t[0];
|
}, testInfo.target.tagName + "." + testInfo.fn + " did not receive scrollend event.");
|
||||||
let expected_x = t[1];
|
if (testInfo.target == document.scrollingElement) {
|
||||||
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)
|
|
||||||
assert_false(element_scrollend_arrived);
|
assert_false(element_scrollend_arrived);
|
||||||
else
|
} else {
|
||||||
assert_false(document_scrollend_arrived);
|
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) => {
|
assert_equals(testInfo.target.scrollLeft, 200,
|
||||||
await waitForCompositorCommit();
|
testInfo.target.tagName + "." + testInfo.fn + " scrollLeft");
|
||||||
|
assert_equals(testInfo.target.scrollTop, 200,
|
||||||
let test_cases = [
|
testInfo.target.tagName + "." + testInfo.fn + " scrollTop");
|
||||||
[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;
|
|
||||||
}
|
}
|
||||||
}, "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>
|
</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);
|
document.body.appendChild(out_div);
|
||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
|
|
||||||
element_scrollend_arrived = false;
|
|
||||||
document_scrollend_arrived = false;
|
|
||||||
inner_div.scrollIntoView({ inline: "end", block: "end", behavior: "auto" });
|
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.scrollLeft, 0, "Nested scrollIntoView root_element scrollLeft");
|
||||||
assert_equals(root_element.scrollTop, 0, "Nested scrollIntoView root_element scrollTop");
|
assert_equals(root_element.scrollTop, 0, "Nested scrollIntoView root_element scrollTop");
|
||||||
assert_equals(out_div.scrollLeft, 100, "Nested scrollIntoView out_div scrollLeft");
|
assert_equals(out_div.scrollLeft, 100, "Nested scrollIntoView out_div scrollLeft");
|
||||||
|
@ -7,64 +7,100 @@
|
|||||||
<script src="/resources/testdriver-vendor.js"></script>
|
<script src="/resources/testdriver-vendor.js"></script>
|
||||||
<script src="scroll_support.js"></script>
|
<script src="scroll_support.js"></script>
|
||||||
<style>
|
<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;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
#innerDiv {
|
#innerDiv {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body style="margin:0" onload=runTest()>
|
<body style="margin:0" onload=runTest()>
|
||||||
<div id="targetDiv">
|
<div id="targetDiv">
|
||||||
<div id="innerDiv">
|
<div id="innerDiv"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="hspacer"></div>
|
||||||
|
<div id="vspacer"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var target_div = document.getElementById('targetDiv');
|
var target_div = document.getElementById('targetDiv');
|
||||||
var horizontal_scrollend_arrived = false;
|
async function resetScrollers(test) {
|
||||||
var vertical_scrollend_arrived = false;
|
await waitForScrollReset(test, target_div);
|
||||||
function onHorizontalScrollEnd(event) {
|
await waitForScrollReset(test, document.scrollingElement);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runTest() {
|
function fail() {
|
||||||
promise_test (async (t) => {
|
assert_true(false);
|
||||||
// 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 runTest() {
|
||||||
|
promise_test(async (t) => {
|
||||||
|
await resetScrollers(t);
|
||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
|
|
||||||
// Scroll left on target div and wait for the doc to get scrollend event.
|
assert_equals(document.scrollingElement.scrollTop, 0,
|
||||||
document.addEventListener("scrollend", onHorizontalScrollEnd);
|
"document should not be scrolled");
|
||||||
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);
|
|
||||||
|
|
||||||
|
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.
|
// Scroll up on target div and wait for the doc to get scrollend event.
|
||||||
document.addEventListener("scrollend", onVerticalScrollEnd);
|
await scrollElementDown(target_div, target_div.clientHeight + 25);
|
||||||
await touchScrollInTarget(300, target_div, 'up');
|
await scrollend_promise;
|
||||||
await waitFor(() => { return vertical_scrollend_arrived; },
|
|
||||||
'Document did not receive scrollend event after scroll up on target.');
|
target_div.removeEventListener("scrollend", fail);
|
||||||
assert_equals(target_div.scrollTop, 0);
|
assert_greater_than(document.scrollingElement.scrollTop, target_div.clientHeight - 1,
|
||||||
}, 'Tests that the document gets scrollend event when no element scrolls by ' +
|
"document is scrolled by the height of target_div");
|
||||||
'touch.');
|
}, "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>
|
</script>
|
||||||
|
@ -7,96 +7,167 @@
|
|||||||
<script src="/resources/testdriver-vendor.js"></script>
|
<script src="/resources/testdriver-vendor.js"></script>
|
||||||
<script src="scroll_support.js"></script>
|
<script src="scroll_support.js"></script>
|
||||||
<style>
|
<style>
|
||||||
#overscrollXDiv {
|
#overscrollXDiv {
|
||||||
width: 600px;
|
width: 200px;
|
||||||
height: 600px;
|
height: 200px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
overscroll-behavior-x: contain;
|
overscroll-behavior-x: contain;
|
||||||
}
|
border: solid 1px black;
|
||||||
#overscrollYDiv {
|
display: grid;
|
||||||
width: 500px;
|
/* Places content and targetXDiv beside each other. */
|
||||||
height: 500px;
|
grid-template-columns: 500px 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#overscrollYDiv {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior-y: none;
|
||||||
}
|
border: solid 1px black;
|
||||||
#targetDiv {
|
}
|
||||||
width: 400px;
|
|
||||||
height: 400px;
|
#targetXDiv {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
border: solid 1px black;
|
||||||
.content {
|
}
|
||||||
width:800px;
|
|
||||||
height:800px;
|
#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>
|
</style>
|
||||||
|
|
||||||
<body style="margin:0" onload=runTest()>
|
<body style="margin:0" onload=runTest()>
|
||||||
<div id="overscrollXDiv">
|
<div id="overscrollXDiv">
|
||||||
<div class=content>
|
<div class="content"></div>
|
||||||
<div id="overscrollYDiv">
|
<div id="targetXDiv">
|
||||||
<div class=content>
|
|
||||||
<div id="targetDiv">
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
<div id="spacer"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var target_div = document.getElementById('targetDiv');
|
var horizontal_scrollend_arrived = false;
|
||||||
var horizontal_scrollend_arrived = false;
|
var vertical_scrollend_arrived = false;
|
||||||
var vertical_scrollend_arrived = false;
|
let scrollers = [document.scrollingElement, targetXDiv, targetYDiv,
|
||||||
function onHorizontalScrollEnd(event) {
|
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.cancelable);
|
||||||
assert_false(event.bubbles);
|
assert_false(event.bubbles);
|
||||||
horizontal_scrollend_arrived = true;
|
horizontal_scrollend_arrived = true;
|
||||||
}
|
}
|
||||||
function onVerticalScrollEnd(event) {
|
function onVerticalScrollEnd(event) {
|
||||||
assert_false(event.cancelable);
|
assert_false(event.cancelable);
|
||||||
assert_false(event.bubbles);
|
assert_false(event.bubbles);
|
||||||
vertical_scrollend_arrived = true;
|
vertical_scrollend_arrived = true;
|
||||||
}
|
}
|
||||||
// Test that both "onscrollend" and addEventListener("scrollend"... work.
|
// Test that both "onscrollend" and addEventListener("scrollend"... work.
|
||||||
document.getElementById('overscrollXDiv').onscrollend = onHorizontalScrollEnd;
|
document.getElementById('overscrollXDiv').onscrollend = onHorizontalScrollEnd;
|
||||||
document.getElementById('overscrollYDiv').
|
document.getElementById('overscrollYDiv').
|
||||||
addEventListener("scrollend", onVerticalScrollEnd);
|
addEventListener("scrollend", onVerticalScrollEnd);
|
||||||
|
|
||||||
function runTest() {
|
function runTest() {
|
||||||
promise_test (async (t) => {
|
promise_test(async (t) => {
|
||||||
// Make sure that no scrollend event is sent to document or target_div.
|
await resetScrollers(t);
|
||||||
document.addEventListener("scrollend",
|
|
||||||
t.unreached_func("Document got unexpected scrollend event."));
|
|
||||||
target_div.addEventListener("scrollend",
|
|
||||||
t.unreached_func("target_div got unexpected scrollend event."));
|
|
||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
|
|
||||||
// Scroll left on target div and wait for the element with overscroll-x to
|
// Make sure that no scrollend event is sent to document.
|
||||||
// get scrollend event.
|
document.addEventListener("scrollend",
|
||||||
await touchScrollInTarget(300, target_div, 'left');
|
t.unreached_func("Document got unexpected scrollend event."));
|
||||||
await waitFor(() => { return horizontal_scrollend_arrived; },
|
let scrollend_promise;
|
||||||
'Expected element did not receive scrollend event after scroll left ' +
|
|
||||||
'on target.');
|
|
||||||
assert_equals(target_div.scrollLeft, 0);
|
|
||||||
|
|
||||||
|
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) => {
|
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;
|
await touchEndPromise;
|
||||||
|
|
||||||
// Ensure we wait at least a tick after the touch end.
|
// Ensure we wait at least a tick after the touch end.
|
||||||
await waitForCompositorCommit();
|
await waitForCompositorCommit();
|
||||||
|
|
||||||
// We should not trigger a scrollend event for a scroll that did not change
|
// We should not trigger a scrollend event for a scroll that did not
|
||||||
// the scroll position.
|
// change the scroll position.
|
||||||
assert_equals(vertical_scrollend_arrived, false);
|
assert_equals(horizontal_scrollend_arrived, false,
|
||||||
assert_equals(target_div.scrollTop, 0);
|
"overscrollXDiv should not receive scrollend");
|
||||||
}, 'Tests that the last element in the cut scroll chain gets scrollend ' +
|
assert_equals(vertical_scrollend_arrived, false,
|
||||||
'event when no element scrolls by touch.');
|
"overscrollYDiv should not receive scrollend");
|
||||||
}
|
}, "Tests that the scroll is not propagated beyond div with non-auto " +
|
||||||
|
"overscroll-behavior.");
|
||||||
|
}
|
||||||
</script>
|
</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="/resources/testdriver-vendor.js"></script>
|
||||||
<script src="scroll_support.js"></script>
|
<script src="scroll_support.js"></script>
|
||||||
<style>
|
<style>
|
||||||
#targetDiv {
|
#spacer {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
#targetDiv {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
#innerDiv {
|
||||||
#innerDiv {
|
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body style="margin:0" onload=runTest()>
|
<body style="margin:0" onload=runTest()>
|
||||||
<div id="targetDiv">
|
<div id="targetDiv">
|
||||||
<div id="innerDiv">
|
<div id="innerDiv"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="spacer"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var target_div = document.getElementById('targetDiv');
|
var target_div = document.getElementById('targetDiv');
|
||||||
var scrollend_arrived = false;
|
async function resetScrollers(test) {
|
||||||
function onScrollEnd(event) {
|
await waitForScrollReset(test, target_div);
|
||||||
assert_false(event.cancelable);
|
await waitForScrollReset(test, document.scrollingElement);
|
||||||
// scrollend events targetting document are bubbled to the window.
|
}
|
||||||
assert_true(event.bubbles);
|
|
||||||
scrollend_arrived = true;
|
|
||||||
}
|
|
||||||
window.addEventListener("scrollend", onScrollEnd);
|
|
||||||
|
|
||||||
function runTest() {
|
function fail() {
|
||||||
promise_test (async (t) => {
|
assert_true(false);
|
||||||
// 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 runTest() {
|
||||||
|
promise_test(async (t) => {
|
||||||
|
await resetScrollers(t);
|
||||||
await waitForCompositorCommit();
|
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.
|
// Scroll up on target div and wait for the doc to get scrollend event.
|
||||||
await touchScrollInTarget(300, target_div, 'up');
|
await scrollElementDown(target_div, target_div.clientHeight + 25);
|
||||||
await waitFor(() => { return scrollend_arrived; },
|
await scrollend_promise;
|
||||||
'Window did not receive scrollend event after scroll up on target.');
|
|
||||||
assert_equals(target_div.scrollTop, 0);
|
target_div.removeEventListener("scrollend", fail);
|
||||||
}, 'Tests that the window gets scrollend event when no element scrolls ' +
|
assert_greater_than(document.scrollingElement.scrollTop, target_div.clientHeight - 1,
|
||||||
'after touch scrolling.');
|
"document is scrolled by the height of target_div");
|
||||||
}
|
}, "testing, vertical");
|
||||||
|
}
|
||||||
</script>
|
</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-actions.js"></script>
|
||||||
<script src="/resources/testdriver-vendor.js"></script>
|
<script src="/resources/testdriver-vendor.js"></script>
|
||||||
<script src="scroll_support.js"></script>
|
<script src="scroll_support.js"></script>
|
||||||
|
<script src="scrollend-user-scroll-common.js"></script>
|
||||||
<style>
|
<style>
|
||||||
#targetDiv {
|
#targetDiv {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
@ -31,169 +32,33 @@
|
|||||||
<script>
|
<script>
|
||||||
var target_div = document.getElementById('targetDiv');
|
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() {
|
function runTest() {
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
// Skip the test on a Mac as they do not support touch screens.
|
await test_scrollend_on_touch_drag(t, target_div);
|
||||||
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);
|
|
||||||
}, 'Tests that the target_div gets scrollend event when touch dragging.');
|
}, 'Tests that the target_div gets scrollend event when touch dragging.');
|
||||||
|
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
// Skip test on platforms that do not have a visible scrollbar (e.g.
|
await test_scrollend_on_scrollbar_gutter_click(t, target_div);
|
||||||
// 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);
|
|
||||||
}, 'Tests that the target_div gets scrollend event when clicking ' +
|
}, 'Tests that the target_div gets scrollend event when clicking ' +
|
||||||
'scrollbar.');
|
'scrollbar.');
|
||||||
|
|
||||||
// Same issue as previous test.
|
// Same issue as previous test.
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
// Skip test on platforms that do not have a visible scrollbar (e.g.
|
await test_scrollend_on_scrollbar_thumb_drag(t, target_div);
|
||||||
// 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);
|
|
||||||
}, 'Tests that the target_div gets scrollend event when dragging the ' +
|
}, 'Tests that the target_div gets scrollend event when dragging the ' +
|
||||||
'scrollbar thumb.');
|
'scrollbar thumb.');
|
||||||
|
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
resetTargetScrollState(t);
|
await test_scrollend_on_mousewheel_scroll(t, target_div);
|
||||||
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);
|
|
||||||
}, 'Tests that the target_div gets scrollend event when mouse wheel ' +
|
}, 'Tests that the target_div gets scrollend event when mouse wheel ' +
|
||||||
'scrolling.');
|
'scrolling.');
|
||||||
|
|
||||||
promise_test(async (t) => {
|
promise_test(async (t) => {
|
||||||
await resetTargetScrollState(t);
|
await test_scrollend_on_keyboard_scroll(t, target_div);
|
||||||
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);
|
|
||||||
}, 'Tests that the target_div gets scrollend event when sending DOWN key ' +
|
}, 'Tests that the target_div gets scrollend event when sending DOWN key ' +
|
||||||
'to the target.');
|
'to the target.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</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"
|
"path": "dom/abort"
|
||||||
},
|
},
|
||||||
"dom/events": {
|
"dom/events": {
|
||||||
"commit": "ab8999891c6225bef1741c2960033aad620481a8",
|
"commit": "0a811c51619b14f78fec60ba7dd1603795ca6a21",
|
||||||
"path": "dom/events"
|
"path": "dom/events"
|
||||||
},
|
},
|
||||||
"encoding": {
|
"encoding": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user