diff --git a/html/semantics/popovers/popover-attribute-basic.html b/html/semantics/popovers/popover-attribute-basic.html index 13108949cb3c5e..2af3bbc137f1a0 100644 --- a/html/semantics/popovers/popover-attribute-basic.html +++ b/html/semantics/popovers/popover-attribute-basic.html @@ -1,6 +1,7 @@ + diff --git a/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html b/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html new file mode 100644 index 00000000000000..4520ab05770404 --- /dev/null +++ b/html/semantics/popovers/popover-top-layer-nesting-anchor.tentative.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + +
+
Single popover=auto ancestor +
+
+ +
Single popover=manual ancestor +
+
+ +
Nested popover=auto ancestors +
+
+
+
+ +
Nested popover=auto ancestors, target is outer +
+
+
+
+ +
Top layer inside of nested element +
+ +
+
+
+ + diff --git a/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html b/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html new file mode 100644 index 00000000000000..4ec1f49bda9fbd --- /dev/null +++ b/html/semantics/popovers/popover-top-layer-nesting-hints.tentative.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + +
+
Single popover=hint ancestor +
+
+ +
Nested auto/hint ancestors +
+
+
+
+ +
Nested auto/hint ancestors, target is auto +
+
+
+
+ +
Unrelated hint, target=hint +
+
+
+ +
Unrelated hint, target=auto +
+
+
+
+ + diff --git a/html/semantics/popovers/popover-top-layer-nesting.tentative.html b/html/semantics/popovers/popover-top-layer-nesting.tentative.html new file mode 100644 index 00000000000000..a0b3b60b72b554 --- /dev/null +++ b/html/semantics/popovers/popover-top-layer-nesting.tentative.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + +
+
Single popover=auto ancestor +
+
+ +
Single popover=manual ancestor +
+
+ +
Nested popover=auto ancestors +
+
+
+
+ +
Nested popover=auto ancestors, target is outer +
+
+
+
+ +
Top layer inside of nested element +
+ +
+
+
+ + diff --git a/html/semantics/popovers/resources/popover-top-layer-nesting.js b/html/semantics/popovers/resources/popover-top-layer-nesting.js new file mode 100644 index 00000000000000..ace10b3f7bc47f --- /dev/null +++ b/html/semantics/popovers/resources/popover-top-layer-nesting.js @@ -0,0 +1,108 @@ +function createTopLayerElement(t,topLayerType) { + let element, show, showing; + switch (topLayerType) { + case 'dialog': + element = document.createElement('dialog'); + show = () => element.showModal(); + showing = () => element.matches(':modal'); + break; + case 'fullscreen': + element = document.createElement('div'); + show = async (topmostElement) => { + // Be sure to add user activation to the topmost visible target: + await blessTopLayer(topmostElement); + await element.requestFullscreen(); + }; + showing = () => document.fullscreenElement === element; + break; + default: + assert_unreached('Invalid top layer type'); + } + t.add_cleanup(() => element.remove()); + return {element,show,showing}; +} +function runTopLayerTests(testCases, testAnchorAttribute) { + testAnchorAttribute = testAnchorAttribute || false; + testCases.forEach(test => { + const description = test.firstChild.data.trim(); + assert_equals(test.querySelectorAll('.target').length,1,'There should be exactly one target'); + const target = test.querySelector('.target'); + assert_true(!!target,'Invalid test case'); + const popovers = Array.from(test.querySelectorAll('[popover]')); + assert_true(popovers.length > 0,'No popovers found'); + ['dialog','fullscreen'].forEach(topLayerType => { + promise_test(async t => { + const {element,show,showing} = createTopLayerElement(t,topLayerType); + target.appendChild(element); + + // Show the popovers. + t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); + popovers.forEach(popover => popover.showPopover()); + popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); + + // Activate the top layer element. + await show(popovers[popovers.length-1]); + assert_true(showing()); + popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior')); + + // Add another popover within the top layer element and make sure entire stack stays open. + const newPopover = document.createElement('div'); + t.add_cleanup(() => newPopover.remove()); + newPopover.popover = popoverHintSupported() ? 'hint' : 'auto'; + element.appendChild(newPopover); + popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Adding another popover shouldn\'t change anything')); + assert_true(showing(),'top layer element should still be top layer'); + newPopover.showPopover(); + assert_true(newPopover.matches(':popover-open')); + popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Showing the popover shouldn\'t change anything')); + assert_true(showing(),'top layer element should still be top layer'); + },`${description} with ${topLayerType}`); + + promise_test(async t => { + const {element,show,showing} = createTopLayerElement(t,topLayerType); + element.popover = popoverHintSupported() ? 'hint' : 'auto'; + target.appendChild(element); + + // Show the popovers. + t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); + popovers.forEach(popover => popover.showPopover()); + popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); + const targetWasOpenPopover = target.matches(':popover-open'); + + // Show the top layer element as a popover first. + element.showPopover(); + assert_true(element.matches(':popover-open'),'element should be open as a popover'); + assert_equals(target.matches(':popover-open'),targetWasOpenPopover,'target shouldn\'t change popover state'); + + try { + await show(element); + assert_unreached('It is an error to activate a top layer element that is already a showing popover'); + } catch (e) { + // We expect an InvalidStateError for dialogs, and a TypeError for fullscreens. + // Anything else should fall through to the test harness. + if (e.name !== 'InvalidStateError' && e.name !== 'TypeError') { + throw e; + } + } + },`${description} with ${topLayerType}, top layer element *is* a popover`); + + if (testAnchorAttribute) { + promise_test(async t => { + const {element,show,showing} = createTopLayerElement(t,topLayerType); + element.anchorElement = target; + document.body.appendChild(element); + + // Show the popovers. + t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); + popovers.forEach(popover => popover.showPopover()); + popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); + + // Activate the top layer element. + await show(popovers[popovers.length-1]); + assert_true(showing()); + popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior')); + },`${description} with ${topLayerType}, anchor attribute`); + } + }); + }); +}