Skip to content

Commit

Permalink
Close watchers: always fire cancel events
Browse files Browse the repository at this point in the history
Sometimes they will have cancelable = false, but they will now always
fire. See whatwg/html#10047.

Bug: 41484805, 40054591
Change-Id: Ica682043fb56c729f4c331e9f3bd0590d3b1d088
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5465306
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1291009}
  • Loading branch information
domenic authored and chromium-wpt-export-bot committed Apr 23, 2024
1 parent d2b4aa8 commit 69f5a6f
Show file tree
Hide file tree
Showing 39 changed files with 266 additions and 114 deletions.
4 changes: 2 additions & 2 deletions close-watcher/abortsignal.html
Expand Up @@ -49,7 +49,7 @@
watcher.requestClose();
controller.abort();

assert_equals(oncancel_call_count_, 0);
assert_equals(oncancel_call_count_, 1);
assert_equals(onclose_call_count_, 1);
}, "requestClose() then abortController.abort() fires only one close event");

Expand Down Expand Up @@ -92,7 +92,7 @@
await sendCloseRequest();
controller.abort();

assert_equals(oncancel_call_count_, 0);
assert_equals(oncancel_call_count_, 1);
assert_equals(onclose_call_count_, 1);
}, "Esc key then abortController.abort() fires only one close event");

Expand Down
22 changes: 11 additions & 11 deletions close-watcher/basic.html
Expand Up @@ -14,8 +14,8 @@

watcher.requestClose();

assert_array_equals(events, ["close"]);
}, "requestClose() with no user activation only fires close");
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "requestClose() with no user activation");

test(t => {
let events = [];
Expand All @@ -25,7 +25,7 @@
watcher.requestClose();

assert_array_equals(events, []);
}, "destroy() then requestClose() fires no events");
}, "destroy() then requestClose()");

test(t => {
let events = [];
Expand All @@ -36,18 +36,18 @@

watcher.requestClose();
assert_array_equals(events, ["close"]);
}, "close() then requestClose() fires only one close event");
}, "close() then requestClose()");

test(t => {
let events = [];
let watcher = createRecordingCloseWatcher(t, events);

watcher.requestClose();
assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);

watcher.destroy();
assert_array_equals(events, ["close"]);
}, "requestClose() then destroy() fires only one close event");
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "requestClose() then destroy()");

test(t => {
let events = [];
Expand All @@ -58,7 +58,7 @@

watcher.destroy();
assert_array_equals(events, ["close"]);
}, "close() then destroy() fires only one close event");
}, "close() then destroy()");

promise_test(async t => {
let events = [];
Expand All @@ -68,7 +68,7 @@
await sendCloseRequest();

assert_array_equals(events, []);
}, "destroy() then close request fires no events");
}, "destroy() then close request");

promise_test(async t => {
let events = [];
Expand All @@ -77,6 +77,6 @@
await sendCloseRequest();
watcher.destroy();

assert_array_equals(events, ["close"]);
}, "Close request then destroy() fires only one close event");
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "Close request then destroy()");
</script>
2 changes: 1 addition & 1 deletion close-watcher/esc-key/keypress.html
Expand Up @@ -16,6 +16,6 @@

await sendEscKey();

assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "A keypress listener can NOT prevent the Esc keypress from being interpreted as a close request");
</script>
2 changes: 1 addition & 1 deletion close-watcher/esc-key/keyup.html
Expand Up @@ -16,6 +16,6 @@

await sendEscKey();

assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "A keyup listener can NOT prevent the Esc keypress from being interpreted as a close request");
</script>
4 changes: 2 additions & 2 deletions close-watcher/esc-key/not-user-activation.html
Expand Up @@ -14,6 +14,6 @@

await sendEscKey();

assert_array_equals(events, ["close"]);
}, "Esc key does not count as user activation, so if it is the sole user interaction, that fires close but not cancel");
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "Esc key does not count as user activation, so if it is the sole user interaction, cancel is cancelable=false");
</script>
24 changes: 12 additions & 12 deletions close-watcher/inside-event-listeners.html
Expand Up @@ -17,10 +17,10 @@
await test_driver.bless("give user activation so that cancel will fire", () => {
watcher.requestClose();
});
assert_array_equals(events, ["cancel"]);
assert_array_equals(events, ["cancel[cancelable=true]"]);

watcher.requestClose();
assert_array_equals(events, ["cancel"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=true]"], "since it was inactive, no more events fired");
}, "destroy() inside oncancel");

test(t => {
Expand All @@ -30,10 +30,10 @@
watcher.onclose = () => { watcher.destroy(); }

watcher.requestClose();
assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);

watcher.requestClose();
assert_array_equals(events, ["close"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=false]", "close"], "since it was inactive, no more events fired");
}, "destroy() inside onclose");

