summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/close-watcher
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:27 +0000
commit40a355a42d4a9444dc753c04c6608dade2f06a23 (patch)
tree871fc667d2de662f171103ce5ec067014ef85e61 /testing/web-platform/tests/close-watcher
parentAdding upstream version 124.0.1. (diff)
downloadfirefox-adbda400be353e676059e335c3c0aaf99e719475.tar.xz
firefox-adbda400be353e676059e335c3c0aaf99e719475.zip
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/close-watcher')
-rw-r--r--testing/web-platform/tests/close-watcher/closewatcher-dialog-popover.html98
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key.html77
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/README.md4
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/keydown.html21
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/keypress.html21
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/keyup.html21
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/not-user-activation.html19
-rw-r--r--testing/web-platform/tests/close-watcher/esc-key/synthetic-keyboard-event.html28
-rw-r--r--testing/web-platform/tests/close-watcher/resources/helpers.js14
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation-CloseWatcher.html75
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation-multiple-plus-free.html32
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation-shared.html201
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/n-activate-preventDefault.html31
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/n-activate.html27
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/n-closerequest-n.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/n-destroy-n.html31
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/n.html25
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nn-activate-CloseWatcher.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nn-activate-dialog.html40
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nn.html26
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nnn-CloseWatcher-dialog-popover.html35
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nnn-popovers.html37
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nnn.html27
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/ny-activate-preventDefault.html37
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/ny.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nyn-popovers.html (renamed from testing/web-platform/tests/close-watcher/popover-closewatcher-multiple-plus-free.html)4
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nyn.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nynn-destroy.html33
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nynn.html31
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nyyn.html36
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/nyyyn.html40
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/y.html25
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yn-activate.html32
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yn.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/ynn.html31
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yy.html30
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yyn.html35
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yyy-CloseWatcher-dialog-popover.html46
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yyy-activate-CloseWatcher-dialog-popover.html49
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yyy-popovers.html (renamed from testing/web-platform/tests/close-watcher/popover-closewatcher.html)18
-rw-r--r--testing/web-platform/tests/close-watcher/user-activation/yyy.html35
41 files changed, 1021 insertions, 501 deletions
diff --git a/testing/web-platform/tests/close-watcher/closewatcher-dialog-popover.html b/testing/web-platform/tests/close-watcher/closewatcher-dialog-popover.html
deleted file mode 100644
index 50d5cb7a4c..0000000000
--- a/testing/web-platform/tests/close-watcher/closewatcher-dialog-popover.html
+++ /dev/null
@@ -1,98 +0,0 @@
-<!DOCTYPE html>
-<link rel=author href="mailto:jarhar@chromium.org">
-<link rel=help href="https://github.com/whatwg/html/pull/9462">
-<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>
-
-<button id=b0>button</button>
-
-<dialog id=dialog>
- <button id=b1>button</button>
- <div id=popover popover=auto>popover</div>
-</dialog>
-
-<script>
-const waitForPotentialCloseEvent = () => {
- // CloseWatchers fire close events synchronously, but dialog elements wait
- // for a rAF before firing them.
- return new Promise(resolve => requestAnimationFrame(resolve));
-};
-
-promise_test(async t => {
- const events = [];
- const closeWatcher = createRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
- const dialog = createRecordingCloseWatcher(t, events, 'dialog', 'dialog');
- const popover = createRecordingCloseWatcher(t, events, 'popover', 'popover');
- assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
- assert_true(popover.matches(':popover-open'), 'The popover should be open.');
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
-
- 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']);
-}, 'Opening a CloseWatcher, modal dialog, and popover without user activation causes them all to be closed with one close request.');
-
-promise_test(async t => {
- const events = [];
- const closeWatcher = await createBlessedRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
- const dialog = await createBlessedRecordingCloseWatcher(t, events, 'dialog', 'dialog');
- const popover = await createBlessedRecordingCloseWatcher(t, events, 'popover', 'popover', dialog);
- assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
- assert_true(popover.matches(':popover-open'), 'The popover should be open.');
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'First close request: The popover should be closed.');
- assert_true(dialog.hasAttribute('open'), 'First close request: The dialog should be open.');
- assert_array_equals(events, []);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'Second close request: The popover should be closed.');
- assert_false(dialog.hasAttribute('open'), 'Second close request: The dialog should be closed.');
- assert_array_equals(events, ['dialog close']);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'Third close request: The popover should be closed.');
- assert_false(dialog.hasAttribute('open'), 'Third close request: The dialog should be closed.');
- assert_array_equals(events, ['dialog close', 'CloseWatcher close']);
-}, 'Opening a CloseWatcher, modal dialog, and popover with user activation for each should close one at a time with close requests.');
-
-promise_test(async t => {
- const events = [];
- const closeWatcher = await createBlessedRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
- const dialog = await createBlessedRecordingCloseWatcher(t, events, 'dialog', 'dialog');
- const popover = await createBlessedRecordingCloseWatcher(t, events, 'popover', 'popover', dialog);
- assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
- assert_true(popover.matches(':popover-open'), 'The popover should be open.');
-
- await blessTopLayer(popover);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'First close request: The popover should be closed.');
- assert_true(dialog.hasAttribute('open'), 'First close request: The dialog should be open.');
- assert_array_equals(events, []);
-
- await blessTopLayer(dialog);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'Second close request: The popover should be closed.');
- assert_false(dialog.hasAttribute('open'), 'Second close request: The dialog should be closed.');
- assert_array_equals(events, ['dialog cancel', 'dialog close']);
-
- await test_driver.bless();
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_false(popover.matches(':popover-open'), 'Third close request: The popover should be closed.');
- assert_false(dialog.hasAttribute('open'), 'Third close request: The dialog should be closed.');
- assert_array_equals(events, ['dialog cancel', 'dialog close', 'CloseWatcher cancel', 'CloseWatcher close']);
-}, 'Opening a CloseWatcher, modal dialog, and popover with user activation for each and sending close requests with user activation should close one at a time and have cancel events.');
-</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key.html b/testing/web-platform/tests/close-watcher/esc-key.html
deleted file mode 100644
index 16fcce6917..0000000000
--- a/testing/web-platform/tests/close-watcher/esc-key.html
+++ /dev/null
@@ -1,77 +0,0 @@
-<!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="resources/helpers.js"></script>
-
-<!--
- Tests in this file are around the interaction of the Esc key specifically, not
- the general concept of close requests. Ideally, all other tests would work
- as-is if you changed the implementation of sendCloseRequest(). These tests
- assume that Esc is the close request for the platform being tested.
--->
-
-<body>
-<script>
-promise_test(async t => {
- let events = [];
- let watcher = createRecordingCloseWatcher(t, events);
-
- 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");
-
-promise_test(async t => {
- let events = [];
- let watcher = createRecordingCloseWatcher(t, events);
-
- window.onkeydown = e => e.preventDefault();
-
- await sendEscKey();
-
- assert_array_equals(events, []);
-}, "A keydown listener can prevent the Esc keypress from being interpreted as a close request");
-
-promise_test(async t => {
- let events = [];
- let watcher = createRecordingCloseWatcher(t, events);
-
- window.onkeyup = e => e.preventDefault();
-
- await sendEscKey();
-
- assert_array_equals(events, []);
-}, "A keyup listener can prevent the Esc keypress from being interpreted as a close request");
-
-promise_test(async t => {
- let events = [];
- let watcher = createRecordingCloseWatcher(t, events);
-
- window.onkeypress = e => e.preventDefault();
-
- await sendEscKey();
-
- assert_array_equals(events, []);
-}, "A keypress listener can prevent the Esc keypress from being interpreted as a close request");
-
-test(t => {
- let events = [];
- let watcher = createRecordingCloseWatcher(t, events);
-
- let keydown = new KeyboardEvent('keydown', {'key': 'Escape', 'keyCode': 27});
- window.dispatchEvent(keydown);
- let keyup = new KeyboardEvent('keyup', {'key': 'Escape', 'keyCode': 27});
- window.dispatchEvent(keyup);
-
- assert_array_equals(events, []);
-
- let keyup2 = document.createEvent("Event");
- keyup2.initEvent("keyup", true);
- window.dispatchEvent(keyup2);
-
- assert_array_equals(events, []);
-}, "close via synthesized Esc key must not work");
-</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key/README.md b/testing/web-platform/tests/close-watcher/esc-key/README.md
new file mode 100644
index 0000000000..817edc09ab
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/README.md
@@ -0,0 +1,4 @@
+Tests in this directory are around the interaction of the Esc key specifically,
+not the general concept of close requests. Ideally, all other tests would work
+as-is if you changed the implementation of `sendCloseRequest()`. These tests
+assume that Esc is the close request for the platform being tested.
diff --git a/testing/web-platform/tests/close-watcher/esc-key/keydown.html b/testing/web-platform/tests/close-watcher/esc-key/keydown.html
new file mode 100644
index 0000000000..cb0ddf0638
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/keydown.html
@@ -0,0 +1,21 @@
+<!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="../resources/helpers.js"></script>
+
+<body>
+<script>
+promise_test(async t => {
+ let events = [];
+ let watcher = createRecordingCloseWatcher(t, events);
+
+ window.onkeydown = e => e.preventDefault();
+
+ await sendEscKey();
+
+ assert_array_equals(events, []);
+}, "A keydown listener can prevent the Esc keypress from being interpreted as a close request");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key/keypress.html b/testing/web-platform/tests/close-watcher/esc-key/keypress.html
new file mode 100644
index 0000000000..8dd58b064d
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/keypress.html
@@ -0,0 +1,21 @@
+<!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="../resources/helpers.js"></script>
+
+<body>
+<script>
+promise_test(async t => {
+ let events = [];
+ let watcher = createRecordingCloseWatcher(t, events);
+
+ window.onkeypress = e => e.preventDefault();
+
+ await sendEscKey();
+
+ assert_array_equals(events, ["close"]);
+}, "A keypress listener can NOT prevent the Esc keypress from being interpreted as a close request");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key/keyup.html b/testing/web-platform/tests/close-watcher/esc-key/keyup.html
new file mode 100644
index 0000000000..341012d6bc
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/keyup.html
@@ -0,0 +1,21 @@
+<!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="../resources/helpers.js"></script>
+
+<body>
+<script>
+promise_test(async t => {
+ let events = [];
+ let watcher = createRecordingCloseWatcher(t, events);
+
+ window.onkeyup = e => e.preventDefault();
+
+ await sendEscKey();
+
+ assert_array_equals(events, ["close"]);
+}, "A keyup listener can NOT prevent the Esc keypress from being interpreted as a close request");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key/not-user-activation.html b/testing/web-platform/tests/close-watcher/esc-key/not-user-activation.html
new file mode 100644
index 0000000000..ac29f84f06
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/not-user-activation.html
@@ -0,0 +1,19 @@
+<!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="../resources/helpers.js"></script>
+
+<body>
+<script>
+promise_test(async t => {
+ let events = [];
+ let watcher = createRecordingCloseWatcher(t, events);
+
+ 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");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/esc-key/synthetic-keyboard-event.html b/testing/web-platform/tests/close-watcher/esc-key/synthetic-keyboard-event.html
new file mode 100644
index 0000000000..37b5507ac4
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/esc-key/synthetic-keyboard-event.html
@@ -0,0 +1,28 @@
+<!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="../resources/helpers.js"></script>
+
+<body>
+<script>
+test(t => {
+ let events = [];
+ let watcher = createRecordingCloseWatcher(t, events);
+
+ let keydown = new KeyboardEvent("keydown", {key: "Escape", keyCode: 27});
+ window.dispatchEvent(keydown);
+ let keyup = new KeyboardEvent("keyup", {key: "Escape", keyCode: 27});
+ window.dispatchEvent(keyup);
+
+ assert_array_equals(events, []);
+
+ let keyup2 = document.createEvent("Event");
+ keyup2.initEvent("keyup", true);
+ window.dispatchEvent(keyup2);
+
+ assert_array_equals(events, []);
+}, "close via synthesized Esc key must not work");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/resources/helpers.js b/testing/web-platform/tests/close-watcher/resources/helpers.js
index 97a62309cd..dd9e191c4d 100644
--- a/testing/web-platform/tests/close-watcher/resources/helpers.js
+++ b/testing/web-platform/tests/close-watcher/resources/helpers.js
@@ -38,6 +38,14 @@ window.createBlessedRecordingCloseWatcher = async (t, events, name, type, parent
return createRecordingCloseWatcher(t, events, name, type, parentWatcher);
};
+window.destroyCloseWatcher = (watcher) => {
+ if (watcher instanceof HTMLElement) {
+ watcher.remove();
+ } else {
+ watcher.destroy();
+ }
+};
+
window.sendEscKey = () => {
// Esc is \uE00C, *not* \uu001B; see https://w3c.github.io/webdriver/#keyboard-actions.
//
@@ -59,3 +67,9 @@ window.maybeTopLayerBless = (watcher) => {
}
return test_driver.bless();
};
+
+window.waitForPotentialCloseEvent = () => {
+ // CloseWatchers fire close events synchronously, but dialog elements wait
+ // for a rAF before firing them.
+ return new Promise(requestAnimationFrame);
+};
diff --git a/testing/web-platform/tests/close-watcher/user-activation-CloseWatcher.html b/testing/web-platform/tests/close-watcher/user-activation-CloseWatcher.html
deleted file mode 100644
index 70435993f5..0000000000
--- a/testing/web-platform/tests/close-watcher/user-activation-CloseWatcher.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!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="resources/helpers.js"></script>
-
-<!-- The dialog element does not support requestClose(), so these tests
- are not shared between dialog elements and CloseWatchers. -->
-
-<body>
-<script>
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
-
- await test_driver.bless("call requestClose()", () => freeWatcher.requestClose());
-
- assert_array_equals(events, ["freeWatcher cancel", "freeWatcher close"]);
-}, "CloseWatchers created without user activation, but requestClose()d via user activation, fires cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
- freeWatcher.addEventListener("cancel", e => e.preventDefault());
-
- await test_driver.bless("call requestClose()", () => freeWatcher.requestClose());
-
- assert_array_equals(events, ["freeWatcher cancel"]);
-}, "CloseWatchers created without user activation, but requestClose()d via user activation, fires cancel, which can be preventDefault()ed");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
- const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher");
-
- await test_driver.bless("call activationWatcher.requestClose()", () => activationWatcher.requestClose());
- assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close"]);
-
- await test_driver.bless("call freeWatcher.requestClose()", () => freeWatcher.requestClose());
- assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close", "freeWatcher cancel", "freeWatcher close"]);
-}, "Creating a CloseWatcher from user activation, and requestClose()ing CloseWatchers with user activation, fires cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
- const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1");
- activationWatcher1.addEventListener("cancel", e => e.preventDefault());
-
- await test_driver.bless("call activationWatcher1.requestClose()", () => activationWatcher1.requestClose());
- assert_array_equals(events, ["activationWatcher1 cancel"]);
-
- // This time we go straight to close, without a second cancel.
- activationWatcher1.requestClose();
- assert_array_equals(events, ["activationWatcher1 cancel", "activationWatcher1 close"]);
-
- freeWatcher.requestClose();
- assert_array_equals(events, ["activationWatcher1 cancel", "activationWatcher1 close", "freeWatcher close"]);
-}, "3 user activations let you have 2 close watchers with 1 cancel event, even if the first cancel event is prevented");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1");
-
- freeWatcher1.requestClose();
- assert_array_equals(events, ["freeWatcher1 close"]);
-
- const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2");
-
- await sendCloseRequest();
- assert_array_equals(events, ["freeWatcher1 close", "freeWatcher2 close"]);
-}, "requestClose()ing the free CloseWatcher allows a new free one to be created without user activation, and it receives the close request");
-
-</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation-multiple-plus-free.html b/testing/web-platform/tests/close-watcher/user-activation-multiple-plus-free.html
deleted file mode 100644
index a94b47904a..0000000000
--- a/testing/web-platform/tests/close-watcher/user-activation-multiple-plus-free.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!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="resources/helpers.js"></script>
-
-<body>
-<script>
-
-// This test needs to be separate from user-activation.html since, unlike those,
-// it relies on there not being any lingering user activation from previous
-// tests hanging around. That is, we need to be sure freeWatcher is created with
-// no user activation, to ensure that activationWatcher1 and activationWatcher2
-// get grouped as expected.
-promise_test(async t => {
- const events = [];
- createRecordingCloseWatcher(t, events, "freeWatcher");
-
- await test_driver.bless("create two more CloseWatchers", () => {
- createRecordingCloseWatcher(t, events, "activationWatcher1");
- createRecordingCloseWatcher(t, events, "activationWatcher2");
- });
-
- await sendCloseRequest();
- assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close"]);
-
- await sendCloseRequest();
- assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close", "freeWatcher close"]);
-}, "Multiple CloseWatchers created from a single user activation close together, but original free CloseWatcher closes separately");
-</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation-shared.html b/testing/web-platform/tests/close-watcher/user-activation-shared.html
deleted file mode 100644
index 77e748532a..0000000000
--- a/testing/web-platform/tests/close-watcher/user-activation-shared.html
+++ /dev/null
@@ -1,201 +0,0 @@
-<!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>
-<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 = location.search.substring(1);
-const waitForPotentialCloseEvent = () => {
- // CloseWatchers fire close events synchronously, but dialog elements wait
- // for a rAF before firing them.
- return new Promise(resolve => requestAnimationFrame(resolve));
-};
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- freeWatcher.addEventListener("cancel", e => e.preventDefault());
-
- await maybeTopLayerBless(freeWatcher);
- freeWatcher.close();
- await waitForPotentialCloseEvent();
-
- assert_array_equals(events, ["freeWatcher close"]);
-}, "Close watchers created without user activation, but close()d via user activation, do not fire cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
-
- await maybeTopLayerBless(freeWatcher);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
-
- assert_array_equals(events, ["freeWatcher cancel", "freeWatcher close"]);
-}, "Close watchers created without user activation, but closed via a close request after user activation, fires cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- freeWatcher.addEventListener("cancel", e => e.preventDefault());
-
- await maybeTopLayerBless(freeWatcher);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
-
- assert_array_equals(events, ["freeWatcher cancel"]);
-}, "Close watchers created without user activation, but closed via a close request after user activation, fires cancel, which can be preventDefault()ed");
-
-promise_test(async t => {
- const events = [];
- createRecordingCloseWatcher(t, events, "freeWatcher", type);
- createRecordingCloseWatcher(t, events, "watcher1", type);
- createRecordingCloseWatcher(t, events, "watcher2", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["watcher2 close", "watcher1 close", "freeWatcher close"]);
-}, "Multiple close watchers created without user activation close together (with no cancel)");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- await createBlessedRecordingCloseWatcher(t, events, "activationWatcher", type, freeWatcher);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher close", "freeWatcher close"]);
-}, "Creating a close watcher from user activation keeps it separate from the free close watcher, but they don't fire cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher", type, freeWatcher);
-
- await maybeTopLayerBless(activationWatcher);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close"]);
-
- await maybeTopLayerBless(freeWatcher);
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close", "freeWatcher cancel", "freeWatcher close"]);
-}, "Creating a close watcher from user activation, and closing close watchers with a close request after user activation, fires cancel");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1", type, freeWatcher);
- await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2", type, activationWatcher1);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher2 close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close"]);
-}, "Multiple close watchers created with user activation close in reverse order");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
- const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1", type, freeWatcher);
- const activationWatcher2 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2", type, activationWatcher1);
- await createBlessedRecordingCloseWatcher(t, events, "activationWatcher3", type, activationWatcher2);
- createRecordingCloseWatcher(t, events, "watcher4", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["watcher4 close", "activationWatcher3 close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close", "activationWatcher1 close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close", "activationWatcher1 close", "freeWatcher close"]);
-}, "3 user activations let you have 3 + 1 = 4 ungrouped close watchers/0 cancel events");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher1 close"]);
-
- const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher1 close", "freeWatcher2 close"]);
-}, "closing the free close watcher via a close request allows a new free one to be created without user activation, and it receives a second close request");
-
-promise_test(async t => {
- const events = [];
- const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher", type);
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher close", "activationWatcher close"]);
-}, "The second watcher can be the free watcher, if the first is created with user activation");
-
-promise_test(async t => {
- const events = [];
- const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1", type);
- const activationWatcher2 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2", type, activationWatcher1);
- const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher", type);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher close", "activationWatcher2 close"]);
-
- await sendCloseRequest();
- await waitForPotentialCloseEvent();
- assert_array_equals(events, ["freeWatcher close", "activationWatcher2 close", "activationWatcher1 close"]);
-}, "The third watcher can be the free watcher, if the first two are created with user activation");
-
-promise_test(async t => {
- const events = [];
- const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1");
-
- if (freeWatcher1 instanceof HTMLDialogElement) {
- freeWatcher1.close();
- } else {
- freeWatcher1.destroy();
- }
- assert_array_equals(events, []);
-
- const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2");
-
- await sendCloseRequest();
- assert_array_equals(events, ["freeWatcher2 close"]);
-}, "destroy()ing the free close watcher allows a new free one to be created without user activation, and it receives the close request");
-</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/n-activate-preventDefault.html b/testing/web-platform/tests/close-watcher/user-activation/n-activate-preventDefault.html
new file mode 100644
index 0000000000..531ef42599
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/n-activate-preventDefault.html
@@ -0,0 +1,31 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+ const watcher = createRecordingCloseWatcher(t, events, undefined, type);
+ watcher.addEventListener("cancel", e => e.preventDefault());
+
+ await maybeTopLayerBless(watcher);
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+
+ assert_array_equals(events, ["cancel"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["cancel", "close"]);
+}, "Create a close watcher without user activation that preventDefault()s cancel; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/n-activate.html b/testing/web-platform/tests/close-watcher/user-activation/n-activate.html
new file mode 100644
index 0000000000..babcf54c3c
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/n-activate.html
@@ -0,0 +1,27 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher = createRecordingCloseWatcher(t, events, undefined, type);
+
+ await maybeTopLayerBless(watcher);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["cancel", "close"]);
+}, "Create a close watcher without user activation; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/n-closerequest-n.html b/testing/web-platform/tests/close-watcher/user-activation/n-closerequest-n.html
new file mode 100644
index 0000000000..2424af7820
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/n-closerequest-n.html
@@ -0,0 +1,30 @@
+<!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>
+<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 = location.search.substring(1);
+promise_test(async t => {
+ const events = [];
+
+ createRecordingCloseWatcher(t, events, "watcher1", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher1 close"]);
+
+ createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher1 close", "watcher2 close"]);
+}, "Create a close watcher without user activation; send a close request; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/n-destroy-n.html b/testing/web-platform/tests/close-watcher/user-activation/n-destroy-n.html
new file mode 100644
index 0000000000..c26f87dd6f
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/n-destroy-n.html
@@ -0,0 +1,31 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1", type);
+
+ destroyCloseWatcher(watcher1);
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, []);
+
+ createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 close"]);
+}, "Create a close watcher without user activation; destroy the close watcher; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/n.html b/testing/web-platform/tests/close-watcher/user-activation/n.html
new file mode 100644
index 0000000000..fe04e0dc1b
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/n.html
@@ -0,0 +1,25 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ createRecordingCloseWatcher(t, events, undefined, type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["close"]);
+}, "Create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nn-activate-CloseWatcher.html b/testing/web-platform/tests/close-watcher/user-activation/nn-activate-CloseWatcher.html
new file mode 100644
index 0000000000..8045f30b48
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nn-activate-CloseWatcher.html
@@ -0,0 +1,30 @@
+<!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>
+
+<!--
+ See note in sibling -dialog.html file.
+-->
+
+<body>
+<script>
+const type = "CloseWatcher";
+
+promise_test(async t => {
+ const events = [];
+
+ createRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await maybeTopLayerBless(watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close", "watcher1 cancel", "watcher1 close"]);
+}, "Create two CloseWatchers without user activation; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nn-activate-dialog.html b/testing/web-platform/tests/close-watcher/user-activation/nn-activate-dialog.html
new file mode 100644
index 0000000000..5cc866044c
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nn-activate-dialog.html
@@ -0,0 +1,40 @@
+<!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>
+
+<!--
+ 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";
+
+promise_test(async t => {
+ const events = [];
+
+ createRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await maybeTopLayerBless(watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher1 cancel", "watcher2 close", "watcher1 close"]);
+}, "Create two dialogs without user activation; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nn.html b/testing/web-platform/tests/close-watcher/user-activation/nn.html
new file mode 100644
index 0000000000..beb63f1b4f
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nn.html
@@ -0,0 +1,26 @@
+<!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>
+<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 = location.search.substring(1);
+
+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 close", "watcher1 close"]);
+}, "Create two close watchers without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nnn-CloseWatcher-dialog-popover.html b/testing/web-platform/tests/close-watcher/user-activation/nnn-CloseWatcher-dialog-popover.html
new file mode 100644
index 0000000000..f8b9061d01
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nnn-CloseWatcher-dialog-popover.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9462">
+<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>
+
+<button id=b0>button</button>
+
+<dialog id=dialog>
+ <button id=b1>button</button>
+ <div id=popover popover=auto>popover</div>
+</dialog>
+
+<script>
+promise_test(async t => {
+ const events = [];
+ const closeWatcher = createRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
+ const dialog = createRecordingCloseWatcher(t, events, 'dialog', 'dialog');
+ const popover = createRecordingCloseWatcher(t, events, 'popover', 'popover');
+ assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
+ assert_true(popover.matches(':popover-open'), 'The popover should be open.');
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+
+ 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']);
+}, 'Create a CloseWatcher without user activation; create a dialog without user activation; create a popover without user activation');
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nnn-popovers.html b/testing/web-platform/tests/close-watcher/user-activation/nnn-popovers.html
new file mode 100644
index 0000000000..ed5d15598f
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nnn-popovers.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9462">
+<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="../resources/helpers.js"></script>
+
+<button id=b0>b0</button>
+
+<div id=p1 popover=auto>
+ <button id=b1>b1</button>
+
+ <div id=p2 popover=auto>
+ <button id=b2>b2</button>
+
+ <div id=p3 popover=auto>p3</div>
+ </div>
+</div>
+
+<script>
+promise_test(async () => {
+ p1.showPopover();
+ p2.showPopover();
+ p3.showPopover();
+ assert_true(p1.matches(':popover-open'), 'p1 should be open.');
+ assert_true(p2.matches(':popover-open'), 'p2 should be open.');
+ assert_true(p3.matches(':popover-open'), 'p3 should be open.');
+
+ await sendCloseRequest();
+ assert_false(p1.matches(':popover-open'), 'p1 should be closed.');
+ assert_false(p2.matches(':popover-open'), 'p2 should be closed.');
+ assert_false(p3.matches(':popover-open'), 'p3 should be closed.');
+}, 'Create three popovers without user activation');
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nnn.html b/testing/web-platform/tests/close-watcher/user-activation/nnn.html
new file mode 100644
index 0000000000..9b604e91db
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nnn.html
@@ -0,0 +1,27 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ createRecordingCloseWatcher(t, events, "watcher1", type);
+ createRecordingCloseWatcher(t, events, "watcher2", type);
+ createRecordingCloseWatcher(t, events, "watcher3", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create three close watchers without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/ny-activate-preventDefault.html b/testing/web-platform/tests/close-watcher/user-activation/ny-activate-preventDefault.html
new file mode 100644
index 0000000000..5ffb64b113
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/ny-activate-preventDefault.html
@@ -0,0 +1,37 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ watcher2.addEventListener("cancel", e => e.preventDefault());
+
+ await maybeTopLayerBless(watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create a close watcher with user activation that preventDefault()s cancel; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/ny.html b/testing/web-platform/tests/close-watcher/user-activation/ny.html
new file mode 100644
index 0000000000..226912233e
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/ny.html
@@ -0,0 +1,30 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1", type);
+ await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create a close watcher with user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/popover-closewatcher-multiple-plus-free.html b/testing/web-platform/tests/close-watcher/user-activation/nyn-popovers.html
index 4913b1454e..b6df610ae0 100644
--- a/testing/web-platform/tests/close-watcher/popover-closewatcher-multiple-plus-free.html
+++ b/testing/web-platform/tests/close-watcher/user-activation/nyn-popovers.html
@@ -6,7 +6,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
-<script src="resources/helpers.js"></script>
+<script src="../resources/helpers.js"></script>
<button id=b0>b0</button>
@@ -39,5 +39,5 @@ promise_test(async () => {
assert_false(p1.matches(':popover-open'), 'second escape: p1 should be closed.');
assert_false(p2.matches(':popover-open'), 'second escape: p2 should be closed.');
assert_false(p3.matches(':popover-open'), 'second escape: p3 should be closed.');
-}, 'Multiple popovers opened from a single user activation close together, but original popover closes separately.');
+}, 'Create a popover without user activation; create a popover with user activation; create a popover without user activation');
</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nyn.html b/testing/web-platform/tests/close-watcher/user-activation/nyn.html
new file mode 100644
index 0000000000..ec5153c767
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nyn.html
@@ -0,0 +1,30 @@
+<!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>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/helpers.js"></script>
+
+<body>
+<script>
+const type = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1");
+ await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ createRecordingCloseWatcher(t, events, "watcher3");
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create a close watcher with user activation; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nynn-destroy.html b/testing/web-platform/tests/close-watcher/user-activation/nynn-destroy.html
new file mode 100644
index 0000000000..8519c8a2a9
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nynn-destroy.html
@@ -0,0 +1,33 @@
+<!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>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/helpers.js"></script>
+
+<body>
+<script>
+const type = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1");
+ const watcher2 = await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ createRecordingCloseWatcher(t, events, "watcher3");
+ createRecordingCloseWatcher(t, events, "watcher4");
+
+ destroyCloseWatcher(watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create a close watcher with user activation; create two close watchers without user activation; remove the second close watcher");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nynn.html b/testing/web-platform/tests/close-watcher/user-activation/nynn.html
new file mode 100644
index 0000000000..f6e74a0ba1
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nynn.html
@@ -0,0 +1,31 @@
+<!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>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../resources/helpers.js"></script>
+
+<body>
+<script>
+const type = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1");
+ await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ createRecordingCloseWatcher(t, events, "watcher3");
+ createRecordingCloseWatcher(t, events, "watcher4");
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create a close watcher with user activation; create two close watchers without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nyyn.html b/testing/web-platform/tests/close-watcher/user-activation/nyyn.html
new file mode 100644
index 0000000000..f3987c1a21
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nyyn.html
@@ -0,0 +1,36 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ const watcher3 = await createBlessedRecordingCloseWatcher(t, events, "watcher3", type, watcher2);
+ createRecordingCloseWatcher(t, events, "watcher4", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher4 close", "watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create two close watchers with user activation; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/nyyyn.html b/testing/web-platform/tests/close-watcher/user-activation/nyyyn.html
new file mode 100644
index 0000000000..6cb8f3a445
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/nyyyn.html
@@ -0,0 +1,40 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+ const watcher1 = createRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ const watcher3 = await createBlessedRecordingCloseWatcher(t, events, "watcher3", type, watcher2);
+ await createBlessedRecordingCloseWatcher(t, events, "watcher4", type, watcher3);
+ createRecordingCloseWatcher(t, events, "watcher5", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher5 close", "watcher4 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher5 close", "watcher4 close", "watcher3 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher5 close", "watcher4 close", "watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher5 close", "watcher4 close", "watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher without user activation; create three close watchers with user activation; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/y.html b/testing/web-platform/tests/close-watcher/user-activation/y.html
new file mode 100644
index 0000000000..ee58a92293
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/y.html
@@ -0,0 +1,25 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ await createBlessedRecordingCloseWatcher(t, events, undefined, type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["cancel", "close"]);
+}, "Create a close watcher with user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yn-activate.html b/testing/web-platform/tests/close-watcher/user-activation/yn-activate.html
new file mode 100644
index 0000000000..af7289aa28
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yn-activate.html
@@ -0,0 +1,32 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await maybeTopLayerBless(watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close", "watcher1 cancel", "watcher1 close"]);
+}, "Create a close watcher with user activation; create a close watcher without user activation; send user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yn.html b/testing/web-platform/tests/close-watcher/user-activation/yn.html
new file mode 100644
index 0000000000..8f7e90e2d8
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yn.html
@@ -0,0 +1,30 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ createRecordingCloseWatcher(t, events, "watcher2", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 close", "watcher1 close"]);
+}, "Create a close watcher with user activation; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/ynn.html b/testing/web-platform/tests/close-watcher/user-activation/ynn.html
new file mode 100644
index 0000000000..8cc7f5bfb6
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/ynn.html
@@ -0,0 +1,31 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ createRecordingCloseWatcher(t, events, "watcher2", type);
+ createRecordingCloseWatcher(t, events, "watcher3", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create a close watcher with user activation; create two close watchers without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yy.html b/testing/web-platform/tests/close-watcher/user-activation/yy.html
new file mode 100644
index 0000000000..0aa03cdd05
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yy.html
@@ -0,0 +1,30 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher2 cancel", "watcher2 close", "watcher1 cancel", "watcher1 close"]);
+}, "Create two close watchers with user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yyn.html b/testing/web-platform/tests/close-watcher/user-activation/yyn.html
new file mode 100644
index 0000000000..b87cf7a7e3
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yyn.html
@@ -0,0 +1,35 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ createRecordingCloseWatcher(t, events, "watcher3", type);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 close", "watcher2 close", "watcher1 close"]);
+}, "Create two close watchers with user activation; create a close watcher without user activation");
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yyy-CloseWatcher-dialog-popover.html b/testing/web-platform/tests/close-watcher/user-activation/yyy-CloseWatcher-dialog-popover.html
new file mode 100644
index 0000000000..f0a1cb06d1
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yyy-CloseWatcher-dialog-popover.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9462">
+<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>
+
+<button id=b0>button</button>
+
+<dialog id=dialog>
+ <button id=b1>button</button>
+ <div id=popover popover=auto>popover</div>
+</dialog>
+
+<script>
+promise_test(async t => {
+ const events = [];
+ const closeWatcher = await createBlessedRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
+ const dialog = await createBlessedRecordingCloseWatcher(t, events, 'dialog', 'dialog');
+ const popover = await createBlessedRecordingCloseWatcher(t, events, 'popover', 'popover', dialog);
+ assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
+ assert_true(popover.matches(':popover-open'), 'The popover should be open.');
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'First close request: The popover should be closed.');
+ assert_true(dialog.hasAttribute('open'), 'First close request: The dialog should be open.');
+ assert_array_equals(events, []);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'Second close request: The popover should be closed.');
+ assert_false(dialog.hasAttribute('open'), 'Second close request: The dialog should be closed.');
+ assert_array_equals(events, ['dialog cancel', 'dialog close']);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'Third close request: The popover should be closed.');
+ assert_false(dialog.hasAttribute('open'), 'Third close request: The dialog should be closed.');
+ assert_array_equals(events, ['dialog cancel', 'dialog close', 'CloseWatcher cancel', 'CloseWatcher close']);
+}, 'Create a CloseWatcher with user activation; create a dialog with user activation; create a popover with user activation');
+</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yyy-activate-CloseWatcher-dialog-popover.html b/testing/web-platform/tests/close-watcher/user-activation/yyy-activate-CloseWatcher-dialog-popover.html
new file mode 100644
index 0000000000..ed41d1bc32
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yyy-activate-CloseWatcher-dialog-popover.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9462">
+<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>
+
+<button id=b0>button</button>
+
+<dialog id=dialog>
+ <button id=b1>button</button>
+ <div id=popover popover=auto>popover</div>
+</dialog>
+
+<script>
+promise_test(async t => {
+ const events = [];
+ const closeWatcher = await createBlessedRecordingCloseWatcher(t, events, 'CloseWatcher', 'CloseWatcher');
+ const dialog = await createBlessedRecordingCloseWatcher(t, events, 'dialog', 'dialog');
+ const popover = await createBlessedRecordingCloseWatcher(t, events, 'popover', 'popover', dialog);
+ assert_true(dialog.hasAttribute('open'), 'The dialog should be open.');
+ assert_true(popover.matches(':popover-open'), 'The popover should be open.');
+
+ await blessTopLayer(popover);
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'First close request: The popover should be closed.');
+ assert_true(dialog.hasAttribute('open'), 'First close request: The dialog should be open.');
+ assert_array_equals(events, []);
+
+ await blessTopLayer(dialog);
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'Second close request: The popover should be closed.');
+ assert_false(dialog.hasAttribute('open'), 'Second close request: The dialog should be closed.');
+ assert_array_equals(events, ['dialog cancel', 'dialog close']);
+
+ await test_driver.bless();
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_false(popover.matches(':popover-open'), 'Third close request: The popover should be closed.');
+ assert_false(dialog.hasAttribute('open'), 'Third close request: The dialog should be closed.');
+ assert_array_equals(events, ['dialog cancel', 'dialog close', 'CloseWatcher cancel', 'CloseWatcher close']);
+}, 'Create a CloseWatcher with user activation; create a dialog with user activation; create a popover with user activation; sending user activation before each close request');
+</script>
diff --git a/testing/web-platform/tests/close-watcher/popover-closewatcher.html b/testing/web-platform/tests/close-watcher/user-activation/yyy-popovers.html
index b40ea2ec7c..6f1b739465 100644
--- a/testing/web-platform/tests/close-watcher/popover-closewatcher.html
+++ b/testing/web-platform/tests/close-watcher/user-activation/yyy-popovers.html
@@ -6,7 +6,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
-<script src="resources/helpers.js"></script>
+<script src="../resources/helpers.js"></script>
<button id=b0>b0</button>
@@ -22,20 +22,6 @@
<script>
promise_test(async () => {
- p1.showPopover();
- p2.showPopover();
- p3.showPopover();
- assert_true(p1.matches(':popover-open'), 'p1 should be open.');
- assert_true(p2.matches(':popover-open'), 'p2 should be open.');
- assert_true(p3.matches(':popover-open'), 'p3 should be open.');
-
- await sendCloseRequest();
- assert_false(p1.matches(':popover-open'), 'p1 should be closed.');
- assert_false(p2.matches(':popover-open'), 'p2 should be closed.');
- assert_false(p3.matches(':popover-open'), 'p3 should be closed.');
-}, 'Opening multiple popovers without user activation causes them all to be closed with one close request.');
-
-promise_test(async () => {
await test_driver.click(b0);
p1.showPopover();
await test_driver.click(b1);
@@ -60,5 +46,5 @@ promise_test(async () => {
assert_false(p1.matches(':popover-open'), 'third escape: p1 should be closed.');
assert_false(p2.matches(':popover-open'), 'third escape: p2 should be closed.');
assert_false(p3.matches(':popover-open'), 'third escape: p3 should be closed.');
-}, 'Opening multiple popovers with user activation should close one at a time with close requests.');
+}, 'Create three popovers with user activation');
</script>
diff --git a/testing/web-platform/tests/close-watcher/user-activation/yyy.html b/testing/web-platform/tests/close-watcher/user-activation/yyy.html
new file mode 100644
index 0000000000..f16767a86b
--- /dev/null
+++ b/testing/web-platform/tests/close-watcher/user-activation/yyy.html
@@ -0,0 +1,35 @@
+<!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>
+<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 = location.search.substring(1);
+
+promise_test(async t => {
+ const events = [];
+
+ const watcher1 = await createBlessedRecordingCloseWatcher(t, events, "watcher1", type);
+ const watcher2 = await createBlessedRecordingCloseWatcher(t, events, "watcher2", type, watcher1);
+ await createBlessedRecordingCloseWatcher(t, events, "watcher3", type, watcher2);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 cancel", "watcher3 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 cancel", "watcher3 close", "watcher2 cancel", "watcher2 close"]);
+
+ await sendCloseRequest();
+ await waitForPotentialCloseEvent();
+ assert_array_equals(events, ["watcher3 cancel", "watcher3 close", "watcher2 cancel", "watcher2 close", "watcher1 cancel", "watcher1 close"]);
+}, "Create three close watchers with user activation");
+</script>