<!doctype html>
<html>
<head>
<title>Set/Release capture</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="pointerevent_styles.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>
<!-- Additional helper script for common checks across event types -->
<script type="text/javascript" src="pointerevent_support.js"></script>
</head>
<body>
<div class="spacer"></div>
<div id="target0"></div>
<div class="spacer"></div>
<div id="target1"></div>
<div class="spacer"></div>
<input type="button" id="captureButton" value="Set Capture">
</body>
<script type='text/javascript'>
window.onload = () => {
let eventLog = [];
let nextUncheckedEventIndex = 0;
const target0 = document.getElementById('target0');
const target1 = document.getElementById('target1');
const captureButton = document.getElementById('captureButton');
function eventLabel(target, eventName) {
return `${eventName}@${target.id}`;
}
function recordEvent(target, eventName) {
eventLog.push(eventLabel(target, eventName));
}
// Ensure match to the next sequence of events in the event log.
function assert_next_events(target, expectedEventNames, message) {
for (let i = 0; i < expectedEventNames.length; i++) {
assert_true(nextUncheckedEventIndex < eventLog.length,
`${message}: empty event queue`);
const observed = eventLog[nextUncheckedEventIndex++];
const expected = eventLabel(target, expectedEventNames[i]);
assert_equals(observed, expected,`${message}: Event mismatch`);
}
}
// After validating the expected events, all entries in the event map
// must be false or we have recorded an unexpected event.
function assert_empty_event_queue(message) {
const uncheckedEvents = eventLog.length - nextUncheckedEventIndex;
assert_equals(uncheckedEvents, 0,
`${message}: Unexpected events ` +
`${eventLog.slice(-uncheckedEvents).join(", ")}`);
}
// Adds listeners for all element-pointerevent combinations. Each listener
// records the event for validation. the pointerdown event on the button
// triggers an extra step of triggering pointer capture.
function addEventListeners(t) {
// Adds a single event that is removed at the conclusion of the test.
const addListener = (target, eventName, fn) => {
const callback = (e) => {
recordEvent(target, eventName);
// Additional event handling is optional.
if (fn)
fn(e);
};
target.addEventListener(eventName, callback);
t.add_cleanup(() => {
target.removeEventListener(eventName, callback);
});
};
[target0, target1, captureButton].forEach(el => {
['gotpointercapture', 'lostpointercapture', 'pointerenter',
'pointerleave', 'pointermove', 'pointerout',
'pointerover'].forEach(eventName => {
addListener(el, eventName);
});
});
addListener(captureButton, 'pointerdown', (e) => {
target0.setPointerCapture(e.pointerId);
});
t.add_cleanup(() => {
eventLog = [];
nextUncheckedEventIndex = 0;
});
}
// Trigger and wait for a pointer move. The wait is to ensure there is
// no coalescence of pointer move events.
async function moveTo(x, y, target) {
const movePromise = getEvent('pointermove', target);
let actions = new test_driver.Actions()
.pointerMove(x, y, { origin: target })
.send();
await actions;
await movePromise;
}
promise_test(async t => {
// Reset pointer position.
await moveTo(0, 0, document.body);
addEventListeners(t);
// Move to the first target.
await moveTo(0, 0, target0);
assert_next_events(target0,["pointerover", "pointerenter", "pointermove"],
'Move to first target');
assert_empty_event_queue('Check after first move');
// Move to the second taret.
await moveTo(0, 0, target1);
assert_next_events(target0, ['pointerout', 'pointerleave'],
'Exit first target');
assert_next_events(target1,
["pointerover", "pointerenter", "pointermove"],
'Move to second target');
assert_empty_event_queue('Check after second move');
// Move to the capture button.
await moveTo(0, 0, captureButton);
assert_next_events(target1, ['pointerout', 'pointerleave'],
'Exit second target');
assert_next_events(
captureButton, ["pointerover", "pointerenter", "pointermove"],
'Move to button');
assert_empty_event_queue('Check after third move');
}, 'Validate pointer events track pointer movement without pointer '
+ 'capture.');
async function runCaptureAndHoverTargetActionSequence(t, hoverTarget) {
const pointerUpPromise = getEvent('pointerup', document);
const actionsPromise =
new test_driver.Actions()
// Start outside capture button.
.pointerMove(0, 0)
.pointerDown()
.pointerUp()
// Move to the capture button
.pointerMove(0, 0, {origin: captureButton})
// Trigger pointer capture
.pointerDown()
// Hover over the target element
.pointerMove(10, 0, {origin: hoverTarget})
// Release capture
.pointerUp()
.send();
await actionsPromise;
await pointerUpPromise;
}
promise_test(async t => {
// Reset pointer position.
await moveTo(0, 0, document.body);
addEventListeners(t);
// On entry for this sub-test, the pointer is at the document origin.
await runCaptureAndHoverTargetActionSequence(t, captureButton);
assert_next_events(
captureButton,
['pointerover', 'pointerenter', 'pointermove'],
'Move to button from origin');
assert_next_events(
captureButton,
['pointerdown', 'pointerout', 'pointerleave'],
'Pointer down on button to trigger capture');
assert_next_events(
target0,
['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
'Capture triggered while hovering over button');
assert_next_events(
target0,
['lostpointercapture', 'pointerout', 'pointerleave'],
'Lose pointer capture while hovering over button');
// Post pointer capture.
assert_next_events(
captureButton,
['pointerover', 'pointerenter'],
'Post capture while hovering over button');
assert_empty_event_queue('Check after button hover');
// On entry for this sub-test, the pointer is over the button.
await runCaptureAndHoverTargetActionSequence(t, target0);
assert_next_events(
captureButton,
['pointerout', 'pointerleave'],
'Move from button to document origin');
assert_next_events(
captureButton,
['pointerover', 'pointerenter', 'pointermove'],
'Move from document origin to button');
assert_next_events(
captureButton,
[ 'pointerdown', 'pointerout', 'pointerleave'],
'Pointer down on button to trigger capture');
assert_next_events(
target0,
['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
'Capture triggered while hovering over capture target');
assert_next_events(
target0,
['lostpointercapture'],
'Lose pointer capture while hovering over capture target');
assert_empty_event_queue('Check after hover on capture target');
// On entry for this sub-test, the pointer is over the capture target.
await runCaptureAndHoverTargetActionSequence(t, target1);
assert_next_events(
target0,
['pointerout', 'pointerleave'],
'Move from capture target to button');
assert_next_events(
captureButton,
['pointerover', 'pointerenter', 'pointermove'],
'Move from capture target to button');
assert_next_events(
captureButton,
[ 'pointerdown', 'pointerout', 'pointerleave'],
'Pointer down on button to trigger capture');
assert_next_events(
target0,
['pointerover', 'pointerenter', 'gotpointercapture', 'pointermove'],
'Capture triggered while hovering over non-capture target');
assert_next_events(
target0,
['lostpointercapture', 'pointerout', 'pointerleave'],
'Lose pointer capture while hovering over non-capture target');
assert_next_events(
target1,
["pointerover", "pointerenter"],
'Post capture while hovering over non-capture target');
assert_empty_event_queue('Check after hover on non-capture target ');
}, 'Test pointer capture.');
};
</script>
</html>