promise_test(async t => {
Expand All @@ -45,10 +45,10 @@
await test_driver.bless("give user activation so that cancel will fire", () => {
watcher.requestClose();
});
assert_array_equals(events, ["cancel", "close"]);
assert_array_equals(events, ["cancel[cancelable=true]", "close"]);

watcher.requestClose();
assert_array_equals(events, ["cancel", "close"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=true]", "close"], "since it was inactive, no more events fired");
}, "close() inside oncancel");

test(t => {
Expand All @@ -58,10 +58,10 @@
watcher.onclose = () => { watcher.close(); }

watcher.requestClose();
assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);

watcher.requestClose();
assert_array_equals(events, ["close"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=false]", "close"], "since it was inactive, no more events fired");
}, "close() inside onclose");

promise_test(async t => {
Expand All @@ -73,10 +73,10 @@
await test_driver.bless("give user activation so that cancel will fire", () => {
watcher.requestClose();
});
assert_array_equals(events, ["cancel", "close"]);
assert_array_equals(events, ["cancel[cancelable=true]", "close"]);

watcher.requestClose();
assert_array_equals(events, ["cancel", "close"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=true]", "close"], "since it was inactive, no more events fired");
}, "requestClose() inside oncancel");

test(t => {
Expand All @@ -86,9 +86,9 @@
watcher.onclose = () => { watcher.requestClose(); }

watcher.requestClose();
assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);

watcher.requestClose();
assert_array_equals(events, ["close"], "since it was inactive, no more events fired");
assert_array_equals(events, ["cancel[cancelable=false]", "close"], "since it was inactive, no more events fired");
}, "requestClose() inside onclose");
</script>
11 changes: 8 additions & 3 deletions close-watcher/resources/helpers.js
Expand Up @@ -26,9 +26,14 @@ window.createRecordingCloseWatcher = (t, events, name, type, parentWatcher) => {
t.add_cleanup(() => watcher.destroy());
}

const prefix = name === undefined ? "" : name + " ";
watcher.addEventListener('cancel', () => events.push(prefix + "cancel"));
watcher.addEventListener('close', () => events.push(prefix + "close"));
const prefix = name === undefined ? '' : name + ' ';
watcher.addEventListener('cancel', e => {
const cancelable = e.cancelable ? '[cancelable=true]' : '[cancelable=false]';
events.push(prefix + 'cancel' + cancelable);
});
watcher.addEventListener('close', () => {
events.push(prefix + 'close');
});

return watcher;
};
Expand Down
25 changes: 25 additions & 0 deletions close-watcher/user-activation/README.md
@@ -0,0 +1,25 @@
# Close watcher user activation tests

These tests are all in separate files (or test variants) because we need to be
sure we're starting from zero user activation.

## Note on variants vs. `-dialog` and `-CloseWatcher` files

We endeavor to have all the tests in these files cover both `<dialog>` elements
and the `CloseWatcher` API. (And sometimes the `popover=""` attribute.)

When the test expectations are the same for both `<dialog>` and `CloseWatcher`,
we use WPT's variants feature.

However, in some cases different expectations are necessary. This is because
`<dialog>`s queue a task to fire their `close` event, and do not queue a task
to fire their `cancel` event. Thus, when you have two `<dialog>`s grouped
together, you get the somewhat-strange behavior of both `cancel`s firing first,
then both `close`s. Whereas `CloseWatcher`s do not have this issue; both events
fire synchronously.

(Note that scheduling the `cancel` event for `<dialog>`s is not really possible,
since it would then fire after the dialog has been closed in the DOM and
visually. So the only reasonable fix for this would be to stop scheduling the
`close` event for dialogs. That's risky from a compat standpoint, so for now,
we test the strange behavior.)
4 changes: 2 additions & 2 deletions close-watcher/user-activation/n-activate-preventDefault.html
Expand Up @@ -22,10 +22,10 @@
await sendCloseRequest();
await waitForPotentialCloseEvent();

assert_array_equals(events, ["cancel"]);
assert_array_equals(events, ["cancel[cancelable=true]"]);

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["cancel", "close"]);
assert_array_equals(events, ["cancel[cancelable=true]", "cancel[cancelable=false]", "close"]);
}, "Create a close watcher without user activation that preventDefault()s cancel; send user activation");
</script>
2 changes: 1 addition & 1 deletion close-watcher/user-activation/n-activate.html
Expand Up @@ -22,6 +22,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["cancel", "close"]);
assert_array_equals(events, ["cancel[cancelable=true]", "close"]);
}, "Create a close watcher without user activation; send user activation");
</script>
4 changes: 2 additions & 2 deletions close-watcher/user-activation/n-closerequest-n.html
Expand Up @@ -19,12 +19,12 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher1 close"]);
assert_array_equals(events, ["watcher1 cancel[cancelable=false]", "watcher1 close"]);

