summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/interactive-elements
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/interactive-elements')
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js36
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html10
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html13
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html12
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html99
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html15
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html52
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html47
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html160
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html175
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html54
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html7
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html38
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html48
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html65
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html80
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html69
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html51
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html10
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html65
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events-closewatcher.tentative.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault-closewatcher.tentative.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html58
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html37
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html109
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html47
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html77
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html53
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html278
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html48
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html21
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html131
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html43
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html54
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html188
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html5
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html41
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html15
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html229
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html73
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html85
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html50
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html36
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html55
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html70
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html131
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html101
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html20
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html101
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html42
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html58
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html17
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html20
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html68
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html52
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html102
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html44
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js18
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css14
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html24
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html1
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpgbin0 -> 7782 bytes
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html63
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html59
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html26
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html65
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html33
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html28
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html31
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html45
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html19
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html54
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.html56
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html134
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html77
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html40
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html9
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html104
166 files changed, 6999 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/META.yml b/testing/web-platform/tests/html/semantics/interactive-elements/META.yml
new file mode 100644
index 0000000000..c1dd8dddf9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - foolip
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js b/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js
new file mode 100644
index 0000000000..f08761be8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/common/accesskey.js
@@ -0,0 +1,36 @@
+setup({explicit_done: true, explicit_timeout: true});
+
+const NOTRUN = 3;
+let status = NOTRUN;
+function notrun() {
+ return status === NOTRUN;
+}
+add_completion_callback(tests => {
+ status = tests[0].status;
+});
+
+function pass() {
+ // Wait a couple of frames in case fail() is also called.
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ if (notrun()) {
+ test(() => {});
+ done();
+ }
+ });
+ });
+}
+
+function fail(msg) {
+ if (notrun()) {
+ test(() => { assert_unreached(msg); });
+ done();
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const accessKeyElement = document.querySelector('[accesskey]');
+ if (accessKeyElement.accessKeyLabel) {
+ document.querySelector('kbd').textContent = accessKeyElement.accessKeyLabel;
+ }
+});
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html
new file mode 100644
index 0000000000..521b4bb975
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-after-legend-manual.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>First input after the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend</legend>
+ <input onfocus="pass()">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html
new file mode 100644
index 0000000000..1c40cc7b81
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-before-legend-manual.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>First input before the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <input onfocus="pass()">
+ <legend accesskey=a>legend
+ <input onfocus="fail('input in legend was focused')">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html
new file mode 100644
index 0000000000..abd3a3b2df
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/first-input-inside-legend-manual.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>First input inside the legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend
+ <input onfocus="pass()">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html
new file mode 100644
index 0000000000..e2880a77bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-manual.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Focusable legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend tabindex=0 onclick="fail('unexpected click event on legend')"
+ onfocus="fail('legend was focused')" accesskey=a>
+ legend
+ <input onfocus="pass()">
+ </legend>
+ <input onfocus="fail('input after legend was focused')">
+</fieldset>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html
new file mode 100644
index 0000000000..49dcaaf7d5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/focusable-legend-sibling-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Focusable legend sibling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>first legend</legend>
+ <legend tabindex=0 onfocus="fail('sibling legend was focused')">second legend</legend>
+</fieldset>
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html
new file mode 100644
index 0000000000..dc6af48323
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/input-outside-fieldset-manual.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Input outside fieldset</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<fieldset>
+ <legend accesskey=a>legend</legend>
+</fieldset>
+<input onfocus="fail('input outside fieldset was focused')">
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html
new file mode 100644
index 0000000000..8a7b20565f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/label-sibling-manual.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Label sibling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<input id=x onfocus="fail('input associated with the label was focused')">
+<fieldset>
+ <legend accesskey=a>legend</legend>
+ <label for=x onclick="fail('label received a click event')">label</label>
+</fieldset>
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html
new file mode 100644
index 0000000000..e7abb71454
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/commands/legend/no-fieldset-parent-manual.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>No fieldset parent</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=../common/accesskey.js></script>
+<p>Press the access key combination for "a". <kbd></kbd></p>
+<legend accesskey=a>
+ legend
+ <input onfocus="fail('input in legend was focused')">
+</legend>
+<input onfocus="fail('input after legend was focused')">
+<script>
+ onkeyup = e => {
+ if (e.keyCode === 65) {
+ pass();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html b/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html
new file mode 100644
index 0000000000..f723d3a92a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/contextmenu-historical.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>menu element removed properties</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-menu-element">
+<link rel="help" href="https://github.com/whatwg/html/pull/2742">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<menu type="context" label="label">
+ <menuitem>Text</menuitem>
+ <menuitem type="checkbox" checked>Checked</menuitem>
+ <menuitem disabled>Disabled</menuitem>
+ <menuitem default>Default</menuitem>
+</menu>
+
+<script>
+"use strict";
+
+const menu = document.querySelector("menu");
+const menuitem = document.querySelector("menuitem");
+
+test(() => {
+ assert_false("HTMLMenuItemElement" in window, "the HTMLMenuItemElement interface must not exist");
+ assert_equals(menuitem.constructor, HTMLUnknownElement, "A <menuitem> must be HTMLUnknownElement");
+
+ for (const prop of ["type", "label", "icon", "disabled", "checked", "radiogroup", "default"]) {
+ assert_false(prop in menuitem, `menuitem.${prop} must not be present`);
+ }
+}, "HTMLMenuItemElement must not be not present");
+
+test(() => {
+ const potentialBadLocations = [
+ window,
+ document,
+ HTMLElement.prototype,
+ SVGElement.prototype,
+ Document.prototype,
+ HTMLDocument.prototype,
+ Element.prototype
+ ];
+ for (const location of potentialBadLocations) {
+ assert_false("onshow" in location,
+ `${location.constructor.name} must not have a property "onshow"`);
+ }
+}, `onshow must not be present on the GlobalEventHandlers locations`);
+
+test(() => {
+ assert_false("RelatedEvent" in window);
+}, "RelatedEvent must not be present");
+
+test(() => {
+ assert_false("contextMenu" in HTMLElement.prototype,
+ "HTMLElement's prototype must not have a property \"contextMenu\"");
+ assert_false("contextMenu" in document.createElement("div"),
+ "A div must not have a property \"contextMenu\"");
+}, "el.contextMenu must not be present");
+
+test(() => {
+ assert_false("type" in menu);
+
+ menu.type = "toolbar";
+ assert_equals(menu.getAttribute("type"), "context");
+}, "menu.type must not exist or reflect the content attribute");
+
+test(() => {
+ assert_false("label" in menu);
+
+ menu.label = "new label";
+ assert_equals(menu.getAttribute("label"), "label");
+}, "menu.label must not exist or reflect the content attribute");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:enabled"), []);
+}, ":enabled must not match menuitems");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:disabled"), []);
+}, ":disabled must not match menuitems");
+
+test(() => {
+ assert_array_equals(document.querySelectorAll("menuitem:checked"), []);
+}, ":checked must not match menuitems");
+
+test(() => {
+ try {
+ assert_array_equals(document.querySelectorAll("menuitem:default"), []);
+ } catch (e) {
+ // Not everyone has implemented :default as of the time of this writing.
+ if (e.name !== "SyntaxError") {
+ throw e;
+ }
+ }
+}, ":default must not match menuitems");
+
+test(() => {
+ assert_equals(getComputedStyle(menu).display, "block");
+}, "The user-agent stylesheet must leave type=\"context\" menus as block display like other menus");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html
new file mode 100644
index 0000000000..0ecd30dda3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-ax-slot-recalc-crash.html
@@ -0,0 +1,28 @@
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1255406">
+<script type="text/javascript">
+var nodes = Array();
+var text = Array();
+ {
+ nodes[9] = document.createElement('textarea');
+ nodes[11] = document.createElement('legend');
+ nodes[44] = document.createElement('details');
+ document.documentElement.appendChild(nodes[44]);
+ nodes[68] = document.createElement('fieldset');
+ nodes[81] = document.createElement('option');
+
+
+
+ nodes[85] = document.createElement('img');
+ text[42] = document.createTextNode('744879385');
+ nodes[44].appendChild(text[42]);
+ nodes[68].appendChild(nodes[11]);
+ nodes[44].appendChild(nodes[68]);
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ try { nodes[85].appendChild(nodes[68]); } catch(e) {}
+ });
+ });
+ nodes[44].appendChild(nodes[9]);
+ requestAnimationFrame(() => { document.execCommand("SelectAll", false, ""); });
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
new file mode 100644
index 0000000000..d3d04f07a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/6466">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="height:2000px">spacer</div>
+
+<details id=details>
+ <div id=target>target</div>
+</details>
+
+<script>
+async_test(t => {
+ assert_false(details.hasAttribute('open'),
+ `The <details> should be closed at the start of the test.`);
+ assert_equals(window.pageYOffset, 0,
+ `The page should be scrolled to the top at the start of the test.`);
+
+ window.location.hash = '#target';
+
+ requestAnimationFrame(t.step_func_done(() => {
+ assert_true(details.hasAttribute('open'),
+ `<details> should be opened by navigating to an element inside it.`);
+ assert_not_equals(window.pageYOffset, 0,
+ `The page should be scrolled down to the <details> element.`);
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html
new file mode 100644
index 0000000000..d24b4634cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/auto-expand-window-find-crash.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264704">
+
+<script>
+function runTest() {
+ details2.appendChild(child);
+ document.caretRangeFromPoint();
+}
+</script>
+
+<body onload=runTest()>
+
+<details style="writing-mode: vertical-rl">
+ <div id=child>foo</div>
+</details>
+
+<details id=details2 open=true ontoggle="window.find()">
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html
new file mode 100644
index 0000000000..1936cdb67d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/closed-details-layout-apis.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/6466">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id=details>
+ <div style="width:100px; height:100px; background-color:red" id=innerdiv></div>
+</details>
+
+<script>
+test(() => {
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width before open');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height before open');
+ details.open = true;
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width after open');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height after open');
+ details.open = false;
+ assert_not_equals(innerdiv.getBoundingClientRect().x, 0, 'x after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().y, 0, 'y after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().width, 0, 'width after close');
+ assert_not_equals(innerdiv.getBoundingClientRect().height, 0, 'height after close');
+}, `Verifies the layout results of elements inside a closed <details> based on the usage of content-visibility:hidden.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html
new file mode 100644
index 0000000000..14f2be232f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<details>
+ <summary>new summary</summary>
+ details
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html
new file mode 100644
index 0000000000..1b0062e43a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-add-summary.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel=match href="details-add-summary-ref.html">
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/6466">
+
+<!-- This test makes sure that new <summary> elements get rendered correctly
+ when added to a <details> element. I ran into it when adding
+ content-visibility:hidden to the second slot of <details>. -->
+
+<script>
+onload = () => {
+ const newsummary = document.createElement('summary');
+ newsummary.textContent = 'new summary';
+ document.getElementById('detailsid').insertBefore(newsummary,
+ document.getElementById('oldsummary'));
+
+ document.documentElement.classList.remove('reftest-wait');
+};
+</script>
+
+<details id=detailsid>
+ <summary id=oldsummary>old summary</summary>
+ details
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html
new file mode 100644
index 0000000000..393e464c4c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-cq-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1334983">
+
+<canvas>
+ <details>
+ <card card>
+
+<script>
+async function trigger() {
+ document.querySelector("canvas").style.setProperty("container-type", "size");
+ document.querySelector("canvas").style.setProperty("column-span", "all");
+ document.querySelector("card").setAttribute("contenteditable", "true");
+}
+onload = requestAnimationFrame(() => requestAnimationFrame(trigger));
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html
new file mode 100644
index 0000000000..dc8686b216
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-findstring-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264507">
+
+<script>
+window.onload = () => {
+ window.getSelection().selectAllChildren(document.body);
+ document.querySelector('object').remove();
+ document.execCommand('FindString',false,0);
+};
+</script>
+
+<details>
+ <object id='id6'></object>
+</details>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html
new file mode 100644
index 0000000000..a5534e24d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details-keyboard-activation.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Details activation with space bar</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1726454">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+:root {
+ scroll-behavior: instant;
+}
+.spacer {
+ height: 200vh;
+}
+</style>
+<details>
+ <summary>Activate me with the <kbd>Space</kbd> key</summary>
+ <p>Summary</p>
+</details>
+<div class="spacer"></div>
+<script>
+function tick() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
+
+promise_test(async t => {
+ const details = document.querySelector("details");
+ const summary = details.querySelector("summary");
+
+ summary.focus();
+ assert_equals(document.activeElement, summary, "Summary should be focusable");
+ assert_false(details.open, "Details should be closed");
+
+ const oldScrollY = window.scrollY;
+ assert_equals(oldScrollY, 0, "Should be at top");
+
+ window.addEventListener("scroll", t.unreached_func("Unexpected scroll event"));
+
+ await test_driver.send_keys(summary, " ");
+
+ assert_true(details.open, "Space bar on summary should open details");
+ assert_equals(window.scrollY, oldScrollY, "Scroll position shouldn't change");
+
+ await tick();
+
+ assert_equals(window.scrollY, oldScrollY, "Scroll position shouldn't change");
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html
new file mode 100644
index 0000000000..5ed14c53af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/details.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML details element API</title>
+ <style>#one, #two { visibility: hidden; }</style>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+
+ <!-- Used in parsing tests -->
+ <div id='one'><details></details><details></details></div>
+ <div id='two'><p><details></details></div>
+
+ <script type="text/javascript">
+
+function makeDetails () {
+ return document.createElement('details');
+}
+
+
+// <details>
+test(function () {
+ var times = document.getElementById('one').getElementsByTagName('details');
+ assert_equals( times.length, 2 );
+}, 'HTML parsing should locate 2 details elements in this document');
+
+test(function () {
+ assert_equals( document.getElementById('two').getElementsByTagName('p')[0].innerHTML, '' );
+}, 'HTML parsing should close an unclosed <p> before <details>');
+
+test(function () {
+ assert_true( !!window.HTMLDetailsElement );
+}, 'HTMLDetailsElement should be exposed for prototyping');
+
+test(function () {
+ assert_true( makeDetails() instanceof window.HTMLDetailsElement);
+}, 'a dynamically created details element should be instanceof HTMLDetailsElement');
+
+test(function () {
+ assert_true( document.getElementById('one').getElementsByTagName('details')[0] instanceof window.HTMLDetailsElement);
+}, 'a details element from the parser should be instanceof HTMLDetailsElement');
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html
new file mode 100644
index 0000000000..80812cccb5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/display-table-with-rt-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=969619">
+<details open="open" style="display:table;"><rt></rt></details>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> { }, "No crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html
new file mode 100644
index 0000000000..35ddca1fa6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/modified-details-crash.html
@@ -0,0 +1,31 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1276488">
+
+<style>
+.first + .second {}
+</style>
+
+<script>
+const details = document.createElement('details');
+const ol = document.createElement('ol');
+const text = document.createTextNode('abcdefghijklmnopqrstuvxyz');
+
+function step3() {
+ ol.setAttribute('class', 'first');
+}
+
+function step2() {
+ details.appendChild(text);
+ requestAnimationFrame(step3);
+}
+
+function runTest() {
+ document.documentElement.appendChild(details);
+ details.appendChild(ol);
+
+ requestAnimationFrame(step2);
+}
+
+onload = runTest;
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html
new file mode 100644
index 0000000000..f3e821a950
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-details-crash.html
@@ -0,0 +1,26 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1270206">
+
+<script type="text/javascript">
+function eventHandler1() {
+ document.getElementById('target').insertAdjacentText("afterEnd", "");
+ document.getElementById('target').focus();
+ document.getElementById('target').hidden = "true";
+}
+function operate() {
+ document.addEventListener('DOMNodeInsertedIntoDocument', eventHandler1, true);
+}
+function exec_event() {
+ event = new Event('DOMNodeInsertedIntoDocument')
+ document.dispatchEvent(event)
+}
+function go(){
+ operate();
+ exec_event();
+}
+</script>
+<body onload="go();" contentEditable="true">
+<details onselectstart='eventHandler2();'>
+<dfn id='target' class=onload='eventHandler1();'>
+<details id= onmsgesturehold='eventHandler2();'>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html
new file mode 100644
index 0000000000..c8d8ae4ed7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/nested-top-layer-elements-in-details-crash.html
@@ -0,0 +1,17 @@
+<!doctype HTML>
+<link rel=author href="mailto:vmpstr@chromium.org">
+<link rel=help href="https://crbug.com/1273395">
+
+<dialog id="parentElement">
+ <details id="childElement" open="true" ontoggle="toggleHandler()">
+ <dialog id="grandchildElement">
+ </dialog>
+ </details>
+</dialog>
+<script>
+function toggleHandler() {
+ grandchildElement.showModal();
+ parentElement.showModal();
+ childElement.open = false;
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
new file mode 100644
index 0000000000..8ad60de5d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The details element</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-details-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<details id=details1>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details2 open>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details3 style="display:none;">
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details4>
+</details>
+<details id=details6>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details7>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details8 open>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<script>
+ window.details9TogglePromise = new Promise(resolve => {
+ window.details9TogglePromiseResolver = resolve;
+ });
+</script>
+<details id=details9 ontoggle="window.details9TogglePromiseResolver()" open>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<details id=details10>
+ <summary>Lorem ipsum</summary>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+</details>
+<script>
+ var t1 = async_test("Adding open to 'details' should fire a toggle event at the 'details' element"),
+ t2 = async_test("Removing open from 'details' should fire a toggle event at the 'details' element"),
+ t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element"),
+ t4 = async_test("Adding open from 'details' (no children) should fire a toggle event at the 'details' element"),
+ t6 = async_test("Calling open twice on 'details' fires only one toggle event"),
+ t7 = async_test("Calling setAttribute('open', '') to 'details' should fire a toggle event at the 'details' element"),
+ t8 = async_test("Calling removeAttribute('open') to 'details' should fire a toggle event at the 'details' element"),
+ t9 = async_test("Setting open=true to opened 'details' element should not fire a toggle event at the 'details' element"),
+ t10 = async_test("Setting open=false to closed 'details' element should not fire a toggle event at the 'details' element"),
+
+ details1 = document.getElementById('details1'),
+ details2 = document.getElementById('details2'),
+ details3 = document.getElementById('details3'),
+ details4 = document.getElementById('details4'),
+ details6 = document.getElementById('details6'),
+ details7 = document.getElementById('details7'),
+ details8 = document.getElementById('details8'),
+ details9 = document.getElementById('details9'),
+ details10 = document.getElementById('details10'),
+ loop=false;
+
+ function testEvent(evt) {
+ assert_true(evt.isTrusted, "event is trusted");
+ assert_false(evt.bubbles, "event doesn't bubble");
+ assert_false(evt.cancelable, "event is not cancelable");
+ assert_equals(Object.getPrototypeOf(evt), Event.prototype, "Prototype of toggle event is Event.prototype");
+ }
+
+ details1.ontoggle = t1.step_func_done(function(evt) {
+ assert_true(details1.open);
+ testEvent(evt)
+ });
+ details1.open = true; // opens details1
+
+ details2.ontoggle = t2.step_func_done(function(evt) {
+ assert_false(details2.open);
+ testEvent(evt);
+ });
+ details2.open = false; // closes details2
+
+ details3.ontoggle = t3.step_func_done(function(evt) {
+ assert_true(details3.open);
+ testEvent(evt);
+ });
+ details3.open = true; // opens details3
+
+ details4.ontoggle = t4.step_func_done(function(evt) {
+ assert_true(details4.open);
+ testEvent(evt);
+ });
+ details4.open = true; // opens details4
+
+ async_test(function(t) {
+ var details5 = document.createElement("details");
+ details5.ontoggle = t.step_func_done(function(evt) {
+ assert_true(details5.open);
+ testEvent(evt);
+ })
+ details5.open = true;
+ }, "Adding open to 'details' (not in the document) should fire a toggle event at the 'details' element");
+
+ details6.open = true;
+ details6.open = false;
+ details6.ontoggle = t6.step_func(function() {
+ if (loop) {
+ assert_unreached("toggle event fired twice");
+ } else {
+ loop = true;
+ }
+ });
+ t6.step_timeout(function() {
+ assert_true(loop);
+ t6.done();
+ }, 0);
+
+ details7.ontoggle = t7.step_func_done(function(evt) {
+ assert_true(details7.open);
+ testEvent(evt)
+ });
+ details7.setAttribute('open', ''); // opens details7
+
+ details8.ontoggle = t8.step_func_done(function(evt) {
+ assert_false(details8.open);
+ testEvent(evt)
+ });
+ details8.removeAttribute('open'); // closes details8
+
+ window.details9TogglePromise.then(t9.step_func(() => {
+ // The toggle event should be fired once when declaring details9 with open
+ // attribute.
+ details9.ontoggle = t9.step_func(() => {
+ assert_unreached("toggle event fired twice on opened details element");
+ });
+ // setting open=true on details9 should not fire another event since it is
+ // already open.
+ details9.open = true;
+ t9.step_timeout(() => {
+ assert_true(details9.open);
+ t9.done();
+ });
+ }));
+
+ details10.ontoggle = t10.step_func_done(function(evt) {
+ assert_unreached("toggle event fired on closed details element");
+ });
+ details10.open = false; // closes details10
+ t10.step_timeout(function() {
+ assert_false(details10.open);
+ t10.done();
+ }, 0);
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html
new file mode 100644
index 0000000000..77ed29ce56
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/abspos-dialog-layout.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="user-scalable=no">
+<title>Tests layout of absolutely positioned modal dialogs.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+/* Remove body margin and dialog styles for easier positioning expected values */
+body {
+ height: 10000px;
+ margin: 0;
+}
+
+dialog {
+ border: 0;
+ padding: 0;
+ max-width: 100%;
+ max-height: 100%;
+}
+
+#absolute-div {
+ position: absolute;
+ top: 800px;
+ height: 50px;
+ width: 90%;
+}
+
+#relative-div {
+ position: relative;
+ top: 20px;
+ height: 30px;
+}
+</style>
+</head>
+<dialog >It is my dialog.</dialog>
+<div id="absolute-div">
+ <div id="relative-div"></div>
+</div>
+<script>
+"use strict";
+
+function checkVerticallyCentered(dialog) {
+ var centeredTop = (document.documentElement.clientHeight - dialog.offsetHeight) / 2;
+ // Using approx equals because getBoundingClientRect() and centeredTop
+ // are calculated by different parts of the engine. Due to the loss
+ // of precision, the numbers might not equal exactly.
+ assert_approx_equals(dialog.getBoundingClientRect().top, centeredTop, 1);
+}
+
+function reset() {
+ document.body.style.width = "auto";
+ dialog.style.top = null;
+ dialog.style.height = null;
+ if (dialog.open)
+ dialog.close();
+ dialog.remove();
+ document.body.appendChild(dialog);
+ window.scroll(0, 500);
+}
+
+var dialog = document.querySelector('dialog');
+var absoluteContainer = document.querySelector('#absolute-div');
+var relativeContainer = document.querySelector('#relative-div');
+reset();
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "showModal() should center in the viewport");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.close();
+ window.scroll(0, 2 * window.scrollY);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Dialog should be recentered if showModal() is called after close()");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ var expectedTop = dialog.getBoundingClientRect().top;
+ window.scroll(0, window.scrollY * 2);
+
+ // Trigger relayout
+ document.body.offsetHeight;
+
+ window.scroll(0, window.scrollY / 2);
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "Dialog should not recenter on relayout.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.style.height = '20000px';
+ dialog.showModal();
+ assert_equals(dialog.getBoundingClientRect().top, 0);
+}, "A tall dialog should be positioned at the top of the viewport.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ document.body.style.width = '4000px';
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+
+}, "The dialog should be centered regardless of the presence of a horizontal scrollbar.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.remove();
+ absoluteContainer.appendChild(dialog);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+ dialog.close();
+
+ dialog.remove();
+ relativeContainer.appendChild(dialog);
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Centering should work when dialog is inside positioned containers.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ var expectedTop = dialog.getBoundingClientRect().top;
+ relativeContainer.style.display = 'none';
+ relativeContainer.style.display = 'block';
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "A centered dialog's position should survive becoming display:none temporarily.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ // Remove and reinsert so that the document position isn't changed by the second remove and reinsert
+ dialog.remove();
+ relativeContainer.appendChild(dialog);
+
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+ dialog.remove();
+
+ relativeContainer.appendChild(dialog);
+ assert_equals(relativeContainer.getBoundingClientRect().top, dialog.getBoundingClientRect().top);
+}, "Dialog should not still be centered when removed, and re-added to the document.");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.style.top = '0px';
+ var expectedTop = dialog.getBoundingClientRect().top;
+ dialog.close();
+ dialog.showModal();
+ assert_equals(dialog.getBoundingClientRect().top, expectedTop);
+}, "Dialog's specified position should survive after close() and showModal().");
+
+test(function() {
+ this.add_cleanup(reset);
+
+ dialog.showModal();
+ dialog.removeAttribute('open');
+ dialog.showModal();
+ checkVerticallyCentered(dialog);
+}, "Dialog should be recentered if showModal() is called after removing 'open'.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html
new file mode 100644
index 0000000000..70d84c21ff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.backdrop {
+ position: absolute;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+</head>
+<body>
+Test ::backdrop used in descendant selectors. The test passes if there are two green boxes and no red.
+<div class="backdrop" style="top: 100px; left: 100px"></div>
+<div class="backdrop" style="top: 100px; left: 300px"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html
new file mode 100644
index 0000000000..3e2ac6afa1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="backdrop-descendant-selector-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+::backdrop {
+ position: absolute;
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+
+/* This shouldn't be matched, dialog is not the parent of ::backdrop.
+ * It is given high specificity so we actually test something.
+ */
+#dialog-parent > #dialog > ::backdrop,
+#dialog-parent > #dialog ::backdrop {
+ background: red;
+}
+
+#dialog-parent > ::backdrop {
+ top: 100px;
+ left: 100px;
+ background: green;
+}
+
+#backdrop-ancestor ::backdrop {
+ top: 100px;
+ left: 300px;
+ background: green;
+}
+</style>
+</head>
+<body>
+Test ::backdrop used in descendant selectors. The test passes if there are two green boxes and no red.
+
+<div id="dialog-parent">
+ <dialog id="dialog"></dialog>
+</div>
+<div id="backdrop-ancestor">
+ <p><span><dialog></dialog></span></p>
+</div>
+<script>
+var dialogs = document.querySelectorAll('dialog');
+for (var i = 0; i < dialogs.length; ++i)
+ dialogs[i].showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit-ref.html
new file mode 100644
index 0000000000..bdc84186f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+#backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test that ::backdrop does not inherit from anything. The test passes if there is
+a green box and no red.
+<div id="backdrop"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit.html
new file mode 100644
index 0000000000..44085b9c31
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-does-not-inherit-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: inherit;
+ background: red;
+}
+
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ visibility: inherit;
+ background: green;
+}
+</style>
+<body>
+Test that ::backdrop does not inherit from anything. The test passes if there is
+a green box and no red.
+<div style="visibility: hidden">
+ <dialog></dialog>
+</div>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html
new file mode 100644
index 0000000000..c49a11d416
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<title>Reference: Test that adding display: none; dynamically on ::backdrop makes it disappear</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<p>Test passes if there is no red.</p>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html
new file mode 100644
index 0000000000..bcf100b368
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-display-none.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<title>Test that adding display: none; dynamically on ::backdrop makes it disappear</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="backdrop-dynamic-display-none-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<p>Test passes if there is no red.</p>
+<dialog></dialog>
+<style>
+dialog { visibility: hidden; }
+::backdrop { background-color: red; }
+.hidden-backdrop::backdrop {
+ display: none;
+}
+</style>
+<script>
+dialog = document.querySelector("dialog");
+dialog.showModal();
+requestAnimationFrame(() => {
+ dialog.classList.add("hidden-backdrop");
+});
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html
new file mode 100644
index 0000000000..01cb93d2ab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background-color: green;
+}
+</style>
+</head>
+<body>
+Test dynamic changes to ::backdrop style. The test passes if there is a green box below.
+<div class="backdrop"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html
new file mode 100644
index 0000000000..19297b8c17
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="backdrop-dynamic-style-change-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background-color: red;
+}
+
+.green::backdrop {
+ background-color: green;
+}
+</style>
+</head>
+<body>
+Test dynamic changes to ::backdrop style. The test passes if there is a green box below.
+<dialog></dialog>
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+dialog.classList.add('green');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html
new file mode 100644
index 0000000000..4857557bf8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+#backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test that position 'static' or 'relative' for ::backdrop computes to 'absolute'.
+The test passes if there is a single green box.
+<div id="backdrop"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html
new file mode 100644
index 0000000000..7201ebbc02
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-in-flow-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ visibility: hidden;
+}
+
+dialog::backdrop {
+ height: 100px;
+ width: 50px;
+}
+
+#left::backdrop {
+ position: static;
+ top: 100px;
+ left: 100px;
+ background: green;
+}
+
+#right::backdrop {
+ position: relative;
+ background: green;
+ top: 100px;
+ left: 150px;
+}
+</style>
+<body>
+Test that position 'static' or 'relative' for ::backdrop computes to 'absolute'.
+The test passes if there is a single green box.
+<dialog id="left"></dialog>
+<dialog id="right"></dialog>
+</div>
+<script>
+document.querySelector('#left').showModal();
+document.querySelector('#right').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html
new file mode 100644
index 0000000000..31c3c6ce3c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<title>Test that ::backdrop receives events for the associated element</title>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<body>
+<style>
+/* ::backdrop takes up whole screen, actual <dialog> is hidden */
+dialog {
+ visibility: hidden;
+ pointer-events: none;
+}
+
+dialog::backdrop {
+ background-color: red;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+dialog.clicked::backdrop {
+ background-color: green;
+}
+</style>
+<dialog></dialog>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+setup({ single_test: true });
+
+const dialog = document.querySelector("dialog");
+dialog.showModal();
+dialog.addEventListener("click", () => {
+ // Change style for debugging purposes, done() actually makes the test pass
+ dialog.className = "clicked";
+ done();
+});
+new test_driver.Actions()
+ .pointerMove(0, 0, {origin: "viewport"})
+ .pointerDown()
+ .pointerUp()
+ .send();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html
new file mode 100644
index 0000000000..d3f82de181
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order-ref.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<style>
+div {
+ position: absolute;
+}
+
+#bottom-backdrop {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ background-color: rgb(0, 50, 0);
+}
+
+#bottom {
+ top: 125px;
+ left: 125px;
+ height: 250px;
+ width: 250px;
+ background-color: rgb(0, 90, 0);
+}
+
+#middle-backdrop {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ background-color: rgb(0, 130, 0);
+}
+
+#middle {
+ top: 175px;
+ left: 175px;
+ height: 150px;
+ width: 150px;
+ background-color: rgb(0, 170, 0);
+}
+
+#top-backdrop {
+ top: 200px;
+ left: 200px;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(0, 210, 0);
+}
+
+#top {
+ top: 225px;
+ left: 225px;
+ height: 50px;
+ width: 50px;
+ background-color: rgb(0, 255, 0);
+}
+</style>
+<body>
+Test for dialog::backdrop stacking order. The test passes if there are 6
+boxes enclosed in each other, becoming increasingly smaller and brighter
+green.
+<div id="bottom-backdrop"></div>
+<div id="bottom"></div>
+<div id="middle-backdrop"></div>
+<div id="middle"></div>
+<div id="top-backdrop"></div>
+<div id="top"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html
new file mode 100644
index 0000000000..57cc63aab4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-stacking-order-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ padding: 0px;
+ border: none;
+ margin: 0px;
+}
+
+#bottom::backdrop {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ background-color: rgb(0, 50, 0);
+ z-index: 100; /* z-index has no effect. */
+}
+
+#bottom {
+ top: 125px;
+ left: 125px;
+ height: 250px;
+ width: 250px;
+ background-color: rgb(0, 90, 0);
+}
+
+#middle::backdrop {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ background-color: rgb(0, 130, 0);
+ z-index: -100; /* z-index has no effect. */
+}
+
+#middle {
+ top: 175px;
+ left: 175px;
+ height: 150px;
+ width: 150px;
+ background-color: rgb(0, 170, 0);
+}
+
+#top::backdrop {
+ top: 200px;
+ left: 200px;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(0, 210, 0);
+ z-index: 0; /* z-index has no effect. */
+}
+
+#top {
+ top: 225px;
+ left: 225px;
+ height: 50px;
+ width: 50px;
+ background-color: rgb(0, 255, 0);
+ z-index: -1000; /* z-index has no effect. */
+}
+</style>
+<body>
+Test for dialog::backdrop stacking order. The test passes if there are 6
+boxes enclosed in each other, becoming increasingly smaller and brighter
+green.
+<dialog id="top"></dialog>
+<dialog id="middle"></dialog>
+<dialog id="bottom"></dialog>
+<script>
+var topDialog = document.getElementById('top');
+var middleDialog = document.getElementById('middle');
+var bottomDialog = document.getElementById('bottom');
+topDialog.showModal();
+bottomDialog.showModal();
+topDialog.close(); // Just to shuffle the top layer order around a little.
+middleDialog.showModal();
+topDialog.showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html
new file mode 100644
index 0000000000..6ffd72296d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering-iframe.sub.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element centered frame</title>
+<style>
+ html {
+ writing-mode: {{GET[html-writing-mode]}}
+ }
+
+ #container {
+ writing-mode: {{GET[container-writing-mode]}}
+ }
+
+ dialog {
+ writing-mode: {{GET[dialog-writing-mode]}};
+ border: none;
+ padding: 0;
+ max-width: initial;
+ max-height: initial;
+ width: {{GET[dialog-width]}};
+ height: {{GET[dialog-height]}};
+ }
+</style>
+
+<div id="container">
+ <dialog>X</dialog>
+</div>
+
+<script>
+"use strict";
+document.querySelector("dialog").showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html
new file mode 100644
index 0000000000..2dc6ce3edf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/centering.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: centered alignment</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/#flow-content-3">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<script>
+"use strict";
+
+// Be sure to sync with centering-iframe.html
+const dialogWidth = 20;
+const dialogHeight = 10;
+
+testDialogCentering("horizontal-tb", "", "", "tall viewport", 40, 100);
+testDialogCentering("horizontal-tb", "", "", "wide viewport", 100, 40);
+testDialogCentering("horizontal-tb", "", "", "square viewport", 100, 100);
+testDialogCentering("horizontal-tb", "", "", "dialog and viewport match", dialogWidth, dialogHeight);
+
+testDialogCentering("vertical-rl", "", "", "tall viewport", 40, 100);
+testDialogCentering("vertical-lr", "", "", "tall viewport", 40, 100);
+
+testDialogCentering("vertical-rl", "", "horizontal-tb", "tall viewport", 40, 100);
+testDialogCentering("vertical-lr", "", "horizontal-tb", "tall viewport", 40, 100);
+
+testDialogCentering("horizontal-tb", "vertical-rl", "", "tall viewport", 40, 100);
+testDialogCentering("vertical-rl", "horizontal-tb", "", "tall viewport", 40, 100);
+
+testDialogCentering("horizontal-tb", "vertical-rl", "horizontal-tb", "tall viewport", 40, 100);
+testDialogCentering("vertical-rl", "horizontal-tb", "vertical-rl", "tall viewport", 40, 100);
+
+function testDialogCentering(writingMode, containerWritingMode, dialogWritingMode, label, iframeWidth, iframeHeight) {
+ const dialogSizesToTest = [["", ""], [dialogWidth.toString()+'px', dialogHeight.toString()+'px']];
+ for (const dialogSizes of dialogSizesToTest) {
+ const isDefaultSize = dialogSizes[0] == "";
+ // This test doesn't make sense if the dialog sizes are default
+ if (isDefaultSize && label == "dialog and viewport match") {
+ continue;
+ }
+
+ async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = `centering-iframe.sub.html?html-writing-mode=${writingMode}&container-writing-mode=${containerWritingMode}&dialog-writing-mode=${dialogWritingMode}&dialog-width=${dialogSizes[0]}&dialog-height=${dialogSizes[1]}`;
+ iframe.width = iframeWidth;
+ iframe.height = iframeHeight;
+ iframe.onload = t.step_func_done(() => {
+ const dialog = iframe.contentDocument.querySelector("dialog");
+ const dialogRect = dialog.getBoundingClientRect();
+
+ const expectedLeftOffset = iframeWidth / 2 - dialogRect.width / 2;
+ const expectedTopOffset = Math.max(iframeHeight / 2 - dialogRect.height / 2, 0);
+
+ if (isDefaultSize) {
+ assert_approx_equals(dialogRect.left, expectedLeftOffset, 1/60);
+ assert_approx_equals(dialogRect.top, expectedTopOffset, 1/60);
+ } else {
+ assert_equals(dialogRect.left, expectedLeftOffset);
+ assert_equals(dialogRect.top, expectedTopOffset);
+ }
+ });
+ document.body.appendChild(iframe);
+ }, writingMode + (containerWritingMode ? ` (container ${containerWritingMode})` : "") +
+ (dialogWritingMode ? ` (dialog ${dialogWritingMode})` : "") + `: ${label}` + `, default-sizes: ${isDefaultSize}`);
+
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html
new file mode 100644
index 0000000000..9d9856962d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/closed-dialog-does-not-block-mouse-events.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+#div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+<div id=div></div>
+<dialog id="dialog"></dialog>
+<dialog></dialog>
+
+<script>
+promise_test(async () => {
+ const dialog = document.getElementById('dialog');
+ dialog.showModal();
+ dialog.close();
+
+ const div = document.getElementById('div');
+ div.addEventListener('click', function(event) {
+ div.firedOn = true;
+ div.style.backgroundColor = 'green';
+ });
+
+ var absoluteTop = 0;
+ var absoluteLeft = 0;
+ for (var parentNode = div; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = absoluteLeft + div.offsetWidth / 2;
+ const y = absoluteTop + div.offsetHeight / 2;
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ assert_true(div.firedOn, 'div should have gotten a click event.');
+}, 'Ensure that closed dialogs do not block mouse events. To test manually, click the red box. The test succeeds if the red box turns green.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html
new file mode 100644
index 0000000000..5a6e6b21fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/default-color.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for dialog element colors</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+:root { background-color: Canvas; color: CanvasText; }
+#light { color-scheme: light }
+#dark { color-scheme: dark }
+</style>
+<dialog id="dialog" open>
+ This is a dialog
+</dialog>
+<dialog id="light" open>
+ This is a dialog
+</dialog>
+<dialog id="dark" open>
+ This is a dialog
+</dialog>
+<script>
+test(function() {
+ let dialog = document.getElementById("dialog");
+ let cs = getComputedStyle(dialog);
+ let rootCs = getComputedStyle(document.documentElement);
+ assert_equals(cs.color, rootCs.color, "Dialog color should match CanvasText");
+ assert_equals(cs.backgroundColor, rootCs.backgroundColor, "Dialog background should match Canvas");
+}, "<dialog> color and background match default")
+
+test(function() {
+ let lightCs = getComputedStyle(document.getElementById("light"));
+ let darkCs = getComputedStyle(document.getElementById("dark"));
+ assert_not_equals(lightCs.color, darkCs.color, "Dialog color should react to color-scheme");
+ assert_not_equals(lightCs.backgroundColor, darkCs.backgroundColor, "Dialog background should react to color-scheme");
+}, "<dialog> colors react to dark mode")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
new file mode 100644
index 0000000000..c8c1ab2826
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1206122">
+
+<body onload=dlg.show()>
+<dialog id="dlg">
+ <audio></audio>
+ <video></video>
+</dialog>
+
+This test passes if it does not crash.
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html
new file mode 100644
index 0000000000..894efd59dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-just-once.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
+<body>
+<dialog>
+<input>
+<input autofocus>
+</dialog>
+<script>
+// https://github.com/whatwg/html/issues/4788
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ assert_equals(document.activeElement, dialog.querySelector('[autofocus]'),
+ 'dialog.show() should set focus on a descendant element with an ' +
+ 'autofocus attribute.');
+ document.activeElement.blur();
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, document.body,
+ 'Non-dialog autofocus processing should be skipped.');
+}, 'An autofocus element in a dialog element should not try to get focus twice.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html
new file mode 100644
index 0000000000..ff9ebd7d28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus-multiple-times.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ assert_equals(document.activeElement, document.getElementById("outer-button"));
+
+ var focusCount = 0;
+ var dlg = document.getElementById("dlg");
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input2");
+ input2.onfocus = function() { focusCount += 1 };
+
+ var expectedFocusCount = 3;
+ for (i = 0; i < expectedFocusCount; i++) {
+ dlg.show();
+ assert_equals(document.activeElement, input2);
+ input1.focus();
+ assert_equals(document.activeElement,input1);
+ dlg.close();
+ }
+
+ assert_equals(focusCount.toString(), expectedFocusCount.toString());
+ });
+}, "autofocus is run every time a dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="dlg">
+ <!-- Unfocusable elements with [autofocus] should be ignored. -->
+ <input autofocus disabled>
+ <textarea autofocus hidden></textarea>
+ <input id="input1"></input>
+ <input id="input2" autofocus></input>
+</dialog>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html
new file mode 100644
index 0000000000..149a53eacf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-autofocus.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ assert_equals(document.activeElement, document.getElementById("outer-button"));
+
+ var dialog = document.getElementById('dialog');
+ dialog.showModal();
+
+ autofocusButton = document.getElementById('autofocus-button');
+ assert_equals(document.activeElement, autofocusButton);
+
+ anotherButton = document.getElementById('another-button');
+ anotherButton.focus();
+ assert_equals(document.activeElement, anotherButton);
+
+ // Test that recreating layout does not give focus back to a previously autofocused element.
+ autofocusButton.style.display = 'none';
+ document.body.offsetHeight;
+ autofocusButton.style.display = 'block';
+ document.body.offsetHeight;
+ assert_equals(document.activeElement, anotherButton);
+
+ // Test that reinserting does not give focus back to a previously autofocused element.
+ var parentNode = autofocusButton.parentNode;
+ parentNode.removeChild(autofocusButton);
+ document.body.offsetHeight;
+ parentNode.appendChild(autofocusButton);
+ document.body.offsetHeight;
+ assert_equals(document.activeElement, anotherButton);
+
+ dialog.close();
+ // Test that dialog focusing steps run when a dialog is reopened.
+ dialog.showModal();
+ assert_equals(document.activeElement, autofocusButton);
+ dialog.close();
+ });
+}, "autofocus when a modal dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="dialog">
+ <button></button>
+ <!-- Unfocusable elements with [autofocus] should be ignored. -->
+ <input autofocus disabled>
+ <textarea autofocus hidden></textarea>
+ <dialog>
+ <button autofocus></button>
+ </dialog>
+ <div>
+ <span>
+ <button id="autofocus-button" autofocus></button>
+ </span>
+ </div>
+ <button id="another-button" autofocus></button>
+</dialog>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events-closewatcher.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events-closewatcher.tentative.html
new file mode 100644
index 0000000000..5b1da9a2e3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events-closewatcher.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event is fired when the dialog is closed by user interaction</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event is fired when the dialog is closed by user interaction</p>
+<dialog>
+ <p>Hello World</p>
+ <button>user activation button</button>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+ var hasCloseEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ dialog.addEventListener("cancel", function(event) {
+ assert_true(true, "cancel event is fired");
+ assert_true(event.cancelable, "cancel event should be cancelable");
+ assert_false(hasCancelEventFired, "cancel event should only be fired once");
+ assert_false(hasCloseEventFired, "close event should be fired after cancel event");
+ hasCancelEventFired = true;
+ });
+
+ dialog.addEventListener("close", function() {
+ assert_true(true, "close event is fired");
+ assert_false(hasCloseEventFired, "close event should only be fired once");
+ assert_true(hasCancelEventFired, "cancel event should be fired before close event");
+ hasCloseEventFired = true;
+ done();
+ });
+
+ dialog.showModal();
+
+ (async () => {
+ // Pressing escape on the dialog needs user activation or else the cancel event won't be fired.
+ const button = dialog.querySelector('button');
+ const buttonClickPromise = new Promise(resolve => button.onclick = resolve);
+ await test_driver.click(button);
+ await buttonClickPromise;
+
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+ })();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
new file mode 100644
index 0000000000..03caab54ba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event is fired when the dialog is closed by user interaction</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event is fired when the dialog is closed by user interaction</p>
+<dialog>
+ <p>Hello World</p>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+ var hasCloseEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ dialog.addEventListener("cancel", function(event) {
+ assert_true(true, "cancel event is fired");
+ assert_true(event.cancelable, "cancel event should be cancelable");
+ assert_false(hasCancelEventFired, "cancel event should only be fired once");
+ assert_false(hasCloseEventFired, "close event should be fired after cancel event");
+ hasCancelEventFired = true;
+ });
+
+ dialog.addEventListener("close", function() {
+ assert_true(true, "close event is fired");
+ assert_false(hasCloseEventFired, "close event should only be fired once");
+ assert_true(hasCancelEventFired, "cancel event should be fired before close event");
+ hasCloseEventFired = true;
+ done();
+ });
+
+ dialog.showModal();
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault-closewatcher.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault-closewatcher.tentative.html
new file mode 100644
index 0000000000..ef99578ca9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault-closewatcher.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event with preventDefault on cancel event for dialog element</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event with preventDefault on cancel event for dialog element</p>
+<dialog>
+ <p>Hello World</p>
+ <button>user activation button</button>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ const verify = () => {
+ assert_true(hasCancelEventFired, "cancel is fired");
+ done();
+ };
+
+ dialog.addEventListener("cancel", function(event) {
+ hasCancelEventFired = true;
+ event.preventDefault();
+ step_timeout(function() {
+ verify();
+ }, 0)
+ });
+
+ dialog.addEventListener("close", function() {
+ assert_true(false, "close event should not be fired");
+ });
+
+ dialog.showModal();
+
+ (async () => {
+ // Pressing escape on the dialog needs user activation or else the cancel event won't be fired.
+ const button = dialog.querySelector('button');
+ const buttonClickPromise = new Promise(resolve => button.onclick = resolve);
+ await test_driver.click(button);
+ await buttonClickPromise;
+
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+ })();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html
new file mode 100644
index 0000000000..79728b649f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event with preventDefault on cancel event for dialog element</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event with preventDefault on cancel event for dialog element</p>
+<dialog>
+ <p>Hello World</p>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ const verify = () => {
+ assert_true(hasCancelEventFired, "cancel is fired");
+ done();
+ };
+
+ dialog.addEventListener("cancel", function(event) {
+ hasCancelEventFired = true;
+ event.preventDefault();
+ step_timeout(function() {
+ verify();
+ }, 0)
+ });
+
+ dialog.addEventListener("close", function() {
+ assert_true(false, "close event should not be fired");
+ });
+
+ dialog.showModal();
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html
new file mode 100644
index 0000000000..153d434317
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-input.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test dialog modal is closed by escape key with input focused</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test dialog modal is closed by escape key with input focused</p>
+<dialog id="dialog">
+ <p>Hello World</p>
+</dialog>
+
+<dialog id="dialogWithAutofocus">
+ <input autofocus/>
+</dialog>
+
+<script>
+ setup({ single_test: true });
+
+ const triggerEscKey = () => {
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+ };
+
+ /* Make sure we still cancel the dialog even if the input element is focused */
+ function runTestCancelWhenInputFocused() {
+ const dialog = document.getElementById("dialogWithAutofocus");
+ const input = document.querySelector("input");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog with input autofocused is closed");
+ done();
+ });
+ dialog.showModal();
+ assert_true(input == document.activeElement, "input element should be focused");
+
+ triggerEscKey();
+ }
+
+ const dialog = document.getElementById("dialog");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog closed");
+ step_timeout(function() {
+ runTestCancelWhenInputFocused();
+ }, 0);
+ });
+
+ dialog.showModal();
+ triggerEscKey();
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html
new file mode 100644
index 0000000000..178d5a2711
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-with-select.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test dialog modal is closed by escape key with select focused</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test dialog modal is closed by escape key with select focused</p>
+<dialog id="dialog">
+ <select>
+ <option value="one">one</option>
+ <option value="two">two</option>
+ </select>
+</dialog>
+
+<script>
+ setup({ single_test: true });
+
+ const dialog = document.getElementById("dialog");
+ const select = document.querySelector("select");
+
+ dialog.addEventListener("close", function() {
+ assert_false(dialog.open, "dialog with select is closed");
+ done();
+ });
+ dialog.showModal();
+ assert_true(select == document.activeElement, "select element should be focused");
+
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html
new file mode 100644
index 0000000000..fc003d29d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=253357">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!--
+To test manually, hit Escape once to see the topmost dialog turn green
+then once again to close it. Repeat for the remaining dialog.
+-->
+
+<style>
+#bottom {
+ top: 100px;
+ left: 100px;
+ height: 300px;
+ width: 300px;
+ margin: 0;
+ background: cyan;
+}
+
+#top {
+ top: 150px;
+ left: 150px;
+ height: 200px;
+ width: 200px;
+ margin: 0;
+ background: yellow;
+}
+</style>
+
+<dialog id="bottom">
+ <span></span>
+ <div>You can't Escape when this textbox has focus: <input id="swallow-input" type="text"></div>
+ <div>You can Escape even if this textbox has focus: <input id="normal-input" type="text"></div>
+</dialog>
+<dialog id="top">
+ <span></span>
+</dialog>
+
+<script>
+async function pressEscape() {
+ const actions = new test_driver.Actions()
+ .keyDown('\uE00C')
+ .keyUp('\uE00C');
+ await actions.send();
+}
+
+function handleCancel(event) {
+ this.style.background = 'green';
+ this.querySelector('span').textContent = 'I blocked the cancel! Try again to close me.';
+ event.preventDefault();
+ this.removeEventListener('cancel', handleCancel);
+}
+
+promise_test(async () => {
+ bottomDialog = document.getElementById('bottom');
+ bottomDialog.addEventListener('cancel', handleCancel);
+
+ topDialog = document.getElementById('top');
+ topDialog.addEventListener('cancel', handleCancel);
+
+ normalInput = document.getElementById('normal-input');
+ swallowInput = document.getElementById('swallow-input');
+ swallowInput.addEventListener('keydown', function(event) {
+ event.preventDefault();
+ });
+
+ bottomDialog.showModal();
+ topDialog.showModal();
+
+ await pressEscape();
+ assert_true(topDialog.open, 'Top dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Top dialog event listener should prevent closing.');
+
+ await pressEscape();
+ assert_false(topDialog.open, 'Top dialog should close.');
+ assert_true(bottomDialog.open, 'Top dialog should close.');
+
+ swallowInput.focus();
+ await pressEscape();
+ await pressEscape();
+ await pressEscape();
+ assert_false(topDialog.open, 'Input should swallow Escape mechanism.');
+ assert_true(bottomDialog.open, 'Input should swallow Escape mechanism.');
+
+ normalInput.focus();
+ await pressEscape();
+ assert_false(topDialog.open, 'Bottom dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Bottom dialog event listener should prevent closing.');
+
+ await pressEscape();
+ assert_false(topDialog.open, 'Bottom dialog should close.');
+ assert_false(bottomDialog.open, 'Bottom dialog should close.');
+
+ await pressEscape();
+ assert_false(topDialog.open, 'Pressing Escape now should do nothing.');
+ assert_false(bottomDialog.open, 'Pressing Escape now should do nothing.');
+
+ bottomDialog.remove();
+ topDialog.remove();
+}, 'Modal dialogs should close when the escape key is pressed.');
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html
new file mode 100644
index 0000000000..0f8d40aa2c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event-async.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<title>dialog element: close()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog id="d1" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ t = async_test("close() fires a close event"),
+ was_queued = false;
+
+ d1.onclose = t.step_func_done(function(e) {
+ assert_true(was_queued, "close event should be queued");
+ assert_true(e.isTrusted, "close event is trusted");
+ assert_false(e.bubbles, "close event doesn't bubble");
+ assert_false(e.cancelable, "close event is not cancelable");
+ });
+
+ t.step(function() {
+ d1.close();
+ was_queued = true;
+ })
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html
new file mode 100644
index 0000000000..b7903ed461
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-event.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=276785">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog></dialog>
+
+<script>
+async_test(t => {
+ document.addEventListener('close', t.step_func_done(() => {
+ t.assert_unreached(`The 'close' event unexpectedly bubbled.`);
+ }));
+
+ closedCount = 0;
+ dialog = document.querySelector('dialog');
+ dialog.addEventListener('close', function(event) {
+ const selfDialog = this;
+ t.step(() => {
+ closedCount++;
+ assert_equals(selfDialog, dialog);
+ assert_false(dialog.open);
+ assert_false(event.cancelable);
+ event.preventDefault();
+
+ if (closedCount == 1) {
+ dialog.show();
+ dialog.close();
+ assert_equals(closedCount, 1, `dialog's close event handler shouldn't be called synchronously.`);
+ } else if (closedCount == 2) {
+ t.done();
+ }
+ });
+ });
+
+ dialog.show();
+ dialog.close();
+
+ // Verify that preventDefault() didn't cancel closing.
+ assert_false(dialog.open);
+
+ // dialog's close event handler shouldn't be called synchronously.
+ assert_equals(closedCount, 0);
+}, "Test that dialog receives a close event upon closing.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html
new file mode 100644
index 0000000000..9029612b24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: close()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<dialog id="d1">
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d3" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d4" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d5" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ d2 = document.getElementById('d2'),
+ d3 = document.getElementById('d3'),
+ d4 = document.getElementById('d4'),
+ d5 = document.getElementById('d5'),
+ t = async_test("close() fires a close event"),
+ was_queued = false;
+
+ test(function(){
+ d1.close("closedialog");
+ assert_equals(d1.returnValue, "");
+ }, "close() on a <dialog> that doesn't have an open attribute aborts the steps");
+
+ test(function(){
+ assert_true(d2.open);
+ assert_equals(d2.returnValue, "");
+ d2.close("closedialog");
+ assert_false(d2.hasAttribute("open"));
+ assert_equals(d2.returnValue, "closedialog");
+ }, "close() removes the open attribute and set the returnValue to the first argument");
+
+ test(function(){
+ assert_true(d3.open);
+ assert_equals(d3.returnValue, "");
+ d3.returnValue = "foobar";
+ d3.close();
+ assert_false(d3.hasAttribute("open"));
+ assert_equals(d3.returnValue, "foobar");
+ }, "close() without argument removes the open attribute and there's no returnValue");
+
+ d4.onclose = t.step_func_done(function(e) {
+ assert_true(was_queued, "close event should be queued");
+ assert_true(e.isTrusted, "close event is trusted");
+ assert_false(e.bubbles, "close event doesn't bubble");
+ assert_false(e.cancelable, "close event is not cancelable");
+ });
+
+ t.step(function() {
+ d4.close();
+ was_queued = true;
+ })
+
+ test(function(){
+ Object.defineProperty(HTMLDialogElement.prototype, 'returnValue', { set: function(v) { assert_unreached('JS-defined setter returnValue on the prototype was invoked'); }, configurable:true });
+ Object.defineProperty(d5, 'returnValue', { set: function(v) { assert_unreached('JS-defined setter returnValue on the instance was invoked'); }, configurable:true });
+ d5.close('foo');
+ }, "close() should set the returnValue IDL attribute but not the JS property");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html
new file mode 100644
index 0000000000..87a130c6f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-enabled.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog></dialog>
+<script>
+test(function() {
+ dialog = document.querySelector('dialog')
+ assert_true(dialog instanceof HTMLDialogElement);
+}, "The DIALOG element should be recognized");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html
new file mode 100644
index 0000000000..2cd63eb796
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow-double-nested.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>dialog focusing delegation: with two nested shadow trees</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<dialog>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button tabindex="-1">Focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+</dialog>
+
+<script>
+function turnIntoShadowTree(template) {
+ for (const subTemplate of template.content.querySelectorAll(".turn-into-shadow-tree")) {
+ turnIntoShadowTree(subTemplate);
+ }
+
+ const div = document.createElement("div");
+ div.attachShadow({ mode: "open", delegatesFocus: template.classList.contains("delegates-focus") });
+ div.shadowRoot.append(template.content);
+ template.replaceWith(div);
+}
+
+for (const template of document.querySelectorAll(".turn-into-shadow-tree")) {
+ turnIntoShadowTree(template);
+}
+
+for (const method of ["show", "showModal"]) {
+ test(t => {
+ const dialog = document.querySelector("dialog");
+ dialog[method]();
+ t.add_cleanup(() => dialog.close());
+
+ const shadowHostOuter = dialog.querySelector("div");
+ assert_equals(document.activeElement, shadowHostOuter, "document.activeElement");
+
+ const shadowHostInner = shadowHostOuter.shadowRoot.querySelector("div");
+ assert_equals(shadowHostOuter.shadowRoot.activeElement, shadowHostInner, "shadowHostOuter.shadowRoot.activeElement");
+
+ const button = shadowHostInner.shadowRoot.querySelector("[autofocus]");
+ assert_equals(shadowHostInner.shadowRoot.activeElement, button, "shadowHostInner.shadowRoot.activeElement");
+ }, `${method}()`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html
new file mode 100644
index 0000000000..e9ea15516e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html
@@ -0,0 +1,278 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>dialog focus delegation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<!--
+ We focus this one between each test, to ensure that for non-modal dialogs,
+ if there is no focus delegate, it stays focused (instead of causing focus to reset to the body).
+-->
+<button tabindex="-1" id="focus-between-tests">Focus between tests</button>
+
+<dialog data-description="No autofocus, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, sibling before">
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, sibling before">
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus before, no delegatesFocus">
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus before, yes delegatesFocus">
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus after, no delegatesFocus">
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus after, yes delegatesFocus">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling before">
+ <button tabindex="-1">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling before">
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling before">
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1">Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling before">
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling after">
+ <template class="turn-into-shadow-tree">
+ <button tabindex="-1">Focusable</button>
+ <button tabindex="-1" autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Two shadow trees, both delegatesFocus, first tree doesn't have autofocus element, second does">
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button tabindex="-1" autofocus>Focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, slotted target">
+ <template class="turn-into-shadow-tree">
+ <button tabindex="-1">Focusable</button>
+ <slot></slot>
+ <button tabindex="-1">Focusable</button>
+ </template>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Shadowroot on child, no autofocus, no delegatesFocus">
+ <div>
+ <template class="turn-into-shadow-tree">
+ <button tabindex="-1">Focusable</button>
+ </template>
+ </div>
+ <button tabindex="-1" class="focus-me">Focusable</button>
+</dialog>
+
+<script>
+for (const template of document.querySelectorAll(".turn-into-shadow-tree")) {
+ const div = document.createElement("div");
+ div.attachShadow({ mode: "open", delegatesFocus: template.classList.contains("delegates-focus") });
+
+ if (template.classList.contains("autofocus")) {
+ div.setAttribute("autofocus", true);
+ }
+ div.shadowRoot.append(template.content);
+ template.replaceWith(div);
+}
+
+const focusBetweenTests = document.querySelector("#focus-between-tests");
+
+for (const dialog of document.querySelectorAll("dialog")) {
+ for (const method of ["show", "showModal"]) {
+ test(t => {
+ focusBetweenTests.focus();
+
+ dialog[method]();
+ t.add_cleanup(() => dialog.close());
+
+ const expectedFocusOutsideShadowTree = dialog.querySelector(".focus-me");
+ if (expectedFocusOutsideShadowTree) {
+ assert_equals(document.activeElement, expectedFocusOutsideShadowTree);
+ } else {
+ const shadowHost = dialog.querySelector("div");
+ const expectedFocusInsideShadowTree = shadowHost.shadowRoot.querySelector(".focus-me");
+ if (expectedFocusInsideShadowTree) {
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowHost.shadowRoot.activeElement, expectedFocusInsideShadowTree);
+ } else {
+ // There is no focus delegate. Expected result depends on show() vs. showModal().
+ if (method === "show") {
+ assert_equals(document.activeElement, focusBetweenTests);
+ } else {
+ assert_equals(document.activeElement, document.body);
+ }
+ }
+ }
+ }, `${method}: ${dialog.dataset.description}`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html
new file mode 100644
index 0000000000..bf621b640b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-disconnected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test focusing steps when dialog is disconnected</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<input>
+<script>
+test(function() {
+ const outerInput = document.querySelector("input");
+ outerInput.focus();
+ assert_equals(document.activeElement, outerInput,
+ "Focus should be on element we just focused");
+
+ const dialog = document.createElement("dialog");
+ assert_false(dialog.open, "Dialog should initially be closed");
+ assert_false(dialog.hasAttribute('open'), "Dialog should initially be closed");
+
+ const innerInput = document.createElement("input");
+ innerInput.autofocus = true;
+ dialog.append(innerInput);
+
+ dialog.show();
+ this.add_cleanup(() => { dialog.close(); });
+ assert_true(dialog.open, "Disconnected dialog can still be open");
+
+
+ assert_equals(document.activeElement, outerInput, "Focusing steps should not change focus");
+}, "dialog.show(): focusing steps should not change focus on disconnected <dialog>");
+
+test(function() {
+ assert_throws_dom("InvalidStateError", () => {
+ document.createElement("dialog").showModal();
+ });
+}, "dialog.showModal() should throw on disconnected <dialog>");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html
new file mode 100644
index 0000000000..003c456179
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test focusing steps when dialog is inert</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<input id="outer-input">
+<dialog>
+ <input autofocus>
+</dialog>
+<script>
+function test_focusing_steps_with_inert_dialog(test, isModal) {
+ const outerInput = document.querySelector("#outer-input");
+ outerInput.focus();
+ assert_equals(document.activeElement, outerInput,
+ "Focus should be on element we just focused");
+
+ const dialog = document.querySelector("dialog");
+ assert_false(dialog.open, "Dialog should initially be closed");
+
+ dialog.inert = true;
+ test.add_cleanup(() => { dialog.inert = false; });
+
+ if (isModal) {
+ dialog.showModal();
+ test.add_cleanup(() => { dialog.close(); });
+ assert_equals(document.activeElement, document.body,
+ "dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert");
+ } else {
+ dialog.show();
+ test.add_cleanup(() => { dialog.close(); });
+ assert_equals(document.activeElement, outerInput,
+ "dialog.show(): focusing steps should not change focus when dialog is inert");
+ }
+}
+
+test(function() {
+ test_focusing_steps_with_inert_dialog(this, false);
+}, "dialog.show(): focusing steps should not change focus when dialog is inert");
+
+test(function() {
+ test_focusing_steps_with_inert_dialog(this, true);
+}, "dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html
new file mode 100644
index 0000000000..2e8563f761
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-prevent-autofocus.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
+<body>
+<dialog></dialog>
+<script>
+// https://github.com/whatwg/html/issues/4788
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ dialog.close();
+ const input = document.createElement('input');
+ input.autofocus = true;
+ document.body.insertBefore(input, dialog);
+ await waitUntilStableAutofocusState();
+ assert_equals(document.activeElement, document.body,
+ 'Non-dialog autofocus processing should be skipped.');
+}, 'After showing a dialog, non-dialog autofocus processing won\'t work.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html
new file mode 100644
index 0000000000..5934485087
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<meta charset=urf-8>
+<meta name=viewport content="width=device-width,initial-scale=1">
+<title>Test dialog form submission</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<dialog id="favDialog">
+ <form id="dialogForm" method="dialog">
+ <button id="confirmBtn" value="default">Confirm</button>
+ <input id="confirmImgBtn" src="./resources/submit.jpg" width="41"
+ height="41" type="image" alt="Hello">
+ </form>
+ <form method="post">
+ <input id="confirmImgBtn2" src="./resources/submit.jpg" width="41"
+ formmethod="dialog" height="41" type="image" alt="Hello">
+ </form>
+</dialog>
+<script>
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.click();
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_equals(dialog.returnValue, "default", "Return the default value");
+}, 'click the form submission button should close the dialog');
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+ button.click();
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_equals(dialog.returnValue, "sushi", "Return the updated value");
+}, 'form submission should return correct value');
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.removeAttribute("value");
+ button.click();
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_not_equals(dialog.returnValue, undefined, "returnValue should not be set");
+}, "no returnValue when there's no result.");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('input');
+ let expectedReturnValue = "";
+ button.addEventListener('click', function(event) {
+ expectedReturnValue = event.offsetX + "," + event.offsetY;
+ });
+ await test_driver.click(button);
+
+ assert_false(dialog.open, "dialog should be closed now");
+ assert_not_equals(dialog.returnValue, "", "returnValue shouldn't be empty string");
+ assert_equals(dialog.returnValue, expectedReturnValue, "returnValue should be the offsets of the click");
+}, "input image button should return the coordinates");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+ const button = document.getElementById('confirmImgBtn2');
+ await test_driver.click(button);
+ assert_false(dialog.open, "dialog should be closed now");
+}, "formmethod attribute should use dialog form submission");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.returnValue = "";
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+
+ const dialogForm = document.getElementById('dialogForm');
+ dialogForm.onsubmit = function() {
+ dialog.close();
+ }
+
+ button.click();
+ assert_false(dialog.open, "dialog should be closed now");
+ // If the submission request got processed, the returnValue should change
+ // to "sushi" because that's the value of the submitter
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+}, "closing the dialog while submitting should stop the submission");
+
+promise_test(async () => {
+ const dialog = document.querySelector('dialog');
+ dialog.returnValue = undefined;
+ dialog.showModal();
+
+ let submitEvent = false;
+ const dialogForm = document.getElementById('dialogForm');
+ dialogForm.onsubmit = function() {
+ submitEvent = true;
+ assert_false(dialog.open, "dialog should be closed");
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+ };
+
+ const button = document.querySelector('button');
+ button.value = "sushi";
+ button.onclick = function() {
+ dialogForm.submit();
+ assert_false(dialog.open, "dialog should be closed now");
+ // The returnValue should be "" because there is no submitter
+ assert_equals(dialog.returnValue, "", "returnValue shouldn be empty string");
+ };
+
+ button.click();
+ assert_true(submitEvent, "Should have submit event");
+ assert_false(dialog.open, "dialog should be closed");
+ assert_equals(dialog.returnValue, "", "dialog's returnValue remains the same");
+}, "calling form.submit() in click handler of submit button should start the submission synchronously");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html
new file mode 100644
index 0000000000..864420b9d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-inert.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ body { margin: 0 }
+ dialog {
+ width: 100%;
+ height: 100%;
+ max-width: 100%;
+ max-height: 100%;
+ box-sizing: border-box;
+ padding: 0;
+ }
+ dialog::backdrop {
+ display: none;
+ }
+</style>
+<dialog id=dialog>Something</dialog>
+<script>
+test(function() {
+ let dialog = document.getElementById("dialog");
+ dialog.showModal();
+ assert_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog is hittable by default",
+ );
+ dialog.inert = true;
+ assert_not_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog becomes inert dynamically",
+ );
+ dialog.close();
+ dialog.showModal();
+ assert_not_equals(
+ document.elementFromPoint(10, 10),
+ dialog,
+ "Dialog remains inert after open",
+ );
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html
new file mode 100644
index 0000000000..4a50b13c87
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-keydown-preventDefault.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cancel event with preventDefault on keydown event for dialog element</title>
+ <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>
+ <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=227534">
+ <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322947">
+</head>
+<body>
+<p>Test cancel event with preventDefault on keydown event for dialog element</p>
+<dialog>
+ <p>Hello World</p>
+</dialog>
+<script>
+ setup({ single_test: true });
+
+ var hasCancelEventFired = false;
+
+ const dialog = document.querySelector("dialog");
+
+ const verify = () => {
+ assert_false(hasCancelEventFired, "cancel should not be fired");
+ assert_true(hasKeydownEventFired, "document level keydown event should be fired");
+ done();
+ };
+
+ dialog.addEventListener("cancel", function(event) {
+ hasCancelEventFired = true;
+ });
+
+ document.addEventListener("keydown", function(event) {
+ hasKeydownEventFired = true;
+ event.preventDefault();
+ step_timeout(function() {
+ verify();
+ }, 0);
+ });
+ dialog.showModal();
+ test_driver.send_keys(document.documentElement, "\uE00C"); // ESC key
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html
new file mode 100644
index 0000000000..fe3fab8ebb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-not-in-tree-crash.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ const dialog = document.createElement("dialog");
+ dialog.show();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html
new file mode 100644
index 0000000000..79120d07eb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open-2.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=90931">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog id=mydialog>It's my dialog.</dialog>
+
+<script>
+test(() => {
+ const dialog = document.getElementById('mydialog');
+ let computedStyle = window.getComputedStyle(dialog, null);
+ assert_equals(computedStyle.getPropertyValue('display'), 'none');
+
+ dialog.show();
+ computedStyle = window.getComputedStyle(dialog, null);
+ assert_equals(computedStyle.getPropertyValue('display'), 'block');
+
+ dialog.close();
+ computedStyle = window.getComputedStyle(dialog, null);
+
+ assert_equals(computedStyle.getPropertyValue('display'), 'none');
+ dialog.close();
+}, "Tests that dialog is visible after show() is called and not visible after close() is called.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html
new file mode 100644
index 0000000000..e1f4c6ab82
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-open.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>dialog element: open</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#dom-dialog-open">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog id="d1">
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<script>
+ var d1 = document.getElementById('d1');
+ var d2 = document.getElementById('d2');
+
+ test(function(){
+ assert_false(d1.open);
+ assert_true(d2.open);
+ }, "On getting, the IDL open attribute must return true if the content open attribute is set, and false if it is absent.");
+
+ test(function(){
+ d1.open = true;
+ assert_true(d1.hasAttribute("open"));
+ d2.open = false;
+ assert_false(d2.hasAttribute("open"));
+ }, "On setting, the content open attribute must be removed if the IDL open attribute is set to false, and must be present if the IDL open attribute is set to true.");
+
+ async_test(function(t){
+ d2.open = true;
+ assert_true(d2.hasAttribute("open"));
+ d2.onclose = t.unreached_func("close event should not be fired when just setting the open attribute");
+ d2.open = false;
+ assert_false(d2.hasAttribute("open"));
+
+ // close event is async, give it a chance to be fired
+ t.step_timeout(function() {
+ t.done();
+ }, 0);
+ }, "On setting it to false, the close event should not be fired");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html
new file mode 100644
index 0000000000..2a80de65a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-return-value.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<dialog></dialog>
+<script>
+test(function() {
+ dialog = document.querySelector('dialog');
+ assert_equals(dialog.returnValue, '');
+
+ dialog.returnValue = 'Setting value directly';
+ assert_equals(dialog.returnValue, 'Setting value directly');
+
+ dialog.returnValue = null;
+ assert_equals(dialog.returnValue, 'null');
+
+ dialog.returnValue = '';
+ assert_equals(dialog.returnValue, '');
+
+ dialog.returnValue = 7;
+ assert_equals(dialog.returnValue, '7');
+
+ dialog.show();
+ dialog.close('Return value set from close()');
+ assert_equals(dialog.returnValue, 'Return value set from close()');
+
+ dialog.show();
+ dialog.close('');
+ assert_equals(dialog.returnValue, '');
+
+ dialog.show();
+ dialog.close(null);
+ assert_equals(dialog.returnValue, 'null');
+
+ dialog.returnValue = 'Should not change because no argument to close()';
+ dialog.show();
+ dialog.close();
+ assert_equals(dialog.returnValue, 'Should not change because no argument to close()');
+
+ dialog.returnValue = 'Should not change because of undefined argument to close()';
+ dialog.show();
+ dialog.close(undefined);
+ assert_equals(dialog.returnValue, 'Should not change because of undefined argument to close()');
+
+ dialog.returnValue = 'Should not change because of no-op close()';
+ dialog.close('blah');
+ assert_equals(dialog.returnValue, 'Should not change because of no-op close()');
+}, "Tests dialog.returnValue is settable and returns the last value set.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html
new file mode 100644
index 0000000000..54c2edab6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-inert-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<iframe id="frame"></iframe>
+<script>
+ async_test(function(t) {
+ onload = t.step_func(() => {
+ const host = document.createElement("div");
+ frame.appendChild(host);
+ frame.contentDocument.body.innerHTML = "<dialog></dialog>";
+ document.body.offsetTop;
+ const root = host.attachShadow({mode: 'open'});
+ root.innerHTML = "<content>";
+ const dialog = frame.contentDocument.querySelector("dialog");
+ dialog.showModal();
+ t.done();
+ });
+ }, "Dialog.showModal() called when we have a dirty shadow distribution should not crash.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html
new file mode 100644
index 0000000000..c2350c3042
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-remove.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>dialog element: removing from document after showModal()</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-showmodal">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#removing-steps">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog></dialog>
+<script>
+async_test(t => {
+ const dialog = document.querySelector('dialog')
+ dialog.showModal()
+ assert_true(dialog.open)
+ // The dialog element is now in top layer. Removing it should synchronously
+ // remove it from top layer, but should leave it in a strange limbo state.
+ dialog.addEventListener('close', t.unreached_func('close event'))
+ dialog.remove()
+ assert_true(dialog.open)
+ // if an event was queued, it would fire before this timeout
+ step_timeout(t.step_func_done(() => {
+ assert_true(dialog.open)
+ // pass if no close event was fired
+ }))
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html
new file mode 100644
index 0000000000..c511631f9f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>dialog element: showModal()</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<button id="b0">OK</button>
+<dialog id="d1">
+ <p>foobar</p>
+ <button id="b1">OK</button>
+</dialog>
+<dialog id="d2" open>
+ <p>foobar</p>
+ <button>OK</button>
+</dialog>
+<dialog id="d3">
+ <p>foobar</p>
+ <button id="b3">OK</button>
+</dialog>
+<dialog id="d4">
+ <p>foobar</p>
+ <button id="b4">OK</button>
+</dialog>
+<dialog id="d5">
+ <p>foobar</p>
+ <button id="b5">OK</button>
+</dialog>
+<dialog id="d6"></dialog>
+<dialog id="d7">
+ <input id="i71" value="foobar">
+ <input id="i72" value="foobar">
+ <button id="b7">OK</button>
+</dialog>
+<dialog id="d8">
+ <input id="i81" value="foobar">
+ <input id="i82" value="foobar" autofocus>
+ <button id="b8">OK</button>
+</dialog>
+<dialog id="d9"></dialog>
+<dialog id="d10"></dialog>
+<dialog id="d11"></dialog>
+<script>
+ var d1 = document.getElementById('d1'),
+ d2 = document.getElementById('d2'),
+ d3 = document.getElementById('d3'),
+ d4 = document.getElementById('d4'),
+ d5 = document.getElementById('d5'),
+ d6 = document.getElementById('d6'),
+ d7 = document.getElementById('d7'),
+ d8 = document.getElementById('d8'),
+ d9 = document.getElementById('d9'),
+ d10 = document.getElementById('d10'),
+ d11 = document.getElementById('d11'),
+ b0 = document.getElementById('b0'),
+ b1 = document.getElementById('b1'),
+ b3 = document.getElementById('b3'),
+ b4 = document.getElementById('b4'),
+ b5 = document.getElementById('b5');
+
+ test(function(){
+ assert_false(d1.open);
+ assert_false(d1.hasAttribute("open"));
+ assert_equals(getComputedStyle(d1).display, "none");
+ d1.showModal();
+ this.add_cleanup(function() { d1.close(); });
+ assert_true(d1.open);
+ assert_equals(d1.getAttribute("open"), "");
+ assert_equals(getComputedStyle(d1).display, "block");
+ assert_equals(document.activeElement, b1);
+ });
+
+ test(function(){
+ this.add_cleanup(function() { d2.close(); });
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ d2.showModal();
+ });
+ }, "showModal() on a <dialog> that already has an open attribute throws an InvalidStateError exception");
+
+ test(function(){
+ d9.showModal();
+ this.add_cleanup(function() { d9.close(); });
+ assert_true(d9.open);
+ d9.removeAttribute("open");
+ assert_false(d9.open);
+ d9.showModal();
+ assert_true(d9.open);
+ }, "showModal() on a <dialog> after initial showModal() and removing the open attribute");
+
+ test(function(){
+ var d = document.createElement("dialog");
+ this.add_cleanup(function() { d.close(); });
+ assert_throws_dom("INVALID_STATE_ERR", function() {
+ d.showModal();
+ });
+ }, "showModal() on a <dialog> not in a Document throws an InvalidStateError exception");
+
+ test(function(){
+ assert_false(d3.open);
+ assert_false(d4.open);
+ assert_false(d5.open);
+ d3.showModal();
+ this.add_cleanup(function() { d3.close(); });
+ d4.showModal();
+ this.add_cleanup(function() { d4.close(); });
+ d5.showModal();
+ this.add_cleanup(function() { d5.close(); });
+ assert_true(d3.open);
+ assert_true(d4.open);
+ assert_true(d5.open);
+ }, "when opening multiple dialogs, only the newest one is non-inert");
+
+ test(function(){
+ assert_false(d6.open);
+ d6.showModal();
+ this.add_cleanup(function() { d6.close(); });
+ assert_true(d6.open);
+ assert_equals(document.activeElement, document.body);
+ }, "opening dialog without focusable children");
+
+ test(function(){
+ assert_false(d7.open);
+ d7.showModal();
+ this.add_cleanup(function() { d7.close(); });
+ assert_true(d7.open);
+ assert_equals(document.activeElement, document.getElementById("i71"));
+ }, "opening dialog with multiple focusable children");
+
+ test(function(){
+ assert_false(d8.open);
+ d8.showModal();
+ this.add_cleanup(function() { d8.close(); });
+ assert_true(d8.open);
+ assert_equals(document.activeElement, document.getElementById("i82"));
+ }, "opening dialog with multiple focusable children, one having the autofocus attribute");
+
+ test(function(){
+ assert_false(d10.open);
+ assert_false(d11.open);
+ d10.showModal();
+ this.add_cleanup(function() { d10.close(); });
+ d11.showModal();
+ this.add_cleanup(function() { d11.close(); });
+ var rect10 = d10.getBoundingClientRect();
+ var rect11 = d11.getBoundingClientRect();
+
+ // The two <dialog>s are both in top layer, with the same position/size.
+ assert_equals(rect10.left, rect11.left);
+ assert_equals(rect10.top, rect11.top);
+ assert_equals(rect10.width, rect11.width);
+ assert_equals(rect10.height, rect11.height);
+
+ var pointX = rect10.left + rect10.width / 2,
+ pointY = rect10.top + rect10.height / 2;
+ function topElement() {
+ return document.elementFromPoint(pointX, pointY);
+ }
+
+ // d11 was most recently openened, and thus on top.
+ assert_equals(topElement(), d11);
+
+ // Removing the open attribute and running through the showModal() algorithm
+ // again should not promote d10 to the top.
+ d10.removeAttribute("open");
+ assert_equals(topElement(), d11);
+ d10.showModal();
+ assert_equals(topElement(), d11);
+
+ // Closing d11 with close() should cause d10 to be the topmost element.
+ d11.close();
+ assert_equals(topElement(), d10);
+ }, "when opening multiple dialogs, the most recently opened is rendered on top");
+
+ test(function() {
+ assert_false(d11.open);
+ d11.parentNode.removeChild(d11);
+ assert_throws_dom("INVALID_STATE_ERR", () => d11.showModal());
+
+ const doc = document.implementation.createHTMLDocument();
+ doc.body.appendChild(d11);
+ this.add_cleanup(() => document.body.append(d11));
+ assert_false(d11.open);
+ d11.showModal();
+ assert_true(d11.open);
+ this.add_cleanup(() => d11.close());
+ }, "Although the document is not attached to any pages, showModal() should execute as normal.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html
new file mode 100644
index 0000000000..4b31dc7062
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<body>
+Test that ::backdrop is not shown for non-open or non-modal dialogs.
+The test passes if there is no red shown.
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html
new file mode 100644
index 0000000000..fec4ba8587
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialogs-with-no-backdrop.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<link rel="match" href="dialogs-with-no-backdrop-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+
+#display-none-backdrop::backdrop {
+ display: none;
+}
+</style>
+<body>
+Test that ::backdrop is not shown for non-open or non-modal dialogs.
+The test passes if there is no red shown.
+<dialog id="never-opened-dialog"></dialog>
+<dialog id="display-none-dialog" style="display: none"></dialog>
+<dialog id="non-modal-dialog" style="visibility: hidden"></dialog>
+<dialog id="display-none-backdrop" style="visibility: hidden"></dialog>
+<dialog id="closed-dialog"></dialog>
+<dialog id="removed-dialog"></dialog>
+<script>
+document.getElementById('display-none-dialog').showModal();
+document.getElementById('non-modal-dialog').show();
+document.getElementById('display-none-backdrop').showModal();
+
+var closedDialog = document.getElementById('closed-dialog');
+closedDialog.showModal();
+closedDialog.close();
+
+var removedDialog = document.getElementById('removed-dialog');
+removedDialog.showModal();
+removedDialog.parentNode.removeChild(removedDialog);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html
new file mode 100644
index 0000000000..535ac93560
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<style>
+#non-modal {
+ position: static;
+}
+</style>
+<p>Test that a non-top layer element doesn't share style with a top layer
+element. The test passes if you see two boxes.</p>
+<dialog id="non-modal" open></dialog>
+<dialog id="modal"></dialog>
+<script>
+document.querySelector('#modal').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html
new file mode 100644
index 0000000000..efbbab010a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<link rel="match" href="dont-share-style-to-top-layer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ position: static;
+}
+</style>
+<p>Test that a non-top layer element doesn't share style with a top layer
+element. The test passes if you see two boxes.</p>
+<dialog open></dialog>
+<dialog id="modal"></dialog>
+<script>
+document.querySelector('#modal').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html
new file mode 100644
index 0000000000..c0b64e68bd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.green {
+ color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/106538">106538</a>: Top layer fails for inline elements</p>
+<p>This tests that position 'static' no longer computes to 'absolute' for an
+element that has been removed from the top layer. The test passes if you see
+a single line of text.</p>
+<span class="green">This is the span.</span>
+<span class="green">This is the dialog following it.</span>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html
new file mode 100644
index 0000000000..d78051a9b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/element-removed-from-top-layer-has-original-position.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="element-removed-from-top-layer-has-original-position-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+.green {
+ color: green;
+}
+
+#right-dialog {
+ display: inline;
+ position: static;
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/106538">106538</a>: Top layer fails for inline elements</p>
+<p>This tests that position 'static' no longer computes to 'absolute' for an
+element that has been removed from the top layer. The test passes if you see
+a single line of text.</p>
+<span class="green">This is the span.</span>
+<dialog class="green" id="right-dialog">This is the dialog following it.</dialog>
+<script>
+var dialog = document.getElementById('right-dialog');
+dialog.showModal();
+dialog.close();
+dialog.show();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html
new file mode 100644
index 0000000000..98835cb795
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<div style="contain: layout">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html
new file mode 100644
index 0000000000..d973c0876d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fixed-position-cb-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<style>
+</style>
+<body>
+<div class="pseudodialog" style="position: fixed">
+Dialog should be centered.
+</div>
+<div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html
new file mode 100644
index 0000000000..fe625f1c9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<svg>
+ <foreignObject>
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+ </foreignObject>
+</svg>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.html
new file mode 100644
index 0000000000..58627443da
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<div style="scale: 1">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.html
new file mode 100644
index 0000000000..14f4391e6b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<title>Test that a fixed positioned child of a dialog is aligned to the viewport</title>
+<head>
+<link rel="match" href="fixed-position-child-with-fixed-position-cb-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<div style="will-change: transform">
+ <dialog id="dialog">
+ Dialog should be centered.
+ <div style="position: fixed; top: 0px; left: 0px">This fixed positioned element is aligned to viewport top-left.</div>
+ </dialog>
+</div>
+<script>
+dialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html
new file mode 100644
index 0000000000..d66d45527a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-after-close.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Test focus is moved to the previously focused element when dialog is closed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<input />
+<dialog>
+ <button id="button1">This is a button1</button>
+ <button id="button2">This is a button2</button>
+ <button id="button3">This is a button3</button>
+</dialog>
+<script>
+
+// Test focus is moved to the previously focused element
+function test_move_to_previously_focused(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+}
+
+// Test focus is moved to the previously focused element with some complex dialog usage
+async function test_move_to_previously_focused_with_complex_dialog_usage(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ const button1 = document.getElementById("button1");
+ const button2 = document.getElementById("button2");
+ const button3 = document.getElementById("button3");
+
+ await test_driver.click(button1);
+ await test_driver.click(button2);
+ await test_driver.click(button3);
+
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+}
+
+// Test focus is moved to the previously focused element even if that element moved in between
+function test_element_move_in_between_show_close(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+
+ assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
+
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ document.body.appendChild(input);
+ assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
+
+ dialog.close();
+ assert_equals(document.activeElement, input, "Focus should be restored to previously focused input");
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+}
+
+// Test focus is moved to the previously focused element even if that element moved to shadow root in between
+function test_element_move_to_shadow_root_in_between_show_close(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+
+ assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
+
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ const shadowHost = document.createElement("div");
+ const shadowRoot = shadowHost.attachShadow({mode: "open"});
+ shadowRoot.appendChild(input);
+ document.body.appendChild(shadowHost);
+
+ assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
+
+ dialog.close();
+ assert_equals(shadowRoot.activeElement, input, "Focus should be restored to previously focused input");
+ assert_equals(document.activeElement, shadowHost, "document.activeElement should be previously focused input's shadow DOM host");
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+ shadowHost.remove();
+}
+
+// Test focus is moved to <body> if the previously focused
+// element can't be focused
+function test_move_to_body_if_fails(showModal) {
+ const input = document.querySelector("input");
+ input.focus();
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+ input.remove();
+ assert_equals(document.activeElement, document.body);
+
+ // Clean up
+ document.body.insertBefore(input, dialog);
+}
+
+// Test focus is moved to shadow host if the previously
+// focused element is a shadow node.
+function test_move_to_shadow_host(showModal) {
+ const shadowHost = document.createElement("div");
+
+ const shadowRoot = shadowHost.attachShadow({mode: "open"});
+ shadowRoot.appendChild(document.createElement("input"));
+
+ document.body.appendChild(shadowHost);
+ const inputElement = shadowRoot.querySelector("input");
+ inputElement.focus();
+
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowRoot.activeElement, inputElement);
+
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+ dialog.close();
+
+ assert_equals(document.activeElement, shadowHost);
+ assert_equals(shadowRoot.activeElement, inputElement);
+
+ // Clean up
+ shadowHost.remove();
+}
+
+// Test moving the focus doesn't scroll the viewport
+async function test_move_focus_dont_scroll_viewport(showModal) {
+ const outViewPortButton = document.createElement("button");
+ outViewPortButton.style.top = (window.innerHeight + 10).toString() + "px";
+ outViewPortButton.style.position = "absolute";
+ document.body.appendChild(outViewPortButton);
+
+ await new Promise(resolve => {
+ document.addEventListener("scroll", () => step_timeout(resolve, 0));
+ outViewPortButton.focus();
+ });
+
+ // Since the outViewPortButton is focused, so the viewport should be
+ // scrolled to it
+ assert_true(document.documentElement.scrollTop > 0 );
+
+ const dialog = document.querySelector("dialog");
+ if (showModal) {
+ dialog.showModal();
+ } else {
+ dialog.show();
+ }
+
+ window.scrollTo(0, 0);
+ assert_equals(document.documentElement.scrollTop, 0);
+
+ dialog.close();
+ assert_equals(document.documentElement.scrollTop, 0);
+
+ assert_equals(document.activeElement, outViewPortButton);
+}
+
+test(() => {
+ test_move_to_previously_focused(true);
+ test_move_to_previously_focused(false);
+}, "Focus should be moved to the previously focused element (Simple dialog usage)");
+
+promise_test(async () => {
+ await test_move_to_previously_focused_with_complex_dialog_usage(true);
+ await test_move_to_previously_focused_with_complex_dialog_usage(false);
+}, "Focus should be moved to the previously focused element (Complex dialog usage)");
+
+test(() => {
+ test_element_move_in_between_show_close(true);
+ test_element_move_in_between_show_close(false);
+}, "Focus should be moved to the previously focused element even if it has moved in between show/close");
+
+test(() => {
+ test_element_move_to_shadow_root_in_between_show_close(true);
+ test_element_move_to_shadow_root_in_between_show_close(false);
+}, "Focus should be moved to the previously focused element even if it has moved to shadow DOM root in between show/close");
+
+test(() => {
+ test_move_to_body_if_fails(true);
+ test_move_to_body_if_fails(false);
+}, "Focus should be moved to the body if the previously focused element is removed");
+
+test(() => {
+ test_move_to_shadow_host(true);
+ test_move_to_shadow_host(false);
+}, "Focus should be moved to the shadow DOM host if the previouly focused element is a shadow DOM node");
+
+promise_test(async () => {
+ await test_move_focus_dont_scroll_viewport(true);
+ await test_move_focus_dont_scroll_viewport(false);
+}, "Focus should not scroll if the previously focused element is outside the viewport");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html
new file mode 100644
index 0000000000..cd23c32a06
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/green-dialog-and-backdrop.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+body { background: red; }
+
+.backdrop {
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.backdrop,
+.pseudodialog {
+ background: green;
+}
+</style>
+<body>
+<div class="backdrop"></div>
+<div class="pseudodialog">PASS if no red shows</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html
new file mode 100644
index 0000000000..b3b0c0a929
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-does-not-match-disabled-selector.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+button {
+ color: green;
+}
+
+button:disabled {
+ color: red;
+}
+
+.trigger-style-recalc {
+ /* No change, we just need a new style recalculation. */
+ font-weight:bold;
+}
+</style>
+</head>
+<body style="color: green">
+<button>The test passes if this is in green.</button>
+<dialog></dialog>
+<script>
+"use strict";
+test(function() {
+ document.querySelector('dialog').showModal();
+ var button = document.querySelector('button');
+ button.classList.add('trigger-style-recalc');
+ var color = document.defaultView.getComputedStyle(button).getPropertyValue('color');
+ assert_equals(color, 'rgb(0, 128, 0)');
+}, "Tests inert elements do not match the :disabled selector.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html
new file mode 100644
index 0000000000..2ccc133285
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-focus-in-frames.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=242848">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe height=400 width=600 id="main-iframe">
+ <frameset rows="*" cols="50,50">
+ <frame src="resources/inert-focus-in-frames-frame1.html">
+ <frame src="resources/inert-focus-in-frames-frame2.html">
+ </frameset>
+</iframe>
+
+<script>
+let framesLoadedResolver = null;
+const framesLoadedPromise = new Promise(resolve => framesLoadedResolver = resolve);
+framesLoaded = 0;
+numFrames = 4;
+
+function frameLoaded() {
+ framesLoaded++;
+ if (framesLoaded == numFrames)
+ framesLoadedResolver();
+}
+var mainIframe = document.getElementById('main-iframe');
+mainIframe.contentDocument.write(mainIframe.textContent);
+mainIframe.contentDocument.close();
+mainIframe.contentWindow.frames[1].window.onload = frameLoaded;
+window.onload = frameLoaded;
+
+promise_test(async () => {
+ await framesLoadedPromise;
+
+ function testFocus(element, expectFocus) {
+ let focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ if (expectFocus) {
+ assert_equals(focusedElement, element, element.id);
+ } else {
+ assert_not_equals(focusedElement, element, element.id);
+ }
+ }
+
+ // Opening a modal dialog in frame1. It blocks other nodes in its document.
+ const frame1 = mainIframe.contentWindow.frames[0].document;
+ frame1.querySelector('dialog').showModal();
+
+ testFocus(frame1.querySelector('.target'), false);
+ const iframe = frame1.querySelector('#iframe1').contentDocument;
+ testFocus(iframe.querySelector('.target'), true);
+
+ // Even a modal dialog in the iframe is blocked by the modal dialog in the parent frame1.
+ iframe.querySelector('dialog').showModal();
+ testFocus(iframe.querySelector('button'), false);
+
+ // An iframe within a modal dialog can still be focused.
+ var dialogIframe = frame1.querySelector('#iframe-in-dialog').contentDocument;
+ testFocus(dialogIframe.querySelector('.target'), true);
+
+ // A modal dialog does not block nodes in a sibling frame.
+ var frame2 = mainIframe.contentWindow.frames[1].document;
+ testFocus(frame2.querySelector('.target'), true);
+
+ // Closing the dialog in frame1. The modal dialog in the iframe does not block nodes in its parent.
+ frame1.querySelector('dialog').close();
+ testFocus(iframe.querySelector('.target'), false);
+ testFocus(frame1.querySelector('.target'), true);
+
+}, 'Tests inert node focusing across frames and iframes.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
new file mode 100644
index 0000000000..5ee4113985
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, click on all the "Click me"s.
+The test fails if you see red.
+</p>
+
+<style>
+dialog {
+ width: 50px;
+}
+</style>
+
+<a id="a" href="javascript:void(0)">Click me</a>
+<button id="button">Click me</button>
+<div id="div" style="background-color: blue; width: 50px; height: 50px">Click meeee</div>
+<span id="span">Click me</span>
+<div id="dialog-parent" style="width: 50px; height: 50px">
+ <span id="dialog-sibling">Click meeee</span>
+ <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ function eventFiredOnInertElement(e) {
+ e.target.style.background = 'red';
+ inertElementFiredOn = true;
+ }
+
+ inertElements = ['a', 'button', 'div', 'span']
+ inertElements.forEach(function(id) {
+ element = document.getElementById(id);
+ element.addEventListener('click', eventFiredOnInertElement);
+ element.addEventListener('mousemove', eventFiredOnInertElement);
+ });
+
+ document.addEventListener('click', function(e) {
+ document.firedOn = true;
+ });
+
+ document.getElementById('dialog-parent').addEventListener('click', function(e) {
+ e.target.firedOn = true;
+ });
+
+ document.querySelector('dialog').showModal();
+ for (const id of inertElements) {
+ expectedTarget = document;
+ if (id == 'dialog-sibling')
+ expectedTarget = document.getElementById('dialog-parent')
+ element = document.getElementById(id);
+ inertElementFiredOn = false;
+ expectedTarget.firedOn = false;
+ await clickOn(element);
+ assert_false(inertElementFiredOn, 'clicking on ' + id);
+ assert_true(expectedTarget.firedOn, 'clicking on ' + id);
+ }
+}, 'Tests that inert inlines do not receive mouse events.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
new file mode 100644
index 0000000000..05f4069d78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=242848">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<label for="submit">Label for Submit</label>
+<dialog>
+ <input id="text" type="text">
+ <input id="submit" type="submit">
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ document.querySelector('dialog').showModal();
+ document.querySelector('#text').focus();
+
+ label = document.querySelector('label');
+ label.focus();
+ assert_equals(document.activeElement, document.querySelector('#submit'),
+ 'label.focus() should send focus to the target.');
+
+ await clickOn(label);
+ assert_equals(document.activeElement, document.body,
+ 'Clicking the label should be the same as clicking the document body.');
+}, 'Tests focusing of an inert label for a non-inert target.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html
new file mode 100644
index 0000000000..1b757ecf62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body p, span {
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>Test that inert nodes are not painted as being selected. The test passes if
+none of the text outside the dialog is highlighted when selected.</p>
+
+<p>Although not shown as selected, the inert nodes are in window.getSelection()
+and copied to the clipboard, which is the same behavior as user-select:
+none (crbug.com/147490).</p>
+
+<br><span>This text shouldn't be highlighted as selected.</span>
+
+<dialog>
+ <div id="selectable">I'm selectable.</div>
+</dialog>
+
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+selectable = document.querySelector('#selectable');
+window.getSelection().selectAllChildren(selectable);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html
new file mode 100644
index 0000000000..f6db38ed72
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-not-highlighted.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="inert-node-is-not-highlighted-ref.html">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>Test that inert nodes are not painted as being selected. The test passes if
+none of the text outside the dialog is highlighted when selected.</p>
+
+<p>Although not shown as selected, the inert nodes are in window.getSelection()
+and copied to the clipboard, which is the same behavior as user-select:
+none (crbug.com/147490).</p>
+
+<br> <!-- Needed to the trigger the bug. -->
+This text shouldn't be highlighted as selected.
+
+<dialog>
+ <div id="selectable">I'm selectable.</div>
+</dialog>
+
+<script>
+dialog = document.querySelector('dialog');
+dialog.showModal();
+document.execCommand('SelectAll');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
new file mode 100644
index 0000000000..9141a383b0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<span id="not-editable" contenteditable>I'm not editable while the dialog is showing.</span>
+<dialog>
+ <span id="editable" contenteditable>I'm editable.</span>
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ let absoluteTop = 0;
+ let absoluteLeft = 0;
+ for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+ absoluteLeft += parentNode.offsetLeft;
+ absoluteTop += parentNode.offsetTop;
+ }
+
+ const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+ const y = Math.round(absoluteTop + element.offsetHeight / 2);
+ const actions = new test_driver.Actions()
+ .pointerMove(x, y)
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ dialog = document.querySelector('dialog');
+ dialog.showModal();
+ notEditable = document.querySelector('#not-editable');
+ editable = document.querySelector('#editable');
+
+ await clickOn(notEditable);
+ oldValue = notEditable.textContent;
+ await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+ assert_equals(notEditable.textContent, oldValue);
+
+ await clickOn(editable);
+ oldValue = editable.textContent;
+ await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+ assert_not_equals(editable.textContent, oldValue);
+
+ notEditable.remove();
+ editable.remove();
+}, 'Test that inert nodes cannot be edited. The test passes if the only text you can edit is in the dialog.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html
new file mode 100644
index 0000000000..56f31f3592
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html id="html" tabindex="1">
+<head>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#blocked-by-a-modal-dialog">
+<meta name="assert" content="Checks that, when opening modal dialogs, inert nodes are not focusable.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body id="body" tabindex="1">
+<dialog id="top-dialog" tabindex="1" style="width: 100px; top: 30px"><button id="top-dialog-button">I get focus</button></dialog>
+<dialog id="bottom-dialog" tabindex="-1" style="width: 100px; bottom: 30px"><button id="bottom-dialog-button">I don't get focus.</button></dialog>
+<div id="container">
+ <input id="text" type="text">
+ <input id="datetime" type="datetime">
+ <input id="color" type="color">
+ <select id="select">
+ <optgroup id="optgroup">
+ <option id="option">Option</option>
+ </optgroup>
+ </select>
+ <div id="contenteditable-div" contenteditable>I'm editable</div>
+ <span id="tabindex-span" tabindex="0">I'm tabindexed.</div>
+ <embed id="embed" type="application/x-blink-test-plugin" width=100 height=100></embed>
+ <a id="anchor" href="">Link</a>
+</div>
+<script>
+"use strict";
+// The test passses if only the topmost dialog and its button are focusable.
+
+function testFocus(element, expectFocus) {
+ test(function() {
+ var focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ var theElement = element;
+ if (expectFocus) {
+ assert_equals(focusedElement, theElement);
+ } else {
+ assert_not_equals(focusedElement, theElement);
+ }
+ }, `#${CSS.escape(element.id)} is ${expectFocus ? "" : "not "} focusable`);
+}
+
+function testTree(element, expectFocus) {
+ if (element.nodeType == Node.ELEMENT_NODE)
+ testFocus(element, expectFocus);
+ var childNodes = element.childNodes;
+ for (var i = 0; i < childNodes.length; i++)
+ testTree(childNodes[i], expectFocus);
+}
+
+var bottomDialog = document.getElementById('bottom-dialog');
+var topDialog = document.getElementById('top-dialog');
+setup(function() {
+ bottomDialog.showModal();
+ topDialog.showModal();
+ add_completion_callback(function() {
+ topDialog.close();
+ bottomDialog.close();
+ });
+});
+
+testFocus(document.documentElement, false);
+testFocus(document.body, false);
+testTree(topDialog, true);
+testTree(bottomDialog, false);
+testTree(document.getElementById('container'), false);
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
new file mode 100644
index 0000000000..2889e1e90a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+Here is a text node you can't select while the dialog is open.
+<dialog>I'm selectable.</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+ document.execCommand('SelectAll');
+ assert_equals(window.getSelection().toString(), "I'm selectable.");
+}, 'Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html
new file mode 100644
index 0000000000..579aca7775
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Hit-testing with SVG made inert by modal dialog</title>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
+<meta assert="assert" content="SVG made inert by modal dialog should be unreachable with hit-testing">
+<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>
+
+<div id="wrapper">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
+ <rect width="500" height="500" id="target" fill="red">
+ </svg>
+</div>
+
+<dialog id="dialog">Content behind the open modal dialog should not be clickable</dialog>
+
+<style>
+dialog::backdrop {
+ display: none;
+}
+</style>
+
+<script>
+const dialog = document.getElementById("dialog");
+const wrapper = document.getElementById("wrapper");
+const target = document.getElementById("target");
+
+promise_test(async function() {
+ dialog.showModal();
+ this.add_cleanup(() => dialog.close());
+
+ let reachedTarget = false;
+ target.addEventListener("mousedown", () => {
+ reachedTarget = true;
+ }, { once: true });
+
+ let wrapperRect = wrapper.getBoundingClientRect();
+ await new test_driver.Actions()
+ .pointerMove(wrapperRect.x + 1, wrapperRect.y + 1, { origin: "viewport" })
+ .pointerDown()
+ .send();
+
+ assert_false(target.matches(":active"), "target is not active");
+ assert_false(target.matches(":hover"), "target is not hovered");
+ assert_false(reachedTarget, "target didn't get event");
+}, "Hit-testing doesn't reach contents of an inert SVG");
+
+promise_test(async function() {
+ assert_false(dialog.open, "dialog is closed");
+
+ let reachedTarget = false;
+ target.addEventListener("mousedown", () => {
+ reachedTarget = true;
+ }, { once: true });
+
+ await new test_driver.Actions()
+ .pointerMove(0, 0, { origin: wrapper })
+ .pointerDown()
+ .send();
+
+ assert_true(target.matches(":active"), "target is active");
+ assert_true(reachedTarget, "target got event");
+}, "Hit-testing can reach contents of a no longer inert SVG");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html
new file mode 100644
index 0000000000..1a509f7f36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Inertness with modal dialogs and iframes</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert">
+<meta name="assert" content="Checks that a modal dialog marks outer nodes as inert,
+ but only in its document, not in the parent browsing context,
+ nor in nested browsing contexts.">
+<div id="log"></div>
+<div id="wrapper">
+ (main document: outer text)
+ <iframe id="outerIframe" srcdoc="
+ <div id='wrapper'>
+ (outer iframe: outer text)
+ <dialog id='dialog' style='display: block'>
+ (outer iframe: dialog)
+ </dialog>
+ </div>
+ "></iframe>
+ <dialog id="dialog" style="display: block">
+ (main document: dialog)
+ <iframe id="innerIframe" srcdoc="
+ <div id='wrapper'>
+ (inner iframe: outer text)
+ <dialog id='dialog' style='display: block'>
+ (inner iframe: dialog)
+ </dialog>
+ </div>
+ "></iframe>
+ </dialog>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const innerIframeWindow = innerIframe.contentWindow;
+const outerIframeWindow = outerIframe.contentWindow;
+promise_setup(async () => {
+ for (let global of [innerIframeWindow, outerIframeWindow]) {
+ if (global.location.href === "about:blank" ||
+ global.document.readyState !== "complete") {
+ await new Promise(resolve => {
+ global.frameElement.addEventListener("load", resolve, {once: true});
+ });
+ }
+ }
+});
+add_completion_callback(() => {
+ for (let global of [window, innerIframeWindow, outerIframeWindow]) {
+ global.getSelection().removeAllRanges();
+ }
+});
+
+function checkSelection(global, expectedText) {
+ const selection = global.getSelection();
+ selection.selectAllChildren(global.wrapper);
+
+ // Remove whitespace between parentheses since it varies among browsers,
+ // but that's not relevant to this test.
+ const actualText = selection.toString().replace(/\)\s*\(/g, ")(").trim();
+ assert_equals(actualText, expectedText);
+}
+
+function showModals(test, globals) {
+ for (let global of globals) {
+ global.dialog.showModal();
+ test.add_cleanup(() => { global.dialog.close(); });
+ }
+}
+
+promise_test(async function() {
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Initially, no node is inert");
+
+promise_test(async function() {
+ showModals(this, [outerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialog in the outer iframe marks outer nodes in that iframe as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialog in the inner iframe marks outer nodes in that iframe as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, outerIframeWindow]);
+
+ checkSelection(window, "(main document: outer text)(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in both iframes mark outer nodes in these iframes as inert.");
+
+promise_test(async function() {
+ showModals(this, [window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialog in the main document marks outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: outer text)(outer iframe: dialog)");
+}, "Modal dialogs in the main document and inner iframe mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [outerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: outer text)(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in the main document and outer iframe mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+
+promise_test(async function() {
+ showModals(this, [innerIframeWindow, outerIframeWindow, window]);
+
+ checkSelection(window, "(main document: dialog)");
+ checkSelection(innerIframeWindow, "(inner iframe: dialog)");
+ checkSelection(outerIframeWindow, "(outer iframe: dialog)");
+}, "Modal dialogs in the main document and both iframes mark outer nodes as inert. Contents of the outer iframe aren't marked as inert.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
new file mode 100644
index 0000000000..c6bcb5d4ca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=329407">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+#ancestor {
+ position: absolute;
+ height: 50px;
+ width: 50px;
+ top: 200px;
+ left: 100px;
+ border: 1px solid;
+}
+
+dialog {
+ height: 50px;
+ width: 50px;
+ top: 200px;
+ left: 200px;
+ margin: 0;
+}
+
+dialog::backdrop {
+ display: none;
+}
+</style>
+
+<div id="ancestor">
+ <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const rect = element.getBoundingClientRect();
+ const actions = new test_driver.Actions()
+ .pointerMove(rect.left + rect.width / 2, rect.top + rect.height / 2)
+ .pointerDown()
+ .pointerUp();
+ await actions.send();
+ }
+
+ const div = document.querySelector('#ancestor');
+ const dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const handledEvent = {};
+ document.addEventListener('click', function(event) {
+ handledEvent['document'] = true;
+ });
+
+ document.body.addEventListener('click', function(event) {
+ handledEvent['body'] = true;
+ // body should get a event only via bubbling.
+ if (event.target != dialog) {
+ assert_unreached('body was targeted for an click event');
+ div.style.backgroundColor = 'red';
+ }
+ });
+
+ div.addEventListener('click', function(event) {
+ handledEvent['div'] = true;
+ // div should get a event only via bubbling.
+ if (event.target != dialog) {
+ assert_unreached('div was targeted for an click event');
+ div.style.backgroundColor = 'red';
+ }
+ });
+
+ dialog.addEventListener('click', function(event) {
+ handledEvent['dialog'] = true;
+ dialog.style.backgroundColor = 'green';
+ if (event.target != dialog) {
+ assert_unreached('dialog was not targeted for a click event');
+ dialog.style.backgroundColor = 'red';
+ }
+ });
+
+ const nodes = [ 'document', 'body', 'div', 'dialog' ];
+ nodes.map(function(node) { handledEvent[node] = false; });
+ await clickOn(div);
+ assert_true(handledEvent.document, 'Clicking on ancestor.');
+ assert_false(handledEvent.body, 'Clicking on ancestor.');
+ assert_false(handledEvent.dialog, 'Clicking on ancestor.');
+ assert_false(handledEvent.div, 'Clicking on ancestor.');
+ handledEvent.document = false;
+
+ await clickOn(dialog);
+ assert_true(handledEvent.document, 'Clicking on dialog.');
+ assert_true(handledEvent.body, 'Clicking on dialog.');
+ assert_true(handledEvent.dialog, 'Clicking on dialog.');
+ assert_true(handledEvent.div, 'Clicking on dialog.');
+}, 'Test that ancestors of modal dialog are inert.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html
new file mode 100644
index 0000000000..d703b7f28e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<style>
+.dialog-default-ua-style {
+ position: absolute;
+ overflow: auto;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
+
+#dialog {
+ margin: auto;
+ height: 100px;
+ width: 100px;
+ top: 100px;
+ z-index: 1000;
+ background: green;
+}
+
+#backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.1);
+ z-index: 100;
+}
+</style>
+<body>
+Test for the default user agent style of dialog::backdrop. The test passes if
+there is a green box, above a very lightly translucent gray box spanning the
+viewport.
+<div id="backdrop"></div>
+<div class="dialog-default-ua-style" id="dialog"></div>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html
new file mode 100644
index 0000000000..a18af0d30e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="match" href="modal-dialog-backdrop-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#user-agent-level-style-sheet-defaults">
+<style>
+dialog {
+ top: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test for the default user agent style of dialog::backdrop. The test passes if
+there is a green box, above a very lightly translucent gray box spanning the
+viewport.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
new file mode 100644
index 0000000000..f6c0ec0ccb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, move the mouse to the blue box, click, and then move the
+mouse outside. Then repeat for the red box. The test succeeds if both boxes
+turn green
+</p>
+
+<style>
+#inert-div {
+ height: 100px;
+ width: 100px;
+ background: blue;
+}
+
+dialog {
+ width: 100px;
+}
+
+dialog::backdrop {
+ display: none;
+}
+
+#dialog-div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+
+<div id="inert-div"></div>
+<dialog id="dialog">
+ <div id="dialog-div"></div>
+</dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const rect = element.getBoundingClientRect();
+ const actions = new test_driver.Actions()
+ .pointerMove(
+ Math.floor(rect.left + rect.width / 2),
+ Math.floor(rect.top + rect.height / 2))
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ dialog.showModal();
+
+ inertDivHandledEvent = false;
+ inertDiv = document.getElementById('inert-div');
+ eventFiredOnInertNode = function(event) {
+ inertDivHandledEvent = true;
+ inertDiv.style.backgroundColor = 'red';
+ };
+
+ events = ['mousedown', 'mouseup', 'click', 'mousemove', 'mouseover', 'mouseout'];
+ dialogDiv = document.getElementById('dialog-div');
+ handledEvents = {};
+ handledEvents.dialogDiv = {};
+ eventFiredOnDialog = function(event) {
+ handledEvents.dialogDiv[event.type] = true;
+ if (Object.keys(handledEvents.dialogDiv).length == events.length)
+ dialogDiv.style.backgroundColor = 'green';
+ };
+
+ handledEvents.document = {};
+ expectedEventCountForDocument = events.length - 1; // document won't get 'mouseout'
+ eventFiredOnDocument = function(event) {
+ handledEvents.document[event.type] = true;
+ if (Object.keys(handledEvents.document).length == document.expectedEventCount && !inertDivHandledEvent) {
+ inertDiv.style.backgroundColor = 'green';
+ }
+ };
+
+ for (let i = 0; i < events.length; ++i) {
+ inertDiv.addEventListener(events[i], eventFiredOnInertNode);
+ dialogDiv.addEventListener(events[i], eventFiredOnDialog);
+ document.addEventListener(events[i], eventFiredOnDocument);
+ }
+
+ await clickOn(inertDiv);
+ assert_false(inertDivHandledEvent, 'Clicking on inert box');
+ assert_equals(Object.keys(handledEvents.document).length, expectedEventCountForDocument, 'Clicking on inert box');
+
+ await clickOn(dialogDiv);
+ assert_false(inertDivHandledEvent, 'Clicking on non-inert box');
+ assert_equals(Object.keys(handledEvents.dialogDiv).length, events.length, 'Clicking on non-inert box');
+}, 'Ensure that mouse events are not dispatched to an inert node.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html
new file mode 100644
index 0000000000..7ac66f5095
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<title>Reference: Test that display: contents; on modal dialog & ::backdrop acts like display: block</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<p>Test passes if there is a green dialog</p>
+<p>Dialog is display:block</p>
+<p>Dialog::backdrop is display:block</p>
+<dialog>Dialog Contents</dialog>
+<style>
+dialog {
+ background-color: green;
+}
+</style>
+<script>
+document.querySelector("dialog").showModal();
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html
new file mode 100644
index 0000000000..032033de01
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-display-contents.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<title>Test that display: contents; on modal dialog & ::backdrop acts like display: block</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="match" href="modal-dialog-display-contents-ref.html">
+<p>Test passes if there is a green dialog</p>
+<p>Dialog is display:<span id="computed-value"></span></p>
+<p>Dialog::backdrop is display:<span id="computed-value-backdrop"></span></p>
+<dialog>Dialog Contents</dialog>
+<style>
+dialog {
+ display: contents;
+ background-color: green;
+}
+dialog::backdrop {
+ display: contents;
+}
+</style>
+<script>
+dialog = document.querySelector("dialog");
+dialog.showModal();
+document.getElementById("computed-value").textContent = getComputedStyle(dialog).display;
+document.getElementById("computed-value-backdrop").textContent = getComputedStyle(dialog, "::backdrop").display;
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html
new file mode 100644
index 0000000000..10c9897c63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<style>
+#dialog {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+
+#dialog-before {
+ position: absolute;
+ top: 0px;
+}
+
+#dialog-after {
+ position: absolute;
+ bottom: 0px;
+}
+
+#dialog-backdrop {
+ position: absolute;
+ top: 100px;
+ left: 300px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+</style>
+<body>
+Test for a modal dialog with ::before, ::after, and ::backdrop. The test passes
+if there are two green boxes, one with the texts "::before" and "::after" in it.
+<div id="dialog">
+ <div id="dialog-before">::before</div>
+ <div id="dialog-after">::after</div>
+</div>
+<div id="dialog-backdrop"></div>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html
new file mode 100644
index 0000000000..86f43e52c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel="match" href="modal-dialog-generated-content-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ padding: 0px;
+ border: none;
+ margin: 0px;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ background: green;
+}
+
+dialog::before {
+ content: '::before';
+ position: absolute;
+ top: 0px;
+}
+
+dialog::after {
+ content: '::after';
+ position: absolute;
+ bottom: 0px;
+}
+
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 300px;
+ height: 100px;
+ width: 100px;
+ background: green;
+ content: 'THIS TEXT SHOULD NOT BE SEEN';
+}
+
+dialog::backdrop::before {
+ content: '::backdrop::before';
+ position: absolute;
+ top: 0px;
+ background: red;
+}
+dialog::backdrop::after {
+ content: '::backdrop::after';
+ position: absolute;
+ bottom: 0px;
+ background: red;
+}
+</style>
+<body>
+Test for a modal dialog with ::before, ::after, and ::backdrop. The test passes
+if there are two green boxes, one with the texts "::before" and "::after" in it.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html
new file mode 100644
index 0000000000..b6c52b7d7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe></iframe>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html
new file mode 100644
index 0000000000..f6440583fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Modal dialog inside iframe should not generate box</title>
+<link rel=match href="modal-dialog-in-iframe-ref.html">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6939">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: red;
+ border-color: red;
+}
+</style>
+<iframe></iframe>
+<script>
+ const iframe = document.querySelector('iframe');
+ const dialog = document.createElement('dialog');
+ iframe.appendChild(dialog);
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html
new file mode 100644
index 0000000000..38e15c1d79
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<object width="200" type="image/svg+xml" data="../../../../images/100px-green-rect.svg"></object>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html
new file mode 100644
index 0000000000..728748a7ee
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-object.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Modal dialog inside object should not generate box</title>
+<link rel=match href="modal-dialog-in-object-ref.html">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6939">
+<link rel=help href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+<object width="200" type="image/svg+xml" data="../../../../images/100px-green-rect.svg">
+ <dialog id="dialog"></dialog>
+</object>
+<script>
+document.getElementById('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html
new file mode 100644
index 0000000000..c837503caf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAQMAAAABGAcJAAAAA1BMVEUAgACc+aWRAAAADElEQVR42mNgIAEAAAAtAAH7KhMqAAAAAElFTkSuQmCC);
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see a green square near the top of the viewport.
+<div></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html
new file mode 100644
index 0000000000..75727b42f0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-replaced-renderer.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Modal dialog inside replaced renderer should not generate box</title>
+<link rel="match" href="modal-dialog-in-replaced-renderer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAQMAAAABGAcJAAAAA1BMVEUAgACc+aWRAAAADElEQVR42mNgIAEAAAAtAAH7KhMqAAAAAElFTkSuQmCC);
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see a green square near the top of the viewport.
+<div>
+<dialog id="dialog"></dialog>
+</div>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html
new file mode 100644
index 0000000000..0310d1ba24
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see no green rectangle.
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html
new file mode 100644
index 0000000000..3d72826b96
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-table-column.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Modal dialog inside display: table-column should not generate box</title>
+<link rel="match" href="modal-dialog-in-table-column-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+div {
+ display: table-column;
+}
+</style>
+</head>
+<body>
+<p>The test passes if you see no green rectangle.
+<div>
+<dialog id="dialog"></dialog>
+</div>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html
new file mode 100644
index 0000000000..abba08cfde
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-in-visibility-hidden.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<title>Test that modal dialogs have visibility: visible set from the UA sheet</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3:is-modal">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="visibility: hidden">
+ <dialog>This is a dialog</dialog>
+</div>
+
+<script>
+let dialog = document.querySelector("dialog");
+
+test(t => {
+ dialog.show();
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog).visibility, "hidden");
+}, "Non-modal dialog should let parent visibility inherit");
+
+test(t => {
+ dialog.showModal();
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog).visibility, "visible");
+}, "Modal dialog should have visibility: visible by default in UA sheet");
+
+test(t => {
+ dialog.style.visibility = "hidden";
+ dialog.showModal();
+ t.add_cleanup(() => {
+ dialog.style.removeProperty("visibility");
+ dialog.close();
+ });
+ assert_equals(getComputedStyle(dialog).visibility, "hidden");
+}, "Modal dialog visibility should be overridable");
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html
new file mode 100644
index 0000000000..638217f021
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-scroll-height.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:skobes@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=403136">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+.spacer {
+ height: 500px;
+}
+dialog {
+ border: 0;
+ margin: 0;
+ padding: 1px;
+}
+</style>
+<div class="spacer"></div>
+<dialog>
+ <div class="spacer"></div>
+</dialog>
+
+<script>
+test(() => {
+ document.querySelector('dialog').showModal();
+ assert_equals(document.scrollingElement.scrollHeight, window.innerHeight);
+}, 'dialogs should be centered before computing overflow.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html
new file mode 100644
index 0000000000..0242080268
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-selection.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Content selection in modal dialog</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#content-selection">
+<meta name="assert" content="Checks that text can be selected in a modal dialog, except with 'user-select: none'.">
+
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+dialog {
+ font: 10px/1 Ahem;
+ text-align: center;
+}
+</style>
+
+<dialog>123456789A</dialog>
+
+<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>
+const dialog = document.querySelector("dialog");
+dialog.showModal();
+
+function selectSomeText() {
+ // Clear existing selection.
+ getSelection().removeAllRanges();
+
+ // The dialog contains 10 characters. Select the 6 ones at the center.
+ return new test_driver.Actions()
+ .pointerMove(-3e1, 0, {origin: dialog})
+ .pointerDown()
+ .pointerMove(+3e1, 0, {origin: dialog})
+ .pointerUp()
+ .send();
+}
+
+function clickOnBackdrop() {
+ getSelection().removeAllRanges();
+
+ return new test_driver.Actions()
+ .pointerMove(10, 10)
+ .pointerDown()
+ .pointerUp()
+ .send();
+}
+
+promise_test(async function() {
+ await selectSomeText();
+ assert_equals(getSelection().toString(), "345678");
+}, "By default, text inside a modal dialog can be selected");
+
+promise_test(async function() {
+ await clickOnBackdrop();
+ assert_equals(getSelection().toString(), "");
+}, "Clicking on backdrop doesn't select text");
+
+promise_test(async function() {
+ dialog.style.userSelect = "none";
+
+ await selectSomeText();
+ assert_equals(getSelection().toString(), "");
+
+ dialog.style.userSelect = "";
+}, "'user-select: none' prevents text from being selected");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html
new file mode 100644
index 0000000000..38b628c309
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/103477">103477</a>: Make
+NodeRenderingContext::parentRenderer and nextRenderer top layer aware
+<p>The test passes if you see a green rectangle in the center of the viewport.
+<dialog id="dialog"></dialog>
+<script>
+document.getElementById('dialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html
new file mode 100644
index 0000000000..85cc61890a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-sibling.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="modal-dialog-sibling-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ background: green;
+ border-color: green;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="http://webkit.org/b/103477">103477</a>: Make
+NodeRenderingContext::parentRenderer and nextRenderer top layer aware
+<p>The test passes if you see a green rectangle in the center of the viewport.
+<div style="display: none" id="div"></div>
+<dialog id="dialog"></dialog>
+<script>
+document.getElementById('dialog').showModal();
+document.getElementById('dialog').offsetTop; // force a layout/renderer creation
+document.getElementById('div').style.display = 'block';
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html
new file mode 100644
index 0000000000..70bb3810e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/multiple-centered-dialogs.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ height: 10000px;
+}
+
+dialog {
+ padding: 0;
+ height: 50px;
+ width: 50px;
+}
+
+#console {
+ position: fixed;
+}
+</style>
+
+<dialog id="top-dialog"></dialog>
+<dialog id="first-middle-dialog"></dialog>
+<dialog id="second-middle-dialog" style="left: 100px"></dialog>
+<dialog id="bottom-dialog"></dialog>
+
+<script>
+test(() => {
+ function documentHeight() {
+ // clientHeight is an integer, but we want the correct floating point
+ // value. Start a binary search at clientHeight-1 and clientHeight+1.
+ let min = document.documentElement.clientHeight;
+ let max = min + 1;
+ --min;
+
+ // binary search with media queries to find the correct height
+ for (let iter = 0; iter < 10; ++iter) {
+ let test = (min + max) / 2;
+ if (window.matchMedia(`(min-height: ${test}px)`).matches)
+ min = test;
+ else
+ max = test;
+ }
+ return min;
+ }
+ function expectedTop(dialog) {
+ let height = documentHeight();
+ return (height - dialog.getBoundingClientRect().height) / 2;
+ }
+
+ function showAndTest(id) {
+ dialog = document.getElementById(id);
+ dialog.showModal();
+ assert_approx_equals(dialog.getBoundingClientRect().top, expectedTop(dialog), 0.05, id);
+ }
+
+ showAndTest('top-dialog');
+
+ window.scroll(0, 100);
+ showAndTest('first-middle-dialog');
+ showAndTest('second-middle-dialog');
+
+ window.scroll(0, 200);
+ showAndTest('bottom-dialog');
+}, 'Test that multiple dialogs are centered properly.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html
new file mode 100644
index 0000000000..b550ba288e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-does-not-block-mouse-events.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, click the red box. The test succeeds if the red box turns green.
+</p>
+
+<style>
+#div {
+ height: 100px;
+ width: 100px;
+ background: red;
+}
+</style>
+
+<div id="div"></div>
+<dialog id="dialog"></dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const actions = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: element})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0);
+ await actions.send();
+ }
+
+ const dialog = document.getElementById('dialog');
+ dialog.show();
+
+ const div = document.getElementById('div');
+ div.firedOn = false;
+ div.addEventListener('click', function(event) {
+ div.firedOn = true;
+ div.style.backgroundColor = 'green';
+ });
+
+ await clickOn(div);
+
+ assert_true(div.firedOn);
+}, 'Ensure that non-modal dialogs do not block mouse events.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html
new file mode 100644
index 0000000000..248bec86f6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/non-modal-dialog-layout.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=382594">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+/* Remove body margin and dialog styles for easier positioning expected values */
+body {
+ height: 10000px;
+ margin: 0;
+}
+
+dialog {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ width: auto;
+ height: auto;
+ max-width: initial;
+ max-height: initial;
+}
+
+#absolute-div {
+ position: absolute;
+ top: 800px;
+ height: 50px;
+ width: 90%;
+}
+
+#relative-div {
+ position: relative;
+ top: 20px;
+ height: 30px;
+}
+</style>
+
+<div id="absolute-div">
+ <div id="relative-div">
+ <dialog id="dialog">It is my dialog.</dialog>
+ </div>
+</div>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('#dialog');
+ const div = document.querySelector('#div-dialog');
+ const relativeContainer = document.querySelector('#relative-div');
+ const offset = 50;
+ dialog.style.top = offset + 'px';
+ dialog.style.left = offset + 'px';
+
+ dialog.style.position = 'absolute';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top + offset,
+ 'Absolute position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left + offset,
+ 'Absolute position.');
+
+ dialog.style.position = 'static';
+ assert_true(dialog.open);
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top,
+ 'Static position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left,
+ 'Static position.');
+ dialog.close();
+
+ dialog.style.position = 'relative';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ relativeContainer.getBoundingClientRect().top + offset,
+ 'Relative position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ relativeContainer.getBoundingClientRect().left + offset,
+ 'Relative position.');
+ dialog.close();
+
+ dialog.style.position = 'fixed';
+ dialog.show();
+ assert_equals(
+ dialog.getBoundingClientRect().top,
+ offset,
+ 'Fixed position.');
+ assert_equals(
+ dialog.getBoundingClientRect().left,
+ offset,
+ 'Fixed position.');
+ dialog.close();
+}, 'Tests layout of non-modal dialogs.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html
new file mode 100644
index 0000000000..2f2fbad1fc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body id="body">
+ <dialog>
+ This is a dialog
+ </dialog>
+ <input />
+<script>
+"use strict";
+function testFocus(element, expectFocus) {
+ var focusedElement = null;
+ element.addEventListener('focus', function() { focusedElement = element; }, false);
+ element.focus();
+ var theElement = element;
+ assert_equals(focusedElement === theElement, expectFocus, element.id);
+}
+
+test(function() {
+ var dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ var input = document.querySelector('input');
+ testFocus(input, false);
+
+ dialog.remove();
+ testFocus(input, true);
+}, "Test that removing dialog unblocks the document.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html
new file mode 100644
index 0000000000..0856d6f9f1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 100px;
+ width: 100px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 0px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 50px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+<div id="bottomDialog" class="pseudodialog"></div>
+<div id="topDialog" class="pseudodialog"></div>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html
new file mode 100644
index 0000000000..b0e50e3869
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/removed-element-is-removed-from-top-layer.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="removed-element-is-removed-from-top-layer-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 100px;
+ width: 100px;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 231px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 50px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+<dialog id="bottomDialog"></dialog>
+<dialog id="topDialog"></dialog>
+<script>
+document.getElementById('topDialog').showModal();
+var bottomDialog = document.getElementById('bottomDialog');
+bottomDialog.showModal();
+bottomDialog.offsetTop; // force a layout
+var parent = bottomDialog.parentNode;
+parent.removeChild(bottomDialog);
+parent.appendChild(bottomDialog);
+</script>
+</body>
+</html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js
new file mode 100644
index 0000000000..c72ed7f19c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/common.js
@@ -0,0 +1,18 @@
+function waitUntilLoadedAndAutofocused() {
+ return new Promise(function(resolve) {
+ var loaded = false;
+ var autofocused = false;
+ window.addEventListener('load', function() {
+ loaded = true;
+ if (autofocused)
+ resolve();
+ }, false);
+ document.addEventListener('focusin', function() {
+ if (autofocused)
+ return;
+ autofocused = true;
+ if (loaded)
+ resolve();
+ }, false);
+ });
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css
new file mode 100644
index 0000000000..571e7b8b6f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/dialog.css
@@ -0,0 +1,14 @@
+.pseudodialog {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: fit-content;
+ height: fit-content;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html
new file mode 100644
index 0000000000..c5566bc092
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+window.onload = parent.parent.frameLoaded;
+</script>
+</head>
+<body>
+<dialog id="dialog">
+ <button id="dialog-button" tabindex="0">Button</button>
+ <iframe id="iframe-in-dialog" srcdoc='
+ <input id="iframe-under-dialog-input" class="target" type="date">
+ '></iframe>
+</dialog>
+<input id="frame1-input" class="target" type="text">
+<iframe id="iframe1" srcdoc='
+ <dialog id="iframe-dialog">
+ <button id="iframe-dialog-button" tabindex="0">Button</button>
+ </dialog>
+ <input id="iframe-input" class="target" type="date">
+ <script>window.onload = parent.parent.parent.frameLoaded;</script>
+'>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html
new file mode 100644
index 0000000000..167c56945d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/inert-focus-in-frames-frame2.html
@@ -0,0 +1 @@
+<div id="frame2-div" class="target" tabindex="0">Hello</div>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg
new file mode 100644
index 0000000000..8909de2430
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/resources/submit.jpg
Binary files differ
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html
new file mode 100644
index 0000000000..164b41459d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/common.js"></script>
+<script>
+promise_test(() => {
+ return waitUntilLoadedAndAutofocused().then(() => {
+ outerButton = document.getElementById('outer-button');
+ assert_equals(document.activeElement, outerButton);
+
+ // Test that focus goes to body if the dialog has no focusable elements, including itself
+ var outerDialog = document.getElementById('outer-dialog');
+ outerDialog.showModal();
+ assert_equals(document.activeElement, document.body);
+
+ // Test that an autofocus element in the dialog gets focus.
+ var dialog = document.getElementById('dialog');
+ dialog.showModal();
+ autofocusButton = document.getElementById('autofocus-button');
+ assert_equals(document.activeElement, autofocusButton);
+ dialog.close();
+
+ // ... or else first focusable element in the dialog gets focus.
+ autofocusButton.parentNode.removeChild(autofocusButton);
+ dialog.showModal();
+ firstButton = document.getElementById('first-button');
+ assert_equals(document.activeElement, firstButton);
+ dialog.close();
+
+ // ... or else the dialog itself gets focus.;
+ var buttons = dialog.querySelectorAll('button');
+ for (var i = 0; i < buttons.length; ++i)
+ buttons[i].hidden = true;
+ dialog.showModal();
+ assert_equals(document.activeElement, dialog);
+ dialog.close();
+
+ document.getElementById('outer-dialog').close();
+ });
+}, "focus when a modal dialog is opened");
+</script>
+</head>
+<body>
+<button id="outer-button" autofocus></button>
+<dialog id="outer-dialog">
+ <dialog id="dialog" tabindex=0>
+ <button disabled></button>
+ <dialog>
+ <button autofocus></button>
+ </dialog>
+ <button id="first-button"></button>
+ <div>
+ <span>
+ <button id="autofocus-button" autofocus></button>
+ </span>
+ </div>
+ <button id="final-button"></button>
+ </dialog>
+</dialog>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html
new file mode 100644
index 0000000000..c9cc150099
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-in-shadow-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:futhark@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=850664">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=851384">
+
+<div id="dialog">
+ <div id="item"></div>
+</div>
+<script>
+const itemRoot = item.attachShadow({mode: 'open'});
+const dialogRoot = dialog.attachShadow({mode: 'open'});
+dialogRoot.innerHTML = '<dialog><slot></slot></dialog>';
+dialog.offsetTop;
+dialogRoot.firstChild.showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html
new file mode 100644
index 0000000000..a1d792010d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/showmodal-shadow-sibling-frame-crash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class=test-wait>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:noel@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=804047">
+
+<template>
+ <custom-dialog></custom-dialog>
+</template>
+<div id=shadow></div>
+<iframe id=sibling></iframe>
+
+<script>
+customElements.define('custom-dialog',class extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({mode: 'open'}).innerHTML = '<dialog></dialog>';
+ }
+ show() {
+ this.shadowRoot.querySelector('dialog').showModal();
+ }
+});
+
+onload = () => {
+ const template = document.querySelector('template');
+ const content = document.importNode(template.content, true);
+ const dialog = content.querySelector('custom-dialog');
+ document.querySelector('div').appendChild(dialog);
+ dialog.show();
+ document.documentElement.classList.remove('test-wait');
+};
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html
new file mode 100644
index 0000000000..8ff8a7e86c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/simulated-click-inert.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>Ensure that simulated click is still dispatched to an inert node.
+To test manually, click the CLICK ME label and verify it does change the value of the checkbox.</p>
+<div>
+</div>
+<input type="checkbox" id="target">
+<dialog><label for="target">CLICK ME</label></dialog>
+
+<script>
+promise_test(async () => {
+ async function clickOn(element) {
+ const actions = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: element})
+ .pointerDown()
+ .pointerUp()
+ await actions.send();
+ }
+
+ document.querySelector('dialog').showModal();
+ await clickOn(document.querySelector('label'));
+ assert_true(document.getElementById('target').checked);
+}, 'Ensure that simulated click is still dispatched to an inert node.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html
new file mode 100644
index 0000000000..5954993d19
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/submit-dialog-close-event.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=304827">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog>
+ <form method="dialog">
+ <input id="goodbye" type="submit" value="Goodbye">
+ <input id="hello" type="submit" value="Hello">
+ </form>
+</dialog>
+
+<script>
+async_test(t => {
+ const dialog = document.querySelector('dialog');
+ dialog.show();
+ dialog.addEventListener('close', t.step_func(() => {
+ assert_false(dialog.open);
+ assert_equals(dialog.returnValue, 'Goodbye');
+
+ dialog.show();
+ dialog.addEventListener('close', t.step_func_done(() => {
+ assert_false(dialog.open);
+ assert_equals(dialog.returnValue, 'Hello');
+ }));
+ document.querySelector('#hello').click();
+ }), {once: true});
+
+ document.querySelector('#goodbye').click();
+}, 'Tests submitting a dialog on a close event triggered by a previous submission.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html
new file mode 100644
index 0000000000..3be8213cd4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/synthetic-click-inert.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+dialog {
+ width: 50px;
+}
+</style>
+
+<button>Click me</button>
+<div id="div">Click me too</div>
+<dialog></dialog>
+
+<script>
+test(() => {
+ dialog = document.querySelector('dialog');
+ dialog.showModal();
+
+ const button = document.querySelector('button');
+ const div = document.getElementById('div');
+ let clicked = false;
+
+ [button, div].forEach(function(element) {
+ element.addEventListener('click', () => clicked = true);
+
+ clicked = false;
+ element.click();
+ assert_true(clicked, 'Calling click() on ' + element.tagName);
+
+ clicked = false;
+ element.dispatchEvent(new Event('click'));
+ assert_true(clicked, 'Calling dispatchEvent() on ' + element.tagName);
+ });
+}, 'Test that inert nodes still get programmatic click events');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html
new file mode 100644
index 0000000000..40b72cf5ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<body>
+<p>
+This tests that a modal dialog's containing block is in the initial containing block and that it is unaffected by
+ancestor elements with overflow or opacity.
+<div class="pseudodialog" style="position: absolute; top: 100px; height: 250px; width: 90%; background-color: yellow">
+ This dialog should be onscreen with a width of 90% of the page. It is the child of an narrow element
+ positioned off screen, but the containing block of a top layer element is the initial containing block, so its
+ position and percent lengths are relative to that.
+</div>
+<div class="pseudodialog" style="position: absolute; top: 200px; left: 0px; height: 100px; background-color: cyan">
+ This dialog should be unaffected by its ancestor with overflow. It should not be clipped.
+</div>
+<div class="pseudodialog" style="position: absolute; top: 250px; left: 0px; background-color: magenta">
+ This dialog should be unaffected by its ancestor with opacity.
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html
new file mode 100644
index 0000000000..10f6c69fbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-containing-block.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-containing-block-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+::backdrop {
+ display: none;
+}
+</style>
+</head>
+<body>
+<p>
+This tests that a modal dialog's containing block is in the initial containing block and that it is unaffected by
+ancestor elements with overflow or opacity.
+<div style="position: absolute; top: 400px; opacity: 0.3">
+ <dialog id="opaqueDialog" style="position: absolute; top: 250px; left: 0px; background-color: magenta">
+ This dialog should be unaffected by its ancestor with opacity.
+ </dialog>
+</div>
+<div style="position: absolute; overflow: hidden; width: 500px; height: 150px; top: 400px; left: 300px">
+ <dialog id="unclippedDialog" style="position: absolute; top: 200px; left: 0px; height: 100px; background-color: cyan">
+ This dialog should be unaffected by its ancestor with overflow. It should not be clipped.
+ </dialog>
+</div>
+<div style="position: absolute; top: 1000px; left: 1000px; width: 20px;">
+ <dialog id="bottomDialog" style="position: absolute; top: 100px; height: 250px; width: 90%; background-color: yellow">
+ This dialog should be onscreen with a width of 90% of the page. It is the child of an narrow element
+ positioned off screen, but the containing block of a top layer element is the initial containing block, so its
+ position and percent lengths are relative to that.
+ </dialog>
+</div>
+<script>
+document.getElementById('bottomDialog').showModal();
+document.getElementById('unclippedDialog').showModal();
+document.getElementById('opaqueDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html
new file mode 100644
index 0000000000..1880668cc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+}
+</style>
+</head>
+<body>
+This tests that a top layer element is not rendered if it, or an ancestor, has display: none.
+It passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<div class="pseudodialog" style="top: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 100px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html
new file mode 100644
index 0000000000..5257823eca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-display-none-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+}
+
+::backdrop {
+ display: none;
+}
+
+.red {
+ background-color: red;
+ top: 200px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 50px;
+ display: none;
+}
+
+#topDialog {
+ background-color: green;
+ top: 100px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+This tests that a top layer element is not rendered if it, or an ancestor, has display: none.
+It passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<dialog id="hiddenDialog" class="red" style="display: none;"></dialog>
+<div id="container">
+ <div>
+ <dialog id="displayNoneChild1" class="red"></dialog>
+ <dialog id="displayNoneChild2" class="red"></dialog>
+ </div>
+</div>
+<dialog id="bottomDialog"></dialog>
+<dialog id="topDialog"></dialog>
+<script>
+document.getElementById('hiddenDialog').showModal();
+document.getElementById('displayNoneChild1').showModal();
+document.getElementById('container').style.display = 'none';
+document.getElementById('displayNoneChild2').showModal();
+
+// Test that stacking works even if an element is added to the top layer when it has no renderer.
+document.getElementById('bottomDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('bottomDialog').style.display = 'block';
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html
new file mode 100644
index 0000000000..0a2936abbe
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+ position: absolute;
+ top: 0; right: 0; bottom: 0; left: 0;
+ margin: auto;
+ border: solid;
+ padding: 1em;
+ background: white;
+ color: black;
+}
+</style>
+</head>
+<body>
+This tests that top layer elements are stacked correctly even if nested in the DOM tree.
+The test passes if you see no red rectangles and see 3 rectangles stacked in the following order (from bottom to top): yellow, blue, green.
+
+<div class="pseudodialog" style="top: 100px; background-color: yellow"></div>
+<div class="pseudodialog" style="top: 150px; left: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 200px; left: 100px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html
new file mode 100644
index 0000000000..6397584387
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-nesting-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: yellow;
+ top: 100px;
+ z-index: 1000;
+}
+
+#middleDialog {
+ background-color: blue;
+ top: 150px;
+ left: 50px;
+ z-index: -500;
+}
+
+#topDialog {
+ background-color: green;
+ top: 200px;
+ left: 100px;
+ z-index: -1000;
+}
+
+.red {
+ background-color: red;
+ top: 250px;
+ left: 0px;
+}
+</style>
+</head>
+<body>
+This tests that top layer elements are stacked correctly even if nested in the DOM tree.
+The test passes if you see no red rectangles and see 3 rectangles stacked in the following order (from bottom to top): yellow, blue, green.
+
+<dialog id="topDialog">
+ <dialog id="middleDialog">
+ <dialog id="bottomDialog">
+ <dialog id="hiddenDialog" class="red">
+ <dialog id="hiddenDialogChild" class="red"></dialog>
+ </dialog>
+ </dialog>
+ </dialog>
+</dialog>
+<script>
+document.getElementById('hiddenDialogChild').showModal();
+document.getElementById('hiddenDialog').showModal();
+document.getElementById('bottomDialog').showModal();
+document.getElementById('middleDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('hiddenDialog').close();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html
new file mode 100644
index 0000000000..9a621e7594
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent clip-path does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ clip-path: circle(5%);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html
new file mode 100644
index 0000000000..020d90a0c8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent filter does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ filter: blur(100px) opacity(50%);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html
new file mode 100644
index 0000000000..daa5ccbbe1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent mask does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ mask-image: radial-gradient(black, transparent);
+ mask-size: 100px;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html
new file mode 100644
index 0000000000..82aa09d6c5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent opacity does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=229317">
+<style>
+body { background: red; }
+
+#parent {
+ opacity: 0;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html
new file mode 100644
index 0000000000..86587254cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: clip; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: clip;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html
new file mode 100644
index 0000000000..afcde733d2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: hidden; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html
new file mode 100644
index 0000000000..dd04c2ed47
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent overflow: scroll; does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ max-width: 0;
+ max-height: 0;
+ width: 0;
+ height: 0;
+ overflow: scroll;
+ position: absolute;
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+ position: absolute;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html
new file mode 100644
index 0000000000..cf35a713f3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<title>Test that parent transform does not affect top layer elements</title>
+<meta charset="utf-8">
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="green-dialog-and-backdrop.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+body { background: red; }
+
+#parent {
+ transform: scale(0);
+}
+
+dialog::backdrop,
+dialog {
+ background: green;
+}
+</style>
+<body>
+<div id="parent">
+ <dialog>PASS if no red shows</dialog>
+</div>
+<script>
+ document.querySelector("dialog").showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html
new file mode 100644
index 0000000000..01eff8c4de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: absolute;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html
new file mode 100644
index 0000000000..0dbef7d2ac
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-relative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=106538">
+<link rel=match href="top-layer-position-ref.html">
+<meta name=assert content="Position relative computes to absolute in the top layer for dialog elements.">
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: relative;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html
new file mode 100644
index 0000000000..86d8c89f88
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position-static.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=106538">
+<link rel=match href="top-layer-position-ref.html">
+<meta name=assert content="Position static computes to absolute in the top layer for dialog elements.">
+
+<style>
+dialog {
+ background-color: green;
+ height: 50px;
+ width: 50px;
+ border: none;
+ padding: 0;
+ margin: 0;
+
+ position: static;
+ top: 100px;
+ left: 100px;
+}
+</style>
+
+<dialog></dialog>
+
+<script>
+document.querySelector('dialog').showModal();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html
new file mode 100644
index 0000000000..1fdbca5c1e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-position.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+test(() => {
+ const dialog = document.createElement('dialog');
+ document.body.appendChild(dialog);
+
+ dialog.style = 'position:static';
+ assert_equals(getComputedStyle(dialog).position, 'static');
+ dialog.showModal();
+ assert_true(dialog.open);
+ assert_equals(getComputedStyle(dialog).position, 'absolute',
+ `dialog should be position:absolute when element.style has position:static.`);
+ dialog.close();
+ assert_false(dialog.open);
+
+ dialog.style = 'position:relative';
+ assert_equals(getComputedStyle(dialog).position, 'relative');
+ dialog.showModal();
+ assert_true(dialog.open);
+ assert_equals(getComputedStyle(dialog).position, 'absolute',
+ `dialog should be position:absolute when element.style has position:relative.`);
+ dialog.close();
+ assert_false(dialog.open);
+}, `Verifies that position:static and position:relative computed to position:absolute in the top layer.`);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html
new file mode 100644
index 0000000000..392d1ca46e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 100px;
+ width: 100px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+
+<div class="pseudodialog" style="top: 100px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 150px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html
new file mode 100644
index 0000000000..3b3e336892
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-correct-order-remove-readd.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-correct-order-remove-readd-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog {
+ height: 100px;
+ width: 100px;
+}
+
+::backdrop {
+ display: none;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 100px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 150px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+<p>Bug <a href="https://bugs.webkit.org/show_bug.cgi?id=105489">105489</a>: Elements must be reattached when inserted/removed from top layer
+<p>The test passes if you see a green rectangle stacked on top of a blue rectangle.
+
+<dialog id="topDialog"></dialog>
+<dialog id="bottomDialog"></dialog>
+<script>
+var topDialog = document.getElementById('topDialog');
+var bottomDialog = document.getElementById('bottomDialog');
+topDialog.showModal();
+bottomDialog.showModal();
+topDialog.offsetTop; // force a layout
+topDialog.close();
+topDialog.showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html
new file mode 100644
index 0000000000..6ddb317633
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.pseudodialog {
+ height: 150px;
+ width: 150px;
+}
+</style>
+</head>
+<body>
+This tests top layer element stacking order after dynamically calling show/close and removal from the DOM tree.
+The test passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<div class="pseudodialog" style="top: 50px; background-color: blue"></div>
+<div class="pseudodialog" style="top: 100px; left: 50px; background-color: green"></div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html
new file mode 100644
index 0000000000..8ab7068d30
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-dynamic-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-dialog-element">
+<style>
+dialog {
+ height: 150px;
+ width: 150px;
+}
+
+::backdrop {
+ display: none;
+}
+
+.red {
+ background-color: red;
+ top: 200px;
+}
+
+#bottomDialog {
+ background-color: blue;
+ top: 50px;
+}
+
+#topDialog {
+ background-color: green;
+ top: 100px;
+ left: 50px;
+}
+</style>
+</head>
+<body>
+This tests top layer element stacking order after dynamically calling show/close and removal from the DOM tree.
+The test passes if you see a green rectangle stacked on top of a blue rectangle, and see no red rectangles.
+
+<dialog id="topDialog"></dialog>
+<dialog id="bottomDialog"></dialog>
+<dialog id="removedDialog" class="red">
+ <dialog id="removedDialogChild" class="red"></dialog>
+</dialog>
+<script>
+document.getElementById('topDialog').showModal();
+var removedDialog = document.getElementById('removedDialog');
+removedDialog.showModal();
+document.getElementById('bottomDialog').showModal();
+document.getElementById('removedDialogChild').showModal();
+removedDialog.parentNode.removeChild(removedDialog);
+document.getElementById('topDialog').close();
+document.getElementById('topDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html
new file mode 100644
index 0000000000..b271ef47b4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="resources/dialog.css">
+</head>
+<style>
+.box {
+ height: 150px;
+ width: 150px;
+}
+.container {
+ perspective: 500px;
+ border: 1px solid black;
+ background-color: magenta;
+}
+.transformed {
+ transform: rotateY(45deg);
+ background-color: cyan;
+}
+</style>
+<body>
+<div class="pseudodialog" style="position: fixed; top: 10px; z-index:3000">
+ This white box is the topmost modal dialog. It should be on top of everything.
+</div>
+<div style="position: absolute; top: 0px; z-index: 3; background-color: red; left: 0; right: 0; height: 200px;"></div>
+<div class="pseudodialog" style="position: absolute; top: 50px; background-color: green; width: 75%; height: 400px; z-index:2000; overflow: auto;">
+ This green box is also a modal dialog. It should be rendered above the red and yellow regions.
+ <div class="container box">
+ <div class="transformed box">A transform within the dialog's subtree.</div>
+ </div>
+ <div class="box" style="position: absolute; top:300px; z-index: 2; background-color: cyan">
+ This shows z-index stacking within the dialog's subtree. The cyan box should be on top of the magenta one.
+ </div>
+ <div class="box" style="position: absolute; top:350px; left:50px; z-index: 1; background-color: magenta"></div>
+ <div style="position: fixed; top: 90px; left: 30px; background-color: green">This is part of the green dialog.</div>
+</div>
+<div style="position: absolute; top: 100px; left: 0px; right: 0px; height: 200em; background-color: yellow; z-index:1000">
+</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.html
new file mode 100644
index 0000000000..6407ef23c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- This tests that top layer elements are rendered above z-indexed elements
+and stacked in the correct order amongst themselves. Also, layer features like
+transforms and z-index are tested inside a top layer element subtree. -->
+<html>
+<head>
+<link rel="match" href="top-layer-stacking-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#new-stacking-layer">
+<style>
+.box {
+ height:150px;
+ width:150px;
+}
+
+::backdrop {
+ display: none;
+}
+
+.container {
+ perspective: 500px;
+ border: 1px solid black;
+ background-color: magenta;
+}
+.transformed {
+ transform: rotateY(45deg);
+ background-color: cyan;
+}
+</style>
+</head>
+<body>
+<dialog id="hiddenDialog" style="display: none; color: red">This should not be displayed.</dialog>
+<dialog id="topDialog" style="position: fixed; top: 10px; z-index: -10;">
+ This white box is the topmost modal dialog. It should be on top of everything.
+</dialog>
+<div style="position: absolute; top: 0px; z-index: 3; background-color: red; left: 0; right: 0; height: 200px;">
+ <dialog id="bottomDialog" style="position: absolute; top: 50px; background-color: green; width: 75%; height: 400px;">
+ This green box is also a modal dialog. It should be rendered above the red and yellow regions.
+ <div class="container box">
+ <div class="transformed box">A transform within the dialog's subtree.</div>
+ </div>
+ <div class="box" style="position: absolute; top:300px; z-index: 2; background-color: cyan">
+ This shows z-index stacking within the dialog's subtree. The cyan box should be on top of the magenta one.
+ </div>
+ <div class="box" style="position: absolute; top:350px; left:50px; z-index: 1; background-color: magenta"></div>
+ <div style="position: fixed; top: 90px; left: 30px; background-color: green">This is part of the green dialog.</div>
+ </dialog>
+</div>
+<div style="position: absolute; top: 100px; left: 0px; right: 0px; height: 200em; background-color: yellow; z-index:1000">
+</div>
+<script>
+document.getElementById('bottomDialog').showModal();
+document.getElementById('topDialog').showModal();
+document.getElementById('hiddenDialog').showModal();
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html
new file mode 100644
index 0000000000..4a3693bd2d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/activation-behavior.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: activation behavior</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-summary-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<details id="happy-path-starts-closed">
+ <summary id="happy-path-starts-closed-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="happy-path-starts-open" open>
+ <summary id="happy-path-starts-open-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="details-not-being-rendered" style="display: none">
+ <summary id="details-not-being-rendered-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="summary-not-being-rendered">
+ <summary id="summary-not-being-rendered-summary" style="display: none">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-element">
+ <span></span>
+ <summary id="has-preceding-element-summary">Summary</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-summary">
+ <summary>Summary 1</summary>
+ <summary id="has-preceding-summary-summary">Summary 2</summary>
+ <p>Contents</p>
+</details>
+
+<details id="has-preceding-summary-descendant">
+ <span><summary>Summary 1</summary></span>
+ <summary id="has-preceding-summary-descendant-summary">Summary 2</summary>
+ <p>Contents</p>
+</details>
+
+<details id="summary-nested">
+ <span><summary id="summary-nested-summary">Summary</summary></span>
+ <p>Contents</p>
+</details>
+
+<details id="toggle-tester">
+ <summary>Summary</summary>
+ <p>Contents</p>
+</details>
+
+<script>
+"use strict";
+
+testSummary(
+ "happy-path-starts-closed", false, true,
+ "Should open a closed details if all conditions are met"
+);
+
+testSummary(
+ "happy-path-starts-open", true, false,
+ "Should close an open details if all conditions are met"
+);
+
+testSummary(
+ "details-not-being-rendered", false, true,
+ "Should open a closed details even if the details is not being rendered"
+);
+
+testSummary(
+ "summary-not-being-rendered", false, true,
+ "Should open a closed details even if the summary is not being rendered"
+);
+
+testSummary(
+ "has-preceding-element", false, true,
+ "Should open a closed details if a span element precedes the summary"
+);
+
+testSummary(
+ "has-preceding-summary", false, false,
+ "Should stay closed if another summary element precedes the summary"
+);
+
+testSummary(
+ "has-preceding-summary-descendant", false, true,
+ "Should open a closed details if another summary element *nested inside a span* precedes the summary"
+);
+
+testSummary(
+ "summary-nested", false, false,
+ "Should stay closed if the summary element is nested inside a span element"
+);
+
+async_test(t => {
+ const details = document.getElementById("toggle-tester");
+ const summary = details.firstElementChild;
+
+ let timesToggleFired = 0;
+ details.addEventListener("toggle", t.step_func(() => {
+ ++timesToggleFired;
+ }));
+
+ t.step_timeout(() => {
+ assert_equals(timesToggleFired, 1, "Expected toggle to fire exactly once");
+ t.done();
+ }, 200);
+
+ summary.click();
+ summary.click();
+ summary.click();
+ summary.click();
+ Promise.resolve().then(() => summary.click());
+
+}, "toggle events should be coalesced even when using the activation behavior of a summary");
+
+function testSummary(detailsId, expectedBefore, expectedAfter, name) {
+ test(() => {
+ const details = document.getElementById(detailsId);
+ const summary = document.getElementById(detailsId + "-summary");
+
+ assert_equals(details.open, expectedBefore, "Before activation: expected open to be " + expectedBefore);
+ summary.click();
+ assert_equals(details.open, expectedAfter, "After activation: expected open to be " + expectedAfter);
+ }, name);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html
new file mode 100644
index 0000000000..6910a5de93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-with-inline-element.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: clicking on anchor containing inline element</title>
+<link rel="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-summary-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id="details_i">
+ <summary>Anchor text is wrapped with &lt;i&gt; tag <a href="#with_i_tag"><i id="with_i">permalink</i></a></summary>
+ <p>asdf</p>
+</details>
+
+<details id="details_span">
+ <summary>This one uses &lt;span&gt;. <a href="#with_span_tag"><span id="with_span">permalink</span></a></summary>
+ <p>asdf</p>
+</details>
+
+<details id="details_svg">
+ <summary>
+ <svg style="width: 100px;" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+ <a href="#inside_svg_w_circle">
+ <circle id="svg_circle" cx="50" cy="40" r="35"/>
+ </a>
+ <a href="#inside_svg_w_text">
+ <text id="svg_text" x="50" y="90" text-anchor="middle">
+ &lt;circle&gt;
+ </text>
+ </a>
+ </svg>
+ </summary>
+ <p>asdf</p>
+</details>
+
+<script>
+function testClickingOnInlineElement(detailsId, targetId, expected, testName) {
+ const details = document.getElementById(detailsId);
+ const target = document.getElementById(targetId);
+ const test = async_test(testName);
+
+ const promise = new Promise((resolve, reject) => {
+ window.onhashchange = test.step_func_done(() => {
+ assert_false(details.open);
+ assert_equals(location.hash, expected);
+ resolve();
+ });
+ });
+
+ if (target.click) {
+ target.click();
+ }
+ else {
+ // svg element don't have click method
+ target.dispatchEvent(new MouseEvent('click', {
+ view: window,
+ bubbles: true,
+ cancelable: true
+ }));
+ }
+ return promise;
+};
+
+async function testAll() {
+ try {
+ await testClickingOnInlineElement("details_i", "with_i", "#with_i_tag", "Expected <a> containing <i> to navigate");
+ await testClickingOnInlineElement("details_span", "with_span", "#with_span_tag", "Expected <a> containing <span> to navigate");
+ await testClickingOnInlineElement("details_svg", "svg_circle", "#inside_svg_w_circle", "Expected <a>, inside svg, containing <circle> to navigate");
+ await testClickingOnInlineElement("details_svg", "svg_text", "#inside_svg_w_text", "Expected <a>, inside svg, containing <text> to navigate");
+ } catch (exception) {
+ assert_unreached("should NOT-THROW exception");
+ }
+};
+
+var allTests = async_test("Clicking on anchor with embedded inline element should navigate instead of opening details");
+testAll().then(()=>{ allTests.done(); });
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html
new file mode 100644
index 0000000000..edaf786b25
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/anchor-without-link.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: clicking on anchor without link</title>
+<link rel="author" title="Di Zhang" href="mailto:dizhangg@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#the-summary-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<details id="details">
+ <summary><a id="no_inline">Details</a></summary>
+ <p>Text</p>
+</details>
+
+<details id="details_inline">
+ <summary><a><i id="has_inline">Details</i></a></summary>
+ <p>Text</p>
+</details>
+
+
+<script>
+
+async function testClickingOnAnchorWithoutLink (detailsId, targetId) {
+ const details = document.getElementById(detailsId);
+ const target = document.getElementById(targetId);
+ const initialLoc = location.hash;
+
+ assert_false(details.open);
+ target.click();
+ assert_true(details.open);
+ assert_equals(location.hash, initialLoc);
+}
+
+promise_test(() => testClickingOnAnchorWithoutLink('details', 'no_inline'),
+ "clicking on anchor without link should open details and not navigate.");
+
+promise_test(() => testClickingOnAnchorWithoutLink('details_inline', 'has_inline'),
+ "clicking on anchor without link, with embedded inline element should open details and not navigate.");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html
new file mode 100644
index 0000000000..4418413fef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/click-behavior-optional.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>summary element: click behavior</title>
+<link rel="author" title="Mu-An Chiou" href="mailto:hi@muan.co">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-summary-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+ <div id="log"></div>
+
+ <details id="details">
+ <summary id="summary">Summary</summary>
+ <p>Contents</p>
+ </details>
+</body>
+
+<script>
+ // This behavior is not specified by HTML standards, but setting focus on
+ // clicked summary tag is the current behavior on Chrome, Safari, and Firefox
+ // in both Windows and macOS.
+ async_test(t => {
+ const details = document.getElementById("details")
+ const summary = document.getElementById("summary")
+
+ t.step_timeout(() => {
+ details.addEventListener("toggle", t.step_func_done(function () {
+ assert_equals(details.open, true, "details should be open")
+ assert_equals(document.activeElement, summary, "active element should be summary")
+ t.done()
+ }))
+
+ new test_driver.click(summary)
+ }, 200)
+ }, "clicking on summary should open details and set focus on summary")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html
new file mode 100644
index 0000000000..57cc45478e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/display-table-with-rt-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=969619">
+<summary style="display:table;"><rt></rt></summary>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> { }, "No crash");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html
new file mode 100644
index 0000000000..21b66d52e7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-summary-element/summary-untrusted-key-event.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Summary</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<details>
+ <summary>Summary</summary>
+ Details
+</details>
+<script type="module">
+const details = document.querySelector("details");
+details.addEventListener("toggle",
+ (e) => assert_true(false, 'details should not be toggled'));
+
+const summary = document.querySelector("summary");
+summary.addEventListener("click",
+ (e) => assert_true(false, `summary should not be clicked`));
+
+test(() => {
+ // keyCode: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keypress", {
+ key: " ",
+ })
+ );
+}, `Dispatching untrusted keypress events to summary should not cause click event`);
+
+test(() => {
+ // keyCode: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 13,
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 13,
+ })
+ );
+
+ // key: Enter
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Enter",
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: "Enter",
+ })
+ );
+
+ // keyCode: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ keyCode: 32,
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ keyCode: 32,
+ })
+ );
+
+ // key: Space
+ summary.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: " ",
+ })
+ );
+ summary.dispatchEvent(
+ new KeyboardEvent("keyup", {
+ key: " ",
+ })
+ );
+}, `Dispatching untrusted keyup/keydown events to summary should not cause click event`);
+</script>
+</body>
+</html>