Source code
Revision control
Copy as Markdown
Other Tools
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Ensure we get a touch-cancel after a contextmenu comes up</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function addMouseEventListeners(aTarget) {
aTarget.addEventListener("mousemove", recordEvent, true);
aTarget.addEventListener("mouseover", recordEvent, true);
aTarget.addEventListener("mouseenter", recordEvent, true);
aTarget.addEventListener("mouseout", recordEvent, true);
aTarget.addEventListener("mouseleave", recordEvent, true);
}
function removeMouseEventListeners(aTarget) {
aTarget.removeEventListener("mousemove", recordEvent, true);
aTarget.removeEventListener("mouseover", recordEvent, true);
aTarget.removeEventListener("mouseenter", recordEvent, true);
aTarget.removeEventListener("mouseout", recordEvent, true);
aTarget.removeEventListener("mouseleave", recordEvent, true);
}
async function longPressLink() {
let target = document.getElementById("b");
addMouseEventListeners(target);
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
dump("Finished synthesizing touch-start, waiting for events...\n");
});
}
var eventsFired = 0;
function recordEvent(e) {
let target = document.getElementById("b");
const platform = getPlatform();
if (platform == "windows") {
// On Windows we get a mouselongtap event once the long-tap has been detected
// by APZ, and that's what we use as the trigger to lift the finger. That then
// triggers the contextmenu. This matches the platform convention.
switch (eventsFired) {
case 0: is(e.type, "touchstart", "Got a touchstart"); break;
case 1:
is(e.type, "mouselongtap", "Got a mouselongtap");
setTimeout(async () => {
await synthesizeNativeTouch(document.getElementById("b"), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
}, 0);
break;
case 2: is(e.type, "touchend", "Got a touchend"); break;
case 3: is(e.type, "mouseover", "Got a mouseover"); break;
case 4: is(e.type, "mouseenter", "Got a mouseenter"); break;
case 5: is(e.type, "mousemove", "Got a mousemove"); break;
case 6: is(e.type, "contextmenu", "Got a contextmenu"); e.preventDefault(); break;
default: ok(false, "Got an unexpected event of type " + e.type); break;
}
eventsFired++;
if (eventsFired == 7) {
removeMouseEventListeners(target);
dump("Finished waiting for events, doing an APZ flush to see if any more unexpected events come through...\n");
promiseOnlyApzControllerFlushed().then(function() {
dump("Done APZ flush, ending test...\n");
subtestDone();
});
}
} else if (platform != "android") {
// On non-Windows desktop platforms we get a contextmenu event once the
// long-tap has been detected. Since we prevent-default that, we don't get
// a mouselongtap event at all, and instead get a touchcancel.
switch (eventsFired) {
case 0: is(e.type, "touchstart", "Got a touchstart"); break;
case 1: is(e.type, "mouseover", "Got a mouseover"); break;
case 2: is(e.type, "mouseenter", "Got a mouseenter"); break;
case 3: is(e.type, "mousemove", "Got a mousemove"); break;
case 4: is(e.type, "contextmenu", "Got a contextmenu");
// Do preventDefault() in this content, thus we will not get any
// touchcancel event.
e.preventDefault();
setTimeout(async () => {
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end, waiting for a touchend event...\n");
});
}, 0);
break;
case 5: is(e.type, "touchend", "Got a touchend");
// Send another long press.
setTimeout(async () => {
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
dump("Finished synthesizing touch-start, waiting for events...\n");
});
}, 0);
break;
case 6: is(e.type, "touchstart", "Got another touchstart"); break;
// NOTE: In this another event case, we don't get mouseover or mouseenter
// event either since the target element hasn't been changed.
case 7: is(e.type, "mousemove", "Got another mousemove"); break;
case 8: is(e.type, "contextmenu", "Got another contextmenu");
// DON'T DO preventDefault() this time, thus we should get a touchcancel
// event.
break;
case 9: is(e.type, "mouselongtap", "Got a mouselongtap"); break;
case 10: is(e.type, "touchcancel", "Got a touchcancel"); break;
default: ok(false, "Got an unexpected event of type " + e.type); break;
}
eventsFired++;
if (eventsFired == 11) {
removeMouseEventListeners(target);
setTimeout(async () => {
// Ensure the context menu got closed, otherwise in the next test case
// events will be consumed by the context menu unfortunately.
await closeContextMenu();
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
promiseOnlyApzControllerFlushed().then(function() {
dump("Done APZ flush, ending test...\n");
subtestDone();
});
});
}, 0);
}
} else {
// On Android we get a contextmenu event once the long-tap has been
// detected. If contextmenu opens we get a touchcancel event, and if
// contextmenu didn't open because of preventDefault() in the content,
// we will not get the touchcancel event.
switch (eventsFired) {
case 0: is(e.type, "touchstart", "Got a touchstart"); break;
case 1: is(e.type, "mouseover", "Got a mouseover"); break;
case 2: is(e.type, "mouseenter", "Got a mouseenter"); break;
case 3: is(e.type, "mousemove", "Got a mousemove"); break;
case 4: is(e.type, "contextmenu", "Got a contextmenu");
// Do preventDefault() in this content, thus we will not get any
// touchcancel event.
e.preventDefault();
setTimeout(async () => {
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end, waiting for a touchend event...\n");
});
}, 0);
break;
case 5: is(e.type, "touchend", "Got a touchend");
// Send another long press.
setTimeout(async () => {
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
dump("Finished synthesizing touch-start, waiting for events...\n");
});
}, 0);
break;
case 6: is(e.type, "touchstart", "Got another touchstart"); break;
// NOTE: In this another event case, we don't get mouseover or mouseenter
// event either since the target element hasn't been changed.
case 7: is(e.type, "mousemove", "Got another mousemove"); break;
case 8: is(e.type, "contextmenu", "Got another contextmenu");
// DON'T DO preventDefault() this time, thus we should get a touchcancel
// event.
break;
case 9: is(e.type, "touchcancel", "Got a touchcancel"); break;
default: ok(false, "Got an unexpected event of type " + e.type); break;
}
eventsFired++;
if (eventsFired == 10) {
removeMouseEventListeners(target);
setTimeout(async () => {
await synthesizeNativeTouch(target, 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
promiseOnlyApzControllerFlushed().then(function() {
dump("Done APZ flush, ending test...\n");
subtestDone();
});
});
}, 0);
}
}
}
window.addEventListener("touchstart", recordEvent, { passive: true, capture: true });
window.addEventListener("touchend", recordEvent, { passive: true, capture: true });
window.addEventListener("touchcancel", recordEvent, true);
window.addEventListener("contextmenu", recordEvent, true);
SpecialPowers.addChromeEventListener("mouselongtap", recordEvent, true);
waitUntilApzStable()
.then(longPressLink);
</script>
</head>
<body>
<a id="b" href="#">Link to nowhere</a>
</body>
</html>