createRecordingCloseWatcher(t, events, "watcher2", type);

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher1 close", "watcher2 close"]);
assert_array_equals(events, ["watcher1 cancel[cancelable=false]", "watcher1 close", "watcher2 cancel[cancelable=false]", "watcher2 close"]);
}, "Create a close watcher without user activation; send a close request; create a close watcher without user activation");
</script>
2 changes: 1 addition & 1 deletion close-watcher/user-activation/n-destroy-n.html
Expand Up @@ -26,6 +26,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher2 close"]);
assert_array_equals(events, ["watcher2 cancel[cancelable=false]", "watcher2 close"]);
}, "Create a close watcher without user activation; destroy the close watcher; create a close watcher without user activation");
</script>
2 changes: 1 addition & 1 deletion close-watcher/user-activation/n.html
Expand Up @@ -20,6 +20,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["close"]);
assert_array_equals(events, ["cancel[cancelable=false]", "close"]);
}, "Create a close watcher without user activation");
</script>
@@ -1,6 +1,4 @@
<!doctype html>
<meta name=variant content="?dialog">
<meta name=variant content="?CloseWatcher">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
Expand All @@ -11,7 +9,7 @@

<body>
<script>
const type = location.search.substring(1);
const type = "CloseWatcher";

promise_test(async t => {
const events = [];
Expand All @@ -21,6 +19,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher2 close", "watcher1 close"]);
assert_array_equals(events, ["watcher2 cancel[cancelable=false]", "watcher2 close", "watcher1 cancel[cancelable=false]", "watcher1 close"]);
}, "Create two close watchers without user activation");
</script>
6 changes: 1 addition & 5 deletions close-watcher/user-activation/nn-activate-CloseWatcher.html
Expand Up @@ -7,10 +7,6 @@
<script src="/common/top-layer.js"></script>
<script src="../resources/helpers.js"></script>

<!--
See note in sibling -dialog.html file.
-->

<body>
<script>
const type = "CloseWatcher";
Expand All @@ -25,6 +21,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher2 cancel", "watcher2 close", "watcher1 cancel", "watcher1 close"]);
assert_array_equals(events, ["watcher2 cancel[cancelable=true]", "watcher2 close", "watcher1 cancel[cancelable=true]", "watcher1 close"]);
}, "Create two CloseWatchers without user activation; send user activation");
</script>
16 changes: 1 addition & 15 deletions close-watcher/user-activation/nn-activate-dialog.html
Expand Up @@ -7,20 +7,6 @@
<script src="/common/top-layer.js"></script>
<script src="../resources/helpers.js"></script>

<!--
This test has different expectations for dialogs vs. CloseWatchers because
dialogs queue a task to fire their close event, and do not do so for their
cancel event. Thus, when you have two dialogs grouped together, you get the
somewhat-strange behavior of both cancels firing first, then both closes.
Whereas CloseWatchers do not have this issue; both fire synchronously.
Note that scheduling the cancel event for dialogs is not really possible since
it would then fire after the dialog has been closed in the DOM and visually.
So the only reasonable fix for this would be to stop scheduling the close
event for dialogs. That's risky from a compat standpoint, so for now, test the
strange behavior.
-->

<body>
<script>
const type = "dialog";
Expand All @@ -35,6 +21,6 @@

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher2 cancel", "watcher1 cancel", "watcher2 close", "watcher1 close"]);
assert_array_equals(events, ["watcher2 cancel[cancelable=true]", "watcher1 cancel[cancelable=true]", "watcher2 close", "watcher1 close"]);
}, "Create two dialogs without user activation; send user activation");
</script>
24 changes: 24 additions & 0 deletions close-watcher/user-activation/nn-dialog.html
@@ -0,0 +1,24 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/common/top-layer.js"></script>
<script src="../resources/helpers.js"></script>

<body>
<script>
const type = "dialog";

promise_test(async t => {
const events = [];

createRecordingCloseWatcher(t, events, "watcher1", type);
createRecordingCloseWatcher(t, events, "watcher2", type);

await sendCloseRequest();
await waitForPotentialCloseEvent();
assert_array_equals(events, ["watcher2 cancel[cancelable=false]", "watcher1 cancel[cancelable=false]", "watcher2 close", "watcher1 close"]);
}, "Create two close watchers without user activation");
</script>
Expand Up @@ -30,6 +30,6 @@

assert_false(popover.matches(':popover-open'), 'The popover should be closed.');
assert_false(dialog.hasAttribute('open'), 'The dialog should be closed.');
assert_array_equals(events, ['CloseWatcher close', 'dialog close']);
assert_array_equals(events, ['dialog cancel[cancelable=false]', 'CloseWatcher cancel[cancelable=false]', 'CloseWatcher close', 'dialog close']);
}, 'Create a CloseWatcher without user activation; create a dialog without user activation; create a popover without user activation');
</script>

0 comments on commit 69f5a6f

Please sign in to comment.