popover-top-layer-nesting.js (5243B)
1 function createTopLayerElement(t,topLayerType) { 2 let element, show, showing; 3 switch (topLayerType) { 4 case 'dialog': 5 element = document.createElement('dialog'); 6 show = () => element.showModal(); 7 showing = () => element.matches(':modal'); 8 break; 9 case 'fullscreen': 10 element = document.createElement('div'); 11 show = async (topmostElement) => { 12 // Be sure to add user activation to the topmost visible target: 13 await blessTopLayer(topmostElement); 14 await element.requestFullscreen(); 15 }; 16 showing = () => document.fullscreenElement === element; 17 break; 18 default: 19 assert_unreached('Invalid top layer type'); 20 } 21 t.add_cleanup(() => element.remove()); 22 return {element,show,showing}; 23 } 24 function runTopLayerTests(testCases, testAnchorAttribute) { 25 testAnchorAttribute = testAnchorAttribute || false; 26 testCases.forEach(test => { 27 const description = test.firstChild.data.trim(); 28 assert_equals(test.querySelectorAll('.target').length,1,'There should be exactly one target'); 29 const target = test.querySelector('.target'); 30 assert_true(!!target,'Invalid test case'); 31 const popovers = Array.from(test.querySelectorAll('[popover]')); 32 assert_true(popovers.length > 0,'No popovers found'); 33 ['dialog','fullscreen'].forEach(topLayerType => { 34 promise_test(async t => { 35 const {element,show,showing} = createTopLayerElement(t,topLayerType); 36 target.appendChild(element); 37 38 // Show the popovers. 39 t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); 40 popovers.forEach(popover => popover.showPopover()); 41 popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); 42 43 // Activate the top layer element. 44 await show(popovers[popovers.length-1]); 45 assert_true(showing()); 46 popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior')); 47 48 // Add another popover within the top layer element and make sure entire stack stays open. 49 const newPopover = document.createElement('div'); 50 t.add_cleanup(() => newPopover.remove()); 51 newPopover.popover = 'hint'; 52 element.appendChild(newPopover); 53 popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Adding another popover shouldn\'t change anything')); 54 assert_true(showing(),'top layer element should still be top layer'); 55 newPopover.showPopover(); 56 assert_true(newPopover.matches(':popover-open')); 57 popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Showing the popover shouldn\'t change anything')); 58 assert_true(showing(),'top layer element should still be top layer'); 59 },`${description} with ${topLayerType}`); 60 61 promise_test(async t => { 62 const {element,show,showing} = createTopLayerElement(t,topLayerType); 63 element.popover = 'hint'; 64 target.appendChild(element); 65 66 // Show the popovers. 67 t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); 68 popovers.forEach(popover => popover.showPopover()); 69 popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); 70 const targetWasOpenPopover = target.matches(':popover-open'); 71 72 // Show the top layer element as a popover first. 73 element.showPopover(); 74 assert_true(element.matches(':popover-open'),'element should be open as a popover'); 75 assert_equals(target.matches(':popover-open'),targetWasOpenPopover,'target shouldn\'t change popover state'); 76 77 try { 78 await show(element); 79 assert_unreached('It is an error to activate a top layer element that is already a showing popover'); 80 } catch (e) { 81 // We expect an InvalidStateError for dialogs, and a TypeError for fullscreens. 82 // Anything else should fall through to the test harness. 83 if (e.name !== 'InvalidStateError' && e.name !== 'TypeError') { 84 throw e; 85 } 86 } 87 },`${description} with ${topLayerType}, top layer element *is* a popover`); 88 89 if (testAnchorAttribute) { 90 promise_test(async t => { 91 const {element,show,showing} = createTopLayerElement(t,topLayerType); 92 element.anchorElement = target; 93 document.body.appendChild(element); 94 95 // Show the popovers. 96 t.add_cleanup(() => popovers.forEach(popover => popover.hidePopover())); 97 popovers.forEach(popover => popover.showPopover()); 98 popovers.forEach(popover => assert_true(popover.matches(':popover-open'),'All popovers should be open')); 99 100 // Activate the top layer element. 101 await show(popovers[popovers.length-1]); 102 assert_true(showing()); 103 popovers.forEach(popover => assert_equals(popover.matches(':popover-open'),popover.dataset.stayOpen==='true','Incorrect behavior')); 104 },`${description} with ${topLayerType}, anchor attribute`); 105 } 106 }); 107 }); 108 }