summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html')
-rw-r--r--testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html607
1 files changed, 607 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html
new file mode 100644
index 0000000000..916d52ef5e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html
@@ -0,0 +1,607 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popover light dismiss behavior</title>
+<meta name="timeout" content="long">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popover.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/popover-utils.js"></script>
+
+<button id=b1t popovertarget='p1'>Popover 1</button>
+<button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button>
+<span id=outside>Outside all popovers</span>
+<div popover id=p1>
+ <span id=inside1>Inside popover 1</span>
+ <button id=b2 popovertarget='p2' popovertargetaction=show>Popover 2</button>
+ <span id=inside1after>Inside popover 1 after button</span>
+ <div popover id=p2>
+ <span id=inside2>Inside popover 2</span>
+ </div>
+</div>
+<button id=after_p1 tabindex="0">Next control after popover1</button>
+<style>
+ #p1 {top: 50px;}
+ #p2 {top: 120px;}
+ [popover] {bottom:auto;}
+ [popover]::backdrop {
+ /* This should *not* affect anything: */
+ pointer-events: auto;
+ }
+</style>
+<script>
+ const popover1 = document.querySelector('#p1');
+ const button1toggle = document.querySelector('#b1t');
+ const button1show = document.querySelector('#b1s');
+ const inside1After = document.querySelector('#inside1after');
+ const button2 = document.querySelector('#b2');
+ const popover2 = document.querySelector('#p2');
+ const outside = document.querySelector('#outside');
+ const inside1 = document.querySelector('#inside1');
+ const inside2 = document.querySelector('#inside2');
+ const afterp1 = document.querySelector('#after_p1');
+
+ let popover1HideCount = 0;
+ popover1.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover1HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+ let popover2HideCount = 0;
+ popover2.addEventListener('beforetoggle',(e) => {
+ if (e.newState !== "closed")
+ return;
+ ++popover2HideCount;
+ e.preventDefault(); // 'beforetoggle' should not be cancellable.
+ });
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ let p1HideCount = popover1HideCount;
+ await clickOn(outside);
+ assert_false(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount+1);
+ },'Clicking outside a popover will dismiss the popover');
+
+ promise_test(async (t) => {
+ const controller = new AbortController();
+ t.add_cleanup(() => controller.abort());
+ function addListener(eventName) {
+ document.addEventListener(eventName,(e) => e.preventDefault(),{signal:controller.signal,capture: true});
+ }
+ addListener('pointerdown');
+ addListener('pointerup');
+ addListener('mousedown');
+ addListener('mouseup');
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ let p1HideCount = popover1HideCount;
+ await clickOn(outside);
+ assert_false(popover1.matches(':popover-open'),'preventDefault should not prevent light dismiss');
+ assert_equals(popover1HideCount,p1HideCount+1);
+ },'Canceling pointer events should not keep clicks from light dismissing popovers');
+
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(inside1);
+ assert_true(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount);
+ popover1.hidePopover();
+ },'Clicking inside a popover does not close that popover');
+
+ promise_test(async () => {
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'));
+ await new test_driver.Actions()
+ .pointerMove(0, 0, {origin: outside})
+ .pointerDown()
+ .send();
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'),'pointerdown (outside the popover) should not hide the popover');
+ await new test_driver.Actions()
+ .pointerUp()
+ .send();
+ await waitForRender();
+ assert_false(popover1.matches(':popover-open'),'pointerup (outside the popover) should trigger light dismiss');
+ },'Popovers close on pointerup, not pointerdown');
+
+ promise_test(async (t) => {
+ t.add_cleanup(() => popover1.hidePopover());
+ assert_false(popover1.matches(':popover-open'));
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ async function testOne(eventName) {
+ document.body.dispatchEvent(new PointerEvent(eventName));
+ document.body.dispatchEvent(new MouseEvent(eventName));
+ document.body.dispatchEvent(new ProgressEvent(eventName));
+ await waitForRender();
+ assert_true(popover1.matches(':popover-open'),`A synthetic "${eventName}" event should not hide the popover`);
+ }
+ await testOne('pointerup');
+ await testOne('pointerdown');
+ await testOne('mouseup');
+ await testOne('mousedown');
+ },'Synthetic events can\'t close popovers');
+
+ promise_test(async (t) => {
+ t.add_cleanup(() => popover1.hidePopover());
+ popover1.showPopover();
+ await clickOn(inside1After);
+ assert_true(popover1.matches(':popover-open'));
+ await sendTab();
+ assert_equals(document.activeElement,afterp1,'Focus should move to a button outside the popover');
+ assert_true(popover1.matches(':popover-open'));
+ },'Moving focus outside the popover should not dismiss the popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ let p2HideCount = popover2HideCount;
+ await clickOn(inside2);
+ assert_true(popover1.matches(':popover-open'),'popover1 should be open');
+ assert_true(popover2.matches(':popover-open'),'popover2 should be open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1');
+ assert_equals(popover2HideCount,p2HideCount,'popover2');
+ popover1.hidePopover();
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking inside a child popover shouldn\'t close either popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ p2HideCount = popover2HideCount;
+ await clickOn(inside1);
+ assert_true(popover1.matches(':popover-open'));
+ assert_equals(popover1HideCount,p1HideCount);
+ assert_false(popover2.matches(':popover-open'));
+ assert_equals(popover2HideCount,p2HideCount+1);
+ popover1.hidePopover();
+ },'Clicking inside a parent popover should close child popover');
+
+ promise_test(async () => {
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'button2 should activate popover2');
+ p2HideCount = popover2HideCount;
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
+ assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case)');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover();
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(popover2.matches(':popover-open'));
+ p2HideCount = popover2HideCount;
+ await clickOn(button2);
+ assert_true(popover2.matches(':popover-open'),'popover2 should stay open');
+ assert_equals(popover2HideCount,p2HideCount,'popover2 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ assert_false(popover2.matches(':popover-open'));
+ },'Clicking on invoking element, after using it for activation, shouldn\'t close its popover (nested case, not used for invocation)');
+
+ promise_test(async () => {
+ popover1.showPopover(); // Directly show the popover
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1show);
+ assert_true(popover1.matches(':popover-open'),'popover1 should stay open');
+ assert_equals(popover1HideCount,p1HideCount,'popover1 should not get hidden and reshown');
+ popover1.hidePopover(); // Cleanup
+ assert_false(popover1.matches(':popover-open'));
+ },'Clicking on invoking element, even if it wasn\'t used for activation, shouldn\'t close its popover');
+
+ promise_test(async () => {
+ popover1.showPopover(); // Directly show the popover
+ assert_true(popover1.matches(':popover-open'));
+ await waitForRender();
+ p1HideCount = popover1HideCount;
+ await clickOn(button1toggle);
+ assert_false(popover1.matches(':popover-open'),'popover1 should be hidden by popovertarget');
+ assert_equals(popover1HideCount,p1HideCount+1,'popover1 should get hidden only once by popovertarget');
+ },'Clicking on popovertarget element, even if it wasn\'t used for activation, should hide it exactly once');
+
+ promise_test(async () => {
+ popover1.showPopover();
+ popover2.showPopover(); // Popover1 is an ancestral element for popover2.
+ assert_true(popover1.matches(':popover-open'));
+ assert_true(popover2.matches(':popover-open'));
+ const drag_actions = new test_driver.Actions();
+ // Drag *from* popover2 *to* popover1 (its ancestor).
+ await drag_actions.pointerMove(0,0,{origin: popover2})
+ .pointerDown({button: drag_actions.ButtonType.LEFT})
+ .pointerMove(0,0,{origin: popover1})
+ .pointerUp({button: drag_actions.ButtonType.LEFT})
+ .send();
+ assert_true(popover1.matches(':popover-open'),'popover1 should be open');
+ assert_true(popover2.matches(':popover-open'),'popover1 should be open');
+ popover1.hidePopover();
+ assert_false(popover2.matches(':popover-open'));
+ },'Dragging from an open popover outside an open popover should leave the popover open');
+</script>
+
+<button id=b3 popovertarget=p3>Popover 3 - button 3
+ <div popover id=p4>Inside popover 4</div>
+</button>
+<div popover id=p3>Inside popover 3</div>
+<div popover id=p5>Inside popover 5
+ <button popovertarget=p3>Popover 3 - button 4 - unused</button>
+</div>
+<style>
+ #p3 {top:100px;}
+ #p4 {top:200px;}
+ #p5 {top:200px;}
+</style>
+<script>
+ const popover3 = document.querySelector('#p3');
+ const popover4 = document.querySelector('#p4');
+ const popover5 = document.querySelector('#p5');
+ const button3 = document.querySelector('#b3');
+ promise_test(async () => {
+ await clickOn(button3);
+ assert_true(popover3.matches(':popover-open'),'invoking element should open popover');
+ popover4.showPopover();
+ assert_true(popover4.matches(':popover-open'));
+ assert_false(popover3.matches(':popover-open'),'popover3 is unrelated to popover4');
+ popover4.hidePopover(); // Cleanup
+ assert_false(popover4.matches(':popover-open'));
+ },'A popover inside an invoking element doesn\'t participate in that invoker\'s ancestor chain');
+
+ promise_test(async () => {
+ popover5.showPopover();
+ assert_true(popover5.matches(':popover-open'));
+ assert_false(popover3.matches(':popover-open'));
+ popover3.showPopover();
+ assert_true(popover3.matches(':popover-open'));
+ assert_false(popover5.matches(':popover-open'),'Popover 5 was not invoked from popover3\'s invoker');
+ popover3.hidePopover();
+ assert_false(popover3.matches(':popover-open'));
+ },'An invoking element that was not used to invoke the popover is not part of the ancestor chain');
+</script>
+
+<div popover id=p6>Inside popover 6
+ <div style="height:2000px;background:lightgreen"></div>
+ Bottom of popover6
+</div>
+<button popovertarget=p6>Popover 6</button>
+<style>
+ #p6 {
+ width: 300px;
+ height: 300px;
+ overflow-y: scroll;
+ }
+</style>
+<script>
+ const popover6 = document.querySelector('#p6');
+ promise_test(async () => {
+ popover6.showPopover();
+ assert_equals(popover6.scrollTop,0,'popover6 should start non-scrolled');
+ await new test_driver.Actions()
+ .scroll(0, 0, 0, 50, {origin: popover6})
+ .send();
+ await waitForRender();
+ assert_true(popover6.matches(':popover-open'),'popover6 should stay open');
+ assert_equals(popover6.scrollTop,50,'popover6 should be scrolled');
+ popover6.hidePopover();
+ },'Scrolling within a popover should not close the popover');
+</script>
+
+<my-element id="myElement">
+ <template shadowrootmode="open">
+ <button id=b7 popovertarget=p7 popovertargetaction=show tabindex="0">Popover7</button>
+ <div popover id=p7 style="top: 100px;">
+ <p>Popover content.</p>
+ <input id="inside7" type="text" placeholder="some text">
+ </div>
+ </template>
+</my-element>
+<script>
+ const button7 = document.querySelector('#myElement').shadowRoot.querySelector('#b7');
+ const popover7 = document.querySelector('#myElement').shadowRoot.querySelector('#p7');
+ const inside7 = document.querySelector('#myElement').shadowRoot.querySelector('#inside7');
+ promise_test(async () => {
+ button7.click();
+ assert_true(popover7.matches(':popover-open'),'invoking element should open popover');
+ inside7.click();
+ assert_true(popover7.matches(':popover-open'));
+ popover7.hidePopover();
+ },'Clicking inside a shadow DOM popover does not close that popover');
+
+ promise_test(async () => {
+ button7.click();
+ inside7.click();
+ assert_true(popover7.matches(':popover-open'));
+ await clickOn(outside);
+ assert_false(popover7.matches(':popover-open'));
+ },'Clicking outside a shadow DOM popover should close that popover');
+</script>
+
+<div popover id=p8>
+ <button tabindex="0">Button</button>
+ <span id=inside8after>Inside popover 8 after button</span>
+</div>
+<button id=p8invoker popovertarget=p8 tabindex="0">Popover8 invoker (no action)</button>
+<script>
+ promise_test(async () => {
+ const popover8 = document.querySelector('#p8');
+ const inside8After = document.querySelector('#inside8after');
+ const popover8Invoker = document.querySelector('#p8invoker');
+ assert_false(popover8.matches(':popover-open'));
+ popover8.showPopover();
+ await clickOn(inside8After);
+ assert_true(popover8.matches(':popover-open'));
+ await sendTab();
+ assert_equals(document.activeElement,popover8Invoker,'Focus should move to the invoker element');
+ assert_true(popover8.matches(':popover-open'),'popover should stay open');
+ popover8.hidePopover(); // Cleanup
+ },'Moving focus back to the invoker element should not dismiss the popover');
+</script>
+
+<!-- Convoluted ancestor relationship -->
+<div popover id=convoluted_p1>Popover 1
+ <button popovertarget=convoluted_p2>Open Popover 2</button>
+<div popover id=convoluted_p2>Popover 2
+ <button popovertarget=convoluted_p3>Open Popover 3</button>
+ <button popovertarget=convoluted_p2 popovertargetaction=show>Self-linked invoker</button>
+ </div>
+ <div popover id=convoluted_p3>Popover 3
+ <button popovertarget=convoluted_p4>Open Popover 4</button>
+ </div>
+ <div popover id=convoluted_p4><p>Popover 4</p></div>
+</div>
+<button onclick="convoluted_p1.showPopover()" tabindex="0">Open convoluted popover</button>
+<style>
+ #convoluted_p1 {top:50px;}
+ #convoluted_p2 {top:150px;}
+ #convoluted_p3 {top:250px;}
+ #convoluted_p4 {top:350px;}
+</style>
+<script>
+const convPopover1 = document.querySelector('#convoluted_p1');
+const convPopover2 = document.querySelector('#convoluted_p2');
+const convPopover3 = document.querySelector('#convoluted_p3');
+const convPopover4 = document.querySelector('#convoluted_p4');
+promise_test(async () => {
+ convPopover1.showPopover(); // Programmatically open p1
+ assert_true(convPopover1.matches(':popover-open'));
+ convPopover1.querySelector('button').click(); // Click to invoke p2
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ convPopover2.querySelector('button').click(); // Click to invoke p3
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ convPopover3.querySelector('button').click(); // Click to invoke p4
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover4.firstElementChild.click(); // Click within p4
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_true(convPopover3.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover1.hidePopover();
+ assert_false(convPopover1.matches(':popover-open'));
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+},'Ensure circular/convoluted ancestral relationships are functional');
+
+promise_test(async () => {
+ convPopover1.showPopover(); // Programmatically open p1
+ convPopover1.querySelector('button').click(); // Click to invoke p2
+ assert_true(convPopover1.matches(':popover-open'));
+ assert_true(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+ convPopover4.showPopover(); // Programmatically open p4
+ assert_true(convPopover1.matches(':popover-open'),'popover1 stays open because it is a DOM ancestor of popover4');
+ assert_false(convPopover2.matches(':popover-open'),'popover2 closes because it isn\'t connected to popover4 via active invokers');
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover4.firstElementChild.click(); // Click within p4
+ assert_true(convPopover1.matches(':popover-open'),'nothing changes');
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_true(convPopover4.matches(':popover-open'));
+ convPopover1.hidePopover();
+ assert_false(convPopover1.matches(':popover-open'));
+ assert_false(convPopover2.matches(':popover-open'));
+ assert_false(convPopover3.matches(':popover-open'));
+ assert_false(convPopover4.matches(':popover-open'));
+},'Ensure circular/convoluted ancestral relationships are functional, with a direct showPopover()');
+</script>
+
+<div popover id=p13>Popover 1
+ <div popover id=p14>Popover 2
+ <div popover id=p15>Popover 3</div>
+ </div>
+</div>
+<style>
+ #p13 {top: 100px;}
+ #p14 {top: 200px;}
+ #p15 {top: 300px;}
+</style>
+<script>
+promise_test(async () => {
+ const p13 = document.querySelector('#p13');
+ const p14 = document.querySelector('#p14');
+ const p15 = document.querySelector('#p15');
+ p13.showPopover();
+ p14.showPopover();
+ p15.showPopover();
+ p15.addEventListener('beforetoggle', (e) => {
+ if (e.newState !== "closed")
+ return;
+ p14.hidePopover();
+ },{once:true});
+ assert_true(p13.matches(':popover-open') && p14.matches(':popover-open') && p15.matches(':popover-open'),'all three should be open');
+ p14.hidePopover();
+ assert_true(p13.matches(':popover-open'),'p13 should still be open');
+ assert_false(p14.matches(':popover-open'));
+ assert_false(p15.matches(':popover-open'));
+ p13.hidePopover(); // Cleanup
+},'Hide the target popover during "hide all popovers until"');
+</script>
+
+<div id=p16 popover>Popover 16
+ <div id=p17 popover>Popover 17</div>
+ <div id=p18 popover>Popover 18</div>
+</div>
+
+<script>
+promise_test(async () => {
+ p16.showPopover();
+ p18.showPopover();
+ let events = [];
+ const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
+ p16.addEventListener('beforetoggle', logEvents);
+ p17.addEventListener('beforetoggle', logEvents);
+ p18.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p17.showPopover();
+ });
+ p16.hidePopover();
+ assert_array_equals(events,['hide p18','show p17','hide p16'],'There should not be a hide event for p17');
+ assert_false(p16.matches(':popover-open'));
+ assert_false(p17.matches(':popover-open'));
+ assert_false(p18.matches(':popover-open'));
+},'Show a sibling popover during "hide all popovers until"');
+</script>
+
+<div id=p19 popover>Popover 19</div>
+<div id=p20 popover>Popover 20</div>
+<button id=example2 tabindex="0">Example 2</button>
+
+<script>
+promise_test(async () => {
+ p19.showPopover();
+ let events = [];
+ const logEvents = (e) => {events.push(`${e.newState==='open' ? 'show' : 'hide'} ${e.target.id}`)};
+ p19.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p20.showPopover();
+ });
+ p20.addEventListener('beforetoggle', logEvents);
+ p19.hidePopover();
+ assert_array_equals(events,['hide p19','show p20'],'There should not be a second hide event for 19');
+ assert_false(p19.matches(':popover-open'));
+ assert_true(p20.matches(':popover-open'));
+ p20.hidePopover(); // Cleanup
+},'Show an unrelated popover during "hide popover"');
+</script>
+
+<div id=p21 popover>21
+ <div id=p22 popover>22</div>
+ <div id=p23 popover>23</div>
+ <div id=p24 popover>24</div>
+</div>
+
+<script>
+promise_test(async () => {
+ p21.showPopover();
+ p22.showPopover();
+ let events = [];
+ const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
+ p22.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p24.showPopover()
+ });
+ p23.addEventListener('beforetoggle', logEvents);
+ p24.addEventListener('beforetoggle', logEvents);
+ p23.showPopover();
+ assert_array_equals(events, ['show p23', 'hide p22', 'show p24'], 'hiding p24 does not fire event');
+ assert_false(p22.matches(':popover-open'));
+ assert_true(p23.matches(':popover-open'));
+ assert_false(p24.matches(':popover-open'));
+ p21.hidePopover(); // Cleanup
+},'Show other auto popover during "hide all popover until"');
+</script>
+
+<div id=p25 popover>
+ <div id=p26 popover>26</div>
+ <div id=p27 popover>27</div>
+ <div id=p28 popover>28</div>
+</div>
+<script>
+promise_test(async () => {
+ p25.showPopover();
+ p26.showPopover();
+ let events = [];
+ const logEvents = (e) => { events.push(`${e.newState === 'open' ? 'show' : 'hide'} ${e.target.id}`) };
+ p26.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p28.showPopover();
+ });
+ p27.addEventListener('beforetoggle', logEvents);
+ p28.addEventListener('beforetoggle', (e) => {
+ logEvents(e);
+ p27.showPopover();
+ });
+ p27.showPopover();
+ assert_array_equals(events, ['show p27', 'hide p26', 'show p28', 'show p27'], 'Nested showPopover should not fire event for its HideAllPopoversUntil');
+ assert_false(p26.matches(':popover-open'));
+ assert_true(p27.matches(':popover-open'));
+ assert_false(p28.matches(':popover-open'));
+ p25.hidePopover(); // Cleanup
+}, 'Nested showPopover');
+</script>
+
+<div id=p29 popover>Popover 29</div>
+<button id=b29 popovertarget=p29>Open popover 29</button>
+<iframe id=iframe29 width=100 height=100></iframe>
+<script>
+promise_test(async () => {
+ let iframe_url = (new URL("/common/blank.html", location.href)).href;
+ iframe29.src = iframe_url;
+ iframe29.contentDocument.body.style.height = '100%';
+ assert_false(p29.matches(':popover-open'));
+ p29.showPopover();
+ assert_true(p29.matches(':popover-open'));
+ let actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: b29})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .send();
+
+ actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: iframe29.contentDocument.body})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ assert_true(p29.matches(':popover-open'));
+},`Pointer down in one document and pointer up in another document shouldn't dismiss popover`);
+</script>