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