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/name-attribute.html469
-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/support/empty-html-document.html2
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html183
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml3
-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.html55
-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.html35
-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.html39
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html25
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html50
-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.html81
-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/child-sequential-focus.html60
-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.html53
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html49
-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.html107
-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-via-attribute.html59
-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-previous-outside.html82
-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.html274
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html58
-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-unusual.html34
-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-no-throw-requested-state.html29
-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-overlay-re-add-during-transition.html32
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html36
-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.html16
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html18
-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.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html27
-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.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html27
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html27
-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/focus-previous-iframe.tentative.html52
-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.html53
-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.html75
-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-opacity-ref.html22
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html19
-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.html21
-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.html59
-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/pass-dialog-ref.html9
-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.html60
-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.html66
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html29
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html30
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html34
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html35
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html29
-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-remove-popover-attribute-ref.html15
-rw-r--r--testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html18
-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.html46
-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.html55
-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.tentative.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
181 files changed, 7960 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/name-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html
new file mode 100644
index 0000000000..2685546e9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/name-attribute.html
@@ -0,0 +1,469 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>Test for the name attribute creating exclusive accordions from details elements</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-details-element">
+<link rel="help" href="https://open-ui.org/components/accordion.explainer">
+<link rel="help" href="https://github.com/openui/open-ui/issues/725">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1444057">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="container">
+</div>
+
+<script>
+
+function assert_element_states(elements, expectations, description) {
+ assert_array_equals(elements.map(e => Number(e.open)), expectations, description);
+}
+
+let container = document.getElementById("container");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a">
+ <summary>1</summary>
+ This is the first item.
+ </details>
+
+ <details name="a">
+ <summary>2</summary>
+ This is the second item.
+ </details>
+ `;
+ let first = container.firstElementChild;
+ let second = first.nextElementSibling;
+ assert_false(first.open);
+ assert_false(second.open);
+ first.open = true;
+ assert_true(first.open);
+ assert_false(second.open);
+ second.open = true;
+ assert_false(first.open);
+ assert_true(second.open);
+ second.open = true;
+ assert_false(first.open);
+ assert_true(second.open);
+ second.open = false;
+ assert_false(first.open);
+ assert_false(second.open);
+}, "basic handling of mutually exclusive details");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" open>
+ <summary>1</summary>
+ This is the first item.
+ </details>
+
+ <details name="a">
+ <summary>2</summary>
+ This is the second item.
+ </details>
+
+ <details name="a" open>
+ <summary>3</summary>
+ This is the third item.
+ </details>
+ `;
+ let first = container.firstElementChild;
+ let second = first.nextElementSibling;
+ let third = second.nextElementSibling;
+ function assert_states(expected_first, expected_second, expected_third, description) {
+ assert_array_equals([first.open, second.open, third.open], [expected_first, expected_second, expected_third], description);
+ }
+
+ assert_states(true, false, false, "initial states from open attribute");
+ first.open = true;
+ assert_states(true, false, false, "non-mutation doesn't change state");
+ second.open = true;
+ assert_states(false, true, false, "mutation closes multiple open elements");
+ third.setAttribute("open", "");
+ assert_states(false, false, true, "setAttribute closes other open element");
+}, "more complex handling of mutually exclusive details");
+
+promise_test(async t => {
+ let details_elements_string = `
+ <details name="a"></details>
+ <details name="a" open></details>
+ <details name="b"></details>
+ <details name="b"></details>
+ `;
+ container.innerHTML = `
+ ${details_elements_string}
+ <div id="shadow_host"></div>
+ `;
+ let shadow_root = document.getElementById("shadow_host").attachShadow({ mode: "open" });
+ shadow_root.innerHTML = details_elements_string;
+ let elements = Array.from(container.querySelectorAll("details")).concat(Array.from(shadow_root.querySelectorAll("details")));
+
+ assert_element_states(elements, [0, 1, 0, 0, 0, 1, 0, 0], "initial states from open attribute");
+ elements[4].open = true;
+ assert_element_states(elements, [0, 1, 0, 0, 1, 0, 0, 0], "after mutation in shadow tree");
+ for (let i = 0; i < 8; ++i) {
+ elements[i].open = true;
+ }
+ assert_element_states(elements, [0, 1, 0, 1, 0, 1, 0, 1], "after setting all elements open");
+ elements[0].open = true;
+ assert_element_states(elements, [1, 0, 0, 1, 0, 1, 0, 1], "after final mutation");
+}, "mutually exclusive details across multiple names and multiple tree scopes");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="a" id="e3" open></details>
+ `;
+ let e2 = document.createElement("details");
+ e2.id = "e2";
+ e2.name = "a";
+ e2.open = true;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ e2,
+ document.getElementById("e3") ];
+ container.insertBefore(e2, elements[3]);
+
+ let mutation_event_received_ids = [];
+ let mutation_listener = event => {
+ assert_equals(event.type, "DOMSubtreeModified");
+ assert_equals(event.target.nodeType, Node.ELEMENT_NODE);
+ let element = event.target;
+ assert_equals(element.localName, "details");
+ mutation_event_received_ids.push(element.id);
+ };
+ let toggle_event_received_ids = [];
+ let toggle_event_promises = [];
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", mutation_listener);
+ toggle_event_promises.push(new Promise((resolve, reject) => {
+ element.addEventListener("toggle", event => {
+ assert_equals(event.type, "toggle");
+ assert_equals(event.target, element);
+ toggle_event_received_ids.push(element.id);
+ resolve(undefined);
+ });
+ }));
+ }
+ assert_array_equals(mutation_event_received_ids, []);
+ assert_element_states(elements, [1, 0, 0, 0], "states before mutation");
+ elements[1].open = true;
+ if (mutation_event_received_ids.length == 0) {
+ // ok if mutation events are not supported
+ } else {
+ assert_array_equals(mutation_event_received_ids, ["e1"],
+ "mutation events received only for open attribute mutation and not for closing other element");
+ }
+ assert_element_states(elements, [0, 1, 0, 0], "states after mutation");
+ assert_array_equals(toggle_event_received_ids, [], "toggle events received before awaiting promises");
+ await Promise.all(toggle_event_promises);
+ assert_array_equals(toggle_event_received_ids, ["e3", "e2", "e1", "e0"], "toggle events received after awaiting promises, including toggle events from parser insertion");
+}, "mutation event and toggle event order");
+
+// This function is used to guard tests that test behavior that is
+// relevant only because of Mutation Events. If mutation events (for
+// attribute addition/removal) are removed from the web, the tests using
+// this function can be removed.
+function mutation_events_for_attribute_removal_supported() {
+ if (!("MutationEvent" in window)) {
+ return false;
+ }
+ container.innerHTML = `<div id="event-removal-test"></div>`;
+ let element = container.firstChild;
+ let event_fired = false;
+ element.addEventListener("DOMSubtreeModified", event => event_fired = true);
+ element.removeAttribute("id");
+ return event_fired;
+}
+
+promise_test(async t => {
+ if (!mutation_events_for_attribute_removal_supported()) {
+ return;
+ }
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="a" id="e2" open></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ let received_ids = [];
+ let listener = event => {
+ received_ids.push(event.target.id);
+ let i = 0;
+ for (let element of elements) {
+ element.setAttribute("name", `b${i++}`);
+ }
+ };
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", listener);
+ }
+ assert_array_equals(received_ids, []);
+ assert_element_states(elements, [1, 0, 0], "states before mutation");
+ elements[1].open = true;
+ assert_array_equals(received_ids, ["e1"],
+ "mutation events received only for open attribute mutation and not for closing other element");
+ assert_element_states(elements, [0, 1, 0], "states after mutation");
+}, "interaction of open attribute changes with mutation events");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details></details>
+ <details></details>
+ <details name></details>
+ <details name></details>
+ <details name=""></details>
+ <details name=""></details>
+ `;
+ let elements = Array.from(container.querySelectorAll("details"));
+
+ assert_element_states(elements, [0, 0, 0, 0, 0, 0], "initial states from open attribute");
+ for (let i = 0; i < 6; ++i) {
+ elements[i].open = true;
+ }
+ assert_element_states(elements, [1, 1, 1, 1, 1, 1], "after setting all elements open");
+}, "empty and missing name attributes do not create groups");
+
+const connected_scenarios = {
+ "connected": {
+ "create": data => container,
+ "cleanup": data => {},
+ },
+ "disconnected": {
+ "create": data => document.createElement("div"),
+ "cleanup": data => {},
+ },
+ "shadow": {
+ "create": data => {
+ let e = document.createElement("div");
+ container.appendChild(e);
+ data.wrapper = e;
+ let shadowRoot = e.attachShadow({ mode: "open" });
+ let d = document.createElement("div");
+ shadowRoot.appendChild(d);
+ return d;
+ },
+ "cleanup": data => { data.wrapper.remove(); },
+ },
+ "shadow-in-disconnected": {
+ "create": data => {
+ let e = document.createElement("div");
+ let shadowRoot = e.attachShadow({ mode: "open" });
+ let d = document.createElement("div");
+ shadowRoot.appendChild(d);
+ return d;
+ },
+ "cleanup": data => {},
+ },
+ "template-in-disconnected": {
+ "create": data => {
+ let e = document.createElement("div");
+ e.innerHTML = `
+ <template>
+ <div></div>
+ </template>
+ `;
+ return e.firstElementChild.content.firstElementChild;
+ },
+ "cleanup": data => {},
+ },
+ "connected-in-xhr-response": {
+ "create": data => new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", "support/empty-html-document.html");
+ xhr.responseType = "document";
+ xhr.send();
+ xhr.addEventListener("load", event => { resolve(xhr.response.body); });
+ let reject_with_type =
+ event => { reject(`${event.type} event received`); }
+ xhr.addEventListener("error", reject_with_type);
+ xhr.addEventListener("abort", reject_with_type);
+ }),
+ "cleanup": data => {},
+ },
+ "connected-in-implementation-create-document": {
+ "create": data => {
+ let doc = document.implementation.createHTMLDocument("impl-created");
+ return doc.body;
+ },
+ "cleanup": data => {},
+ },
+ "connected-in-template": {
+ "create": data => {
+ container.innerHTML = `
+ <template>
+ <div></div>
+ </template>
+ `;
+ return container.firstElementChild.content.firstElementChild;
+ },
+ "cleanup": data => { container.innerHTML = ""; },
+ },
+};
+
+for (const [scenario, scenario_callbacks] of Object.entries(connected_scenarios)) {
+ promise_test(async t => {
+ let data = {};
+ let container = await scenario_callbacks.create(data);
+ t.add_cleanup(async () => await scenario_callbacks.cleanup(data));
+ assert_true(container instanceof HTMLDivElement ||
+ container instanceof HTMLBodyElement,
+ "error in test setup");
+
+ container.innerHTML = `
+ <details name="scenariotest" open></details>
+ <details name="scenariotest"></details>
+ `;
+
+ let elements = Array.from(container.querySelectorAll("details[name='scenariotest']"));
+ assert_element_states(elements, [1, 0], "state before toggle");
+ elements[1].open = true;
+ assert_element_states(elements, [0, 1], "state after toggle enforces exclusivity");
+ }, `exclusivity enforcement with attachment scenario ${scenario}`);
+}
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ <details name="b" id="e2" open></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ let mutation_received_ids = [];
+ let listener = event => {
+ mutation_received_ids.push(event.target.id);
+ };
+ for (let element of elements) {
+ element.addEventListener("DOMSubtreeModified", listener);
+ }
+
+ assert_element_states(elements, [1, 0, 1], "states before first mutation");
+ assert_array_equals(mutation_received_ids, [], "mutation events received before first mutation");
+ elements[2].name = "a";
+ assert_element_states(elements, [1, 0, 0], "states after first mutation");
+ if (mutation_received_ids.length != 0) {
+ // OK to not support mutation events, or to send DOMSubtreeModified
+ // only for attribute addition/removal (open) but not for attribute
+ // change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received after first mutation");
+ }
+ elements[0].name = "c";
+ elements[2].open = true;
+ assert_element_states(elements, [1, 0, 1], "states before second mutation");
+ if (mutation_received_ids.length != 0) { // OK to not support mutation events
+ if (mutation_received_ids.length == 1) {
+ // OK to receive DOMSubtreeModified for attribute addition/removal
+ // (open) but not for attribute change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received before second mutation");
+ } else {
+ assert_array_equals(mutation_received_ids, ["e2", "e0", "e2"], "mutation events received before second mutation");
+ }
+ }
+ elements[0].name = "a";
+ assert_element_states(elements, [0, 0, 1], "states after second mutation");
+ if (mutation_received_ids.length != 0) { // OK to not support mutation events
+ if (mutation_received_ids.length == 1) {
+ // OK to receive DOMSubtreeModified for attribute addition/removal
+ // (open) but not for attribute change (name)
+ assert_array_equals(mutation_received_ids, ["e2"], "mutation events received before second mutation");
+ } else {
+ assert_array_equals(mutation_received_ids, ["e2", "e0", "e2", "e0"], "mutation events received after second mutation");
+ }
+ }
+}, "handling of name attribute changes");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1" open></details>
+ <details open name="a" id="e2"></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1"),
+ document.getElementById("e2") ];
+
+ assert_element_states(elements, [1, 0, 0], "states after insertion by parser");
+}, "closing as a result of parsing doesn't depend on attribute order");
+
+promise_test(async t => {
+ container.innerHTML = `
+ <details name="a" id="e0" open></details>
+ <details name="a" id="e1"></details>
+ `;
+ let elements = [ document.getElementById("e0"),
+ document.getElementById("e1") ];
+
+ assert_element_states(elements, [1, 0], "states before first mutation");
+
+ let make_details = () => {
+ let e = document.createElement("details");
+ e.setAttribute("name", "a");
+ return e;
+ };
+
+ let watch_e0 = new EventWatcher(t, elements[0], ['toggle']);
+ let watch_e1 = new EventWatcher(t, elements[1], ['toggle']);
+
+ let expect_opening = async (watcher) => {
+ await watcher.wait_for(['toggle'], {record: 'all'}).then((events) => {
+ assert_equals(events[0].oldState, "closed");
+ assert_equals(events[0].newState, "open");
+ });
+ };
+
+ let expect_closing = async (watcher) => {
+ await watcher.wait_for(['toggle'], {record: 'all'}).then((events) => {
+ assert_equals(events[0].oldState, "open");
+ assert_equals(events[0].newState, "closed");
+ });
+ };
+
+ let track_mutations = (element) => {
+ let result = { count: 0 };
+ let listener = event => {
+ ++result.count;
+ };
+ element.addEventListener("DOMSubtreeModified", listener);
+ return result;
+ }
+
+ await expect_opening(watch_e0);
+
+ // Test appending an open element in the group.
+ let new1 = make_details();
+ let mutations1 = track_mutations(new1);
+ let watch_new1 = new EventWatcher(t, new1, ['toggle']);
+ new1.open = true;
+ assert_in_array(mutations1.count, [0, 1], "mutation events count before inserting new1");
+ await expect_opening(watch_new1);
+ container.appendChild(new1);
+ await expect_closing(watch_new1);
+ assert_in_array(mutations1.count, [0, 1], "mutation events count after inserting new1");
+
+ // Test appending a closed element in the group.
+ let new2 = make_details();
+ let mutations2 = track_mutations(new2);
+ let watch_new2 = new EventWatcher(t, new2, ['toggle']);
+ container.appendChild(new2);
+ assert_equals(mutations2.count, 0, "mutation events count after inserting new2");
+
+ // Test inserting an open element at the start of the group.
+ let new3 = make_details();
+ let mutations3 = track_mutations(new3);
+ new3.open = true; // this time do this before creating the EventWatcher
+ let watch_new3 = new EventWatcher(t, new3, ['toggle']);
+ assert_in_array(mutations3.count, [0, 1], "mutation events count before inserting new3");
+ await expect_opening(watch_new3);
+ container.insertBefore(new3, elements[0]);
+ await expect_closing(watch_new3);
+ assert_in_array(mutations3.count, [0, 1], "mutation events count after inserting new3");
+}, "handling of insertion of elements into group");
+
+</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/support/empty-html-document.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html
new file mode 100644
index 0000000000..56415b8476
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/support/empty-html-document.html
@@ -0,0 +1,2 @@
+<!DOCTYPE HTML>
+<title>Empty HTML Document</title>
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..c918f8eb62
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html
@@ -0,0 +1,183 @@
+<!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>
+ <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>
+ <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, with 'oldState: closed' and 'newState: open'"),
+ t2 = async_test("Adding open to 'details' and then removing open from that 'details' should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t3 = async_test("Adding open to 'details' (display:none) should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t4 = async_test("Adding open to 'details' (no children) should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t6 = async_test("Adding open to 'details' and then removing open from that 'details' and then again adding open to that 'details' should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t7 = async_test("Adding open to 'details' using setAttribute('open', '') should fire a toggle event at the 'details' element, with 'oldState: closed' and 'newState: open'"),
+ t8 = async_test("Adding open to 'details' and then calling removeAttribute('open') should fire only one toggle event at the 'details' element, with 'oldState: closed' and 'newState: closed'"),
+ t9 = async_test("Setting open=true on an opened 'details' element should not fire a toggle event at the 'details' element"),
+ t10 = async_test("Setting open=false on a 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), ToggleEvent.prototype, "Prototype of toggle event is ToggleEvent.prototype");
+ }
+
+ details1.ontoggle = t1.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details1.open);
+ testEvent(evt)
+ });
+ details1.open = true; // opens details1
+
+ details2.ontoggle = t2.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ assert_false(details2.open);
+ testEvent(evt);
+ });
+ details2.open = true;
+ details2.open = false; // closes details2
+
+ details3.ontoggle = t3.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details3.open);
+ testEvent(evt);
+ });
+ details3.open = true; // opens details3
+
+ details4.ontoggle = t4.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ 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_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ 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, with 'oldState: closed' and 'newState: open'");
+
+ details6.open = true;
+ details6.open = false;
+ details6.ontoggle = t6.step_func(function(evt) {
+ if (loop) {
+ assert_unreached("toggle event fired twice");
+ } else {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ loop = true;
+ }
+ });
+ t6.step_timeout(function() {
+ assert_true(loop);
+ t6.done();
+ }, 0);
+
+ details7.ontoggle = t7.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "open");
+ assert_true(details7.open);
+ testEvent(evt)
+ });
+ details7.setAttribute('open', ''); // opens details7
+
+ details8.ontoggle = t8.step_func_done(function(evt) {
+ assert_equals(evt.oldState, "closed");
+ assert_equals(evt.newState, "closed");
+ assert_false(details8.open);
+ testEvent(evt)
+ });
+ details8.open = true;
+ 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);
+
+ async_test(function(t) {
+ new DOMParser().parseFromString("<details open>", "text/html").querySelector("details").ontoggle = t.step_func_done(function(e) {
+ assert_true(e.target.open);
+ });
+ }, "Setting open from the parser fires a toggle event");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml
new file mode 100644
index 0000000000..f270236cff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: dialog
+ files: "**"
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..73706548ea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-descendant-selector.html
@@ -0,0 +1,55 @@
+<!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 {
+ visibility: visible;
+ 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-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..6c609a769c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-dynamic-style-change.html
@@ -0,0 +1,35 @@
+<!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 {
+ visibility: visible;
+ 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..06f790979e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-in-flow.html
@@ -0,0 +1,39 @@
+<!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 {
+ visibility: visible;
+ 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-inherits-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-ref.html
new file mode 100644
index 0000000000..d9f4cb2c84
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits-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 inherits from its originating element. 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-inherits.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html
new file mode 100644
index 0000000000..458320f019
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-inherits.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="match" href="backdrop-inherits-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#backdrop">
+<style>
+dialog {
+ --backdrop-bg: green;
+ visibility: hidden;
+}
+
+dialog::backdrop {
+ position: absolute;
+ top: 100px;
+ left: 100px;
+ height: 100px;
+ width: 100px;
+ visibility: visible;
+ background-color: var(--backdrop-bg);
+}
+</style>
+Test that ::backdrop inherits from its originating element. The test passes if
+there is a green box and no red.
+<dialog></dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
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..5d515000ce
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html
@@ -0,0 +1,50 @@
+<!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 {
+ visibility: visible;
+ pointer-events: initial;
+ 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..897f54a53f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/backdrop-stacking-order.html
@@ -0,0 +1,81 @@
+<!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;
+ outline: none;
+}
+
+#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/child-sequential-focus.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html
new file mode 100644
index 0000000000..bc787202cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/child-sequential-focus.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/8199">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog autofocus id=autofocusdialog data-description="dialog element with autofocus should get initial focus." class=target>
+ <button>focusable button</button>
+ <button autofocus>autofocusable button</button>
+</dialog>
+
+<dialog id=keyboardfocusdialog data-description="Only keyboard-focusable elements should get dialog initial focus.">
+ <button tabindex="-1">mouse focusable button</button>
+ <button class=target>keyboard focusable button</button>
+</dialog>
+
+<dialog id=autofocuswithoutkeyboarddialog data-description="Autofocus takes precedence over keyboard-focusable requirement.">
+ <button>keyboard focusable button</button>
+ <button tabindex="-1" autofocus class=target>mouse focusable autofocus button</button>
+</dialog>
+
+<dialog id=subtree data-description="Only keyboard-focusable elements should get dialog initial focus including in subtrees.">
+ <div>
+ <button tabindex="-1">mouse focusable button</button>
+ <button class=target>keyboard focusable button</button>
+ </div>
+</dialog>
+
+<dialog id=nestedbuttons data-description="Only keyboard-focusable elements should get dialog initial focus including in nested buttons.">
+ <button tabindex="-1">
+ <span>mouse focusable button</span>
+ <button tabindex="-1">nested mouse focusable button</button>
+ </button>
+ <button class=target>keyboard focusable button</button>
+</dialog>
+
+<script>
+document.querySelectorAll('dialog').forEach(dialog => {
+ test(t => {
+ let target = dialog.querySelector('.target');
+ if (dialog.classList.contains('target')) {
+ target = dialog;
+ }
+ t.add_cleanup(() => {
+ if (dialog.open)
+ dialog.close();
+ });
+
+ dialog.showModal();
+ assert_equals(document.activeElement, target,
+ 'showModal: the target element did not receive initial focus.');
+ dialog.close();
+
+ dialog.show();
+ assert_equals(document.activeElement, target,
+ 'show: the target element did not receive initial focus.');
+ dialog.close();
+ }, dialog.dataset.description);
+});
+</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.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
new file mode 100644
index 0000000000..d583c4cd23
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-events.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test cancel event is fired when the dialog is closed by user close requests</title>
+<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">
+<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="/common/top-layer.js"></script>
+<script src="/close-watcher/resources/helpers.js"></script>
+
+<dialog>
+ <p>Hello World</p>
+</dialog>
+
+<script type="module">
+setup({ single_test: true });
+
+const dialog = document.querySelector("dialog");
+const events = [];
+
+dialog.addEventListener("cancel", event => {
+ assert_true(event.cancelable, "cancel event should be cancelable");
+ assert_array_equals(events, []);
+
+ events.push("addEventListener cancel");
+});
+
+assert_equals(dialog.oncancel, null);
+dialog.oncancel = () => {
+ assert_array_equals(events, ["addEventListener cancel"]);
+
+ events.push("oncancel");
+};
+
+dialog.addEventListener("close", () => {
+ assert_array_equals(events, ["addEventListener cancel", "oncancel"]);
+
+ events.push("addEventListener close");
+});
+
+assert_equals(dialog.onclose, null);
+dialog.onclose = () => {
+ assert_array_equals(events, ["addEventListener cancel", "oncancel", "addEventListener close"]);
+
+ done();
+};
+
+dialog.showModal();
+await blessTopLayer(dialog);
+await sendCloseRequest();
+</script>
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..4daffc09a4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-cancel-preventDefault.html
@@ -0,0 +1,49 @@
+<!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>
+ <script src="/common/top-layer.js"></script>
+ <script src="/close-watcher/resources/helpers.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 type=module>
+ 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();
+
+ await blessTopLayer(dialog);
+ await sendCloseRequest();
+</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..e368bde6fb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html
@@ -0,0 +1,107 @@
+<!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>
+<script src="/common/top-layer.js"></script>
+<script src="/close-watcher/resources/helpers.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>
+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();
+ await blessTopLayer(bottomDialog);
+ topDialog.showModal();
+
+ await blessTopLayer(topDialog);
+ await sendEscKey();
+ assert_true(topDialog.open, 'Top dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Top dialog event listener should prevent closing.');
+
+ await blessTopLayer(topDialog);
+ await sendEscKey();
+ assert_false(topDialog.open, 'Top dialog should close.');
+ assert_true(bottomDialog.open, 'Top dialog should close.');
+
+ swallowInput.focus();
+ await sendEscKey();
+ await sendEscKey();
+ await sendEscKey();
+ assert_false(topDialog.open, 'Input should swallow Escape mechanism.');
+ assert_true(bottomDialog.open, 'Input should swallow Escape mechanism.');
+
+ normalInput.focus();
+ await sendEscKey();
+ assert_false(topDialog.open, 'Bottom dialog event listener should prevent closing.');
+ assert_true(bottomDialog.open, 'Bottom dialog event listener should prevent closing.');
+
+ await sendEscKey();
+ assert_false(topDialog.open, 'Bottom dialog should close.');
+ assert_false(bottomDialog.open, 'Bottom dialog should close.');
+
+ await sendEscKey();
+ 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-via-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html
new file mode 100644
index 0000000000..5c2e70f87a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-close-via-attribute.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/5802">
+<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>
+
+<button>button</button>
+<dialog>hello world</dialog>
+
+<script>
+const dialog = document.querySelector('dialog');
+const button = document.querySelector('button');
+
+promise_test(async t => {
+ dialog.showModal();
+
+ let closeFired = false;
+ let cancelFired = false;
+ dialog.addEventListener('close', () => closeFired = true);
+ dialog.addEventListener('cancel', () => cancelFired = true);
+
+ dialog.removeAttribute('open');
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(requestAnimationFrame);
+
+ assert_false(dialog.matches(':modal'),
+ 'The dialog should not match :modal after closing.');
+ assert_false(cancelFired,
+ 'The cancel event should not fire when removing the open attribute.');
+ assert_true(closeFired,
+ 'The close event should be fired when removing the open attribute.');
+
+ let buttonFiredClick = false;
+ button.addEventListener('click', () => buttonFiredClick = true);
+ await test_driver.click(button);
+ assert_true(buttonFiredClick,
+ 'The page should not be inert or blocked after removing the open attribute.');
+}, 'Removing the open attribute from an open modal dialog should run the closing algorithm.');
+
+promise_test(async t => {
+ dialog.show();
+
+ let closeFired = false;
+ let cancelFired = false;
+ dialog.addEventListener('close', () => closeFired = true);
+ dialog.addEventListener('cancel', () => cancelFired = true);
+
+ dialog.removeAttribute('open');
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(requestAnimationFrame);
+
+ assert_false(cancelFired,
+ 'The cancel event should not fire when removing the open attribute.');
+ assert_true(closeFired,
+ 'The close event should be fired when removing the open attribute.');
+}, 'Removing the open attribute from an open non-modal dialog should fire a close event.');
+</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-previous-outside.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html
new file mode 100644
index 0000000000..43a17676ed
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-previous-outside.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/issues/8904">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<button id=b1>button 1</button>
+<button id=b2>button 2</button>
+<div id=host>
+ <template shadowrootmode=open>
+ <button>button in shadowroot outside dialog</button>
+ </template>
+</div>
+<dialog id=mydialog>
+ <button id=b3>button in dialog</button>
+ <div id=dialoghost>
+ <template shadowrootmode=open>
+ <button>button in shadowroot in dialog</button>
+ </template>
+ </div>
+</dialog>
+
+<div id=host2>
+ <template shadowrootmode=open>
+ <dialog>
+ <slot></slot>
+ </dialog>
+ </template>
+ <button id=host2button>button</button>
+</div>
+
+<dialog id=mydialog2>hello world</dialog>
+
+<script>
+test(() => {
+ b1.focus();
+ mydialog.show();
+ b2.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus should not be restored if the currently focused element is not inside the dialog.');
+
+test(() => {
+ const shadowbutton = host.shadowRoot.querySelector('button');
+ b2.focus();
+ mydialog.show();
+ shadowbutton.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, host, 'document.activeElement should point at the shadow host.');
+ assert_equals(host.shadowRoot.activeElement, shadowbutton, 'The button in the shadowroot should remain focused.');
+}, 'Focus restore should not occur when the focused element is in a shadowroot outside of the dialog.');
+
+test(() => {
+ const shadowbutton = dialoghost.shadowRoot.querySelector('button');
+ b2.focus();
+ mydialog.show();
+ shadowbutton.focus();
+ mydialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus restore should occur when the focused element is in a shadowroot inside the dialog.');
+
+test(() => {
+ const dialog = host2.shadowRoot.querySelector('dialog');
+ b2.focus();
+ dialog.show();
+ host2button.focus();
+ dialog.close();
+ assert_equals(document.activeElement, b2);
+}, 'Focus restore should occur when the focused element is slotted into a dialog.');
+
+test(() => {
+ b1.focus();
+ const dialog = document.getElementById('mydialog2');
+ dialog.showModal();
+ dialog.blur();
+ assert_equals(document.activeElement, document.body,
+ 'Focus should return to the body when calling dialog.blur().');
+ dialog.close();
+ assert_equals(document.activeElement, b1,
+ 'Focus should be restored to the previously focused element.');
+}, 'Focus restore should always occur for modal dialogs.');
+</script>
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..7e57685425
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focus-shadow.html
@@ -0,0 +1,274 @@
+<!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 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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button 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 class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, yes delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button>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 class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus before, no delegatesFocus">
+ <button autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button disabled>Non-focusable</button>
+ <button>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus before, yes delegatesFocus">
+ <button autofocus class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button disabled>Non-focusable</button>
+ <button>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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button 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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button 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 class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling before">
+ <button>Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus autofocus">
+ <button disabled>Non-focusable</button>
+ <button 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 class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree autofocus">
+ <button disabled>Non-focusable</button>
+ <button>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>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button 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>Focusable</button>
+ <button 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 class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button>Focusable</button>
+ <button 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>Focusable</button>
+ <button autofocus class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button>Focusable</button>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, no siblings">
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling before">
+ <button class="focus-me">Focusable</button>
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <button 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>Focusable</button>
+ <button autofocus>Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <button 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 class="focus-me">Focusable</button>
+ <button disabled>Non-focusable</button>
+ </template>
+ <template class="turn-into-shadow-tree delegates-focus">
+ <button autofocus>Focusable</button>
+ </template>
+</dialog>
+
+<dialog data-description="No autofocus, no delegatesFocus, slotted target">
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ <slot></slot>
+ <button>Focusable</button>
+ </template>
+ <button class="focus-me">Focusable</button>
+</dialog>
+
+<dialog data-description="Shadowroot on child, no autofocus, no delegatesFocus">
+ <div>
+ <template class="turn-into-shadow-tree">
+ <button>Focusable</button>
+ </template>
+ </div>
+ <button 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. The dialog element should be focused.
+ assert_equals(document.activeElement, dialog);
+ }
+ }
+ }, `${method}: ${dialog.dataset.description}`);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html
new file mode 100644
index 0000000000..1e00086609
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusability.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>dialog element: focusability</title>
+<link rel=help href="https://github.com/whatwg/html/pull/8199">
+<link rel=author href="mailto:masonf@chromium.org">
+<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>
+
+<button id="before">before</button>
+<dialog id="dialog1" open>
+ <button id="within1">button</button>
+</dialog>
+<button id="after1">after 1</button>
+<dialog tabindex=0 id="dialog2" open>
+ <button id="within2">button</button>
+</dialog>
+<button id="after2">after 2</button>
+<dialog tabindex="-1" id="dialog3" open>
+ <button id="within3">button</button>
+</dialog>
+<button id="after3">after 3</button>
+<dialog contenteditable="true" id="dialog4" open>
+ <button id="within4">button</button>
+</dialog>
+<button id="after4">after 4</button>
+
+<style>
+ #dialog1 { top: 25px; }
+ #dialog2 { top: 100px; }
+ #dialog3 { top: 175px; }
+ #dialog4 { top: 250px; }
+</style>
+
+<script>
+ function navigateForward() {
+ const TAB = '\ue004';
+ return test_driver.send_keys(document.body, TAB);
+ }
+ async function assert_focus_order(elements) {
+ assert_true(elements.length >= 2);
+ elements[0].focus();
+ for(let i=0;i<elements.length;++i) {
+ assert_equals(document.activeElement,elements[i],`Focus order mismatch at step ${i+1}/${elements.length}`);
+ await navigateForward();
+ }
+ }
+
+ async_test((t) => {
+ window.onload = async () => {
+ await assert_focus_order([before,within1,after1,dialog2,within2,after2,
+ within3,after3,dialog4,within4,after4]);
+ t.done();
+ };
+ }, "The dialog element itself should not be keyboard focusable.");
+</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..4d1e792c1c
--- /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_not_equals(document.activeElement, input,
+ '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-unusual.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html
new file mode 100644
index 0000000000..ae0de29a89
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-form-submission-unusual.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Test dialog form submission, unusual cases</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=log></div>
+
+<dialog>
+ <form method=dialog action="https://test:test/" target=doesnotmatter rel=noopener>
+ <input type=submit formaction="https://test:test/" id=submit-1>
+ <input type=submit id=submit-2>
+ </form>
+</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector("dialog");
+ dialog.showModal();
+ assert_true(dialog.open);
+
+ document.getElementById("submit-1").click();
+ assert_false(dialog.open);
+}, "A form's action and rel=noopener are ignored during submission");
+
+test(() => {
+ const dialog = document.querySelector("dialog");
+ dialog.showModal();
+ assert_true(dialog.open);
+
+ document.getElementById("submit-2").click();
+ assert_false(dialog.open);
+}, "A form's action and rel=noopener are ignored during submission, part 2");
+
+</script>
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-no-throw-requested-state.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html
new file mode 100644
index 0000000000..c86cbe84a6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-no-throw-requested-state.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/9142">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<dialog>hello</dialog>
+
+<script>
+test(() => {
+ const dialog = document.querySelector('dialog');
+
+ // calling close() on a dialog that is already closed should not throw.
+ dialog.close();
+
+ dialog.show();
+ // calling show() on a dialog that is already showing non-modal should not throw.
+ dialog.show();
+ assert_throws_dom('InvalidStateError', () => dialog.showModal(),
+ 'Calling showModal() on a dialog that is already showing non-modal should throw.');
+ dialog.close();
+
+ dialog.showModal();
+ assert_throws_dom('InvalidStateError', () => dialog.show(),
+ 'Calling show() on a dialog that is already showing modal should throw.');
+ // calling showModal() on a dialog that is already showing modal should not throw.
+ dialog.showModal();
+});
+</script>
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-overlay-re-add-during-transition.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html
new file mode 100644
index 0000000000..30d104a973
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay-re-add-during-transition.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>dialog: close and re-add modal dialog during overlay transition</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#overlay">
+<link rel="match" href="pass-dialog-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<dialog id="dialog1">PASS</dialog>
+<dialog id="dialog2">FAIL</dialog>
+<style>
+ dialog::backdrop { background-color: black; }
+ #dialog1 {
+ transition-property: overlay, display;
+ transition-duration: 100s;
+ }
+</style>
+<script>
+ const dialog1 = document.getElementById("dialog1");
+ const dialog2 = document.getElementById("dialog2");
+
+ dialog1.showModal();
+ dialog2.showModal();
+ dialog1.close();
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ // dialog1 no longer "in top layer" even if rendered in top-layer, should
+ // be added as last top layer element.
+ dialog1.showModal();
+ takeScreenshot();
+ })
+ );
+</script>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html
new file mode 100644
index 0000000000..9cd2426b8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dialog-overlay.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>dialog: overlay</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel="help" href="https://drafts.csswg.org/css-position-4/#overlay">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<dialog id="dialog"></dialog>
+<script>
+ const dialog = document.getElementById("dialog");
+
+ test(() => {
+ assert_equals(getComputedStyle(dialog).overlay, "none",
+ "Computed overlay");
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+ }, "dialog computed overlay initially 'none'");
+
+ test(() => {
+ dialog.showModal();
+
+ assert_equals(getComputedStyle(dialog).overlay, "auto",
+ "Computed overlay on open dialog");
+ // ::backdrop pseudo element is always rendered in the top layer when its
+ // originating element is. It does not get its overlay property changed,
+ // though.
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+
+ dialog.close();
+
+ assert_equals(getComputedStyle(dialog).overlay, "none",
+ "Computed overlay on closed dialog");
+ assert_equals(getComputedStyle(dialog, "::backdrop").overlay, "none",
+ "Computed overlay for ::backdrop");
+ }, "Opening and closing a modal dialog changes computed overlay to 'auto' and back to 'none'");
+</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..47612e759e
--- /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, d6);
+ }, "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 promote d10 to the top.
+ d10.removeAttribute("open");
+ assert_equals(topElement(), d11);
+ d10.showModal();
+ assert_equals(topElement(), d10);
+
+ // 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..7e6112b3ce
--- /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,16 @@
+<!doctype html>
+<style>
+dialog {
+ outline: none;
+}
+#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..e4f4ce50b3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/dont-share-style-to-top-layer.html
@@ -0,0 +1,18 @@
+<!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;
+}
+#modal {
+ outline: none;
+}
+</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..0dead33163
--- /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,35 @@
+<!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;
+ outline: none;
+}
+</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..5ee64fc1d9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-contain-ancestor.html
@@ -0,0 +1,27 @@
+<!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;
+}
+#dialog {
+ outline: 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..2bc294be2f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-fo-ancestor.html
@@ -0,0 +1,29 @@
+<!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;
+}
+#dialog {
+ outline: 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.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html
new file mode 100644
index 0000000000..527d508252
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-transformed-ancestor.tentative.html
@@ -0,0 +1,27 @@
+<!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;
+}
+#dialog {
+ outline: 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.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html
new file mode 100644
index 0000000000..e9db7321cd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/fixed-position-child-with-will-change-ancestor.tentative.html
@@ -0,0 +1,27 @@
+<!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;
+}
+#dialog {
+ outline: 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..93baf65cf6
--- /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", resolve, { once: true });
+ 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/focus-previous-iframe.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html
new file mode 100644
index 0000000000..c31daa4876
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/focus-previous-iframe.tentative.html
@@ -0,0 +1,52 @@
+<!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>
+<dialog>Dialog in parent</dialog>
+
+<iframe srcdoc="<input><dialog> Dialog in child </dialog>"></iframe>
+
+<input>
+<script>
+test(() => {
+ window.onload = function() {
+ const iframe = document.querySelector("iframe");
+ const input = iframe.contentDocument.querySelector("input");
+ // <input> in the child document is focused
+ input.focus();
+
+ const dialog = document.querySelector("dialog");
+ // <dialog> in the parent document is opened
+ dialog.showModal();
+ dialog.close();
+
+ assert_equals(document.activeElement, iframe);
+ assert_equals(iframe.contentDocument.activeElement, input);
+ }
+}, "Focus should move back from parent document to child document");
+
+test(() => {
+ window.onload = function() {
+ const iframe = document.querySelector("iframe");
+ const input = document.querySelector("input");
+ // <input> in the parent document is focused
+ input.focus();
+
+ const dialog = iframe.contentDocument.querySelector("dialog");
+
+ // <dialog> in the child document is focused
+ dialog.showModal();
+ dialog.close();
+
+ assert_equals(document.activeElement, input);
+ }
+}, "Focus should move back from child document to parent document");
+</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..61e3ddeaf2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
@@ -0,0 +1,53 @@
+<!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');
+ submit = document.querySelector('#submit');
+ label.focus();
+ assert_equals(document.activeElement, submit,
+ 'label.focus() should send focus to the target.');
+
+ await clickOn(label);
+ assert_not_equals(document.activeElement, label,
+ 'Clicking the label should not focus the label.');
+ assert_not_equals(document.activeElement, submit,
+ 'Clicking the label should not focus the submit input.');
+}, '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..74379f50e2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html
@@ -0,0 +1,75 @@
+<!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">
+<style>
+dialog {
+ outline: none;
+}
+</style>
+<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-opacity-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html
new file mode 100644
index 0000000000..b50e6ae026
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<link rel="stylesheet" href="resources/dialog.css">
+<style>
+.backdrop {
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ background-color: green;
+ opacity: 0.5;
+}
+</style>
+<body>
+<div class="backdrop"></div>
+<div class="pseudodialog">Test passes if you see a green backdrop at half opacity.</div>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html
new file mode 100644
index 0000000000..09c31ce2af
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop-opacity.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="match" href="modal-dialog-backdrop-opacity-ref.html">
+<link rel="help" href="https://fullscreen.spec.whatwg.org/#user-agent-level-style-sheet-defaults">
+<style>
+dialog::backdrop {
+ background-color: rgb(0, 128, 0);
+ opacity: 0.5;
+}
+dialog:focus {
+ outline: none;
+}
+</style>
+<body>
+<dialog>Test passes if you see a green backdrop at half opacity.</dialog>
+<script>
+document.querySelector('dialog').showModal();
+</script>
+</body>
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..55d7132f8c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-backdrop.html
@@ -0,0 +1,21 @@
+<!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;
+ outline: none;
+}
+</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..96b97f8ec3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/modal-dialog-generated-content.html
@@ -0,0 +1,59 @@
+<!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;
+ outline: none;
+}
+
+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/pass-dialog-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html
new file mode 100644
index 0000000000..6f1a8fde21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/pass-dialog-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<style>
+ dialog::backdrop { background-color: black; }
+</style>
+<dialog id="dialog">PASS</dialog>
+<script>
+ dialog.showModal();
+</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..6a2ad8c4a0
--- /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 the dialog if the dialog has no focusable elements
+ var outerDialog = document.getElementById('outer-dialog');
+ outerDialog.showModal();
+ assert_equals(document.activeElement, outerDialog);
+
+ // 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..0886c2cd2c
--- /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; outline: none">
+ 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..ba790c1db9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-display-none.html
@@ -0,0 +1,60 @@
+<!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;
+ outline: none;
+}
+
+::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..9e0616e952
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-nesting.html
@@ -0,0 +1,66 @@
+<!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;
+ outline: none;
+}
+
+::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..6e3c52aa02
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-clip.html
@@ -0,0 +1,29 @@
+<!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;
+ outline: none;
+}
+</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..589d539779
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-filter.html
@@ -0,0 +1,30 @@
+<!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;
+ outline: none;
+}
+</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..8ba3ed47c2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-mask.html
@@ -0,0 +1,30 @@
+<!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;
+ outline: none;
+}
+</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..46c5de2a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-opacity.html
@@ -0,0 +1,30 @@
+<!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;
+ outline: none;
+}
+</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..d805954969
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-clip.html
@@ -0,0 +1,35 @@
+<!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;
+ outline: none;
+}
+</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..f5389ddc09
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-hidden.html
@@ -0,0 +1,34 @@
+<!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;
+ outline: none;
+}
+</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..a230defeea
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-overflow-scroll.html
@@ -0,0 +1,35 @@
+<!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;
+ outline: none;
+}
+</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..ac6f3cffc3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-parent-transform.html
@@ -0,0 +1,29 @@
+<!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;
+ outline: none;
+}
+</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-remove-popover-attribute-ref.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html
new file mode 100644
index 0000000000..7aadaf51b7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<head>
+ <title>Shown modal dialog where the popover attribute is removed</title>
+</head>
+<body>
+ <dialog popover style="padding: 2em"></dialog>
+ <script>
+ const d = document.querySelector("dialog");
+ d.showModal();
+ </script>
+</body>
+<html>
+
diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html
new file mode 100644
index 0000000000..3827e90c5b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-remove-popover-attribute.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<meta name="assert" content="Removing the popover attribute of a hidden popover should not remove the dialog from the top layer.">
+<head>
+ <title>Shown modal dialog where the popover attribute is removed</title>
+ <link rel="help" href="https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm">
+ <link rel="match" href="top-layer-remove-popover-attribute-ref.html">
+</head>
+<body>
+ <dialog popover style="padding: 2em"></dialog>
+ <script>
+ const d = document.querySelector("dialog");
+ d.showModal();
+ d.popover = null;
+ </script>
+</body>
+<html>
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..4fdd28820d
--- /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,46 @@
+<!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;
+ outline: none;
+}
+
+::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..ebccdc66cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking-dynamic.html
@@ -0,0 +1,55 @@
+<!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;
+ outline: none;
+}
+
+::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.tentative.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-dialog-element/top-layer-stacking.tentative.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.tentative.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>