closes-on-navigation-or-destroy.https.html (5571B)
1 <!DOCTYPE html> 2 <title>Test that a document picture-in-picture window closes when itself or the opener is navigated / destroyed</title> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <script src="/resources/testdriver.js"></script> 6 <script src="/resources/testdriver-vendor.js"></script> 7 <link rel="help" href="https://wicg.github.io/document-picture-in-picture/#close-on-destroy"/> 8 <link rel="help" href="https://wicg.github.io/document-picture-in-picture/#close-on-navigate"/> 9 <body> 10 <script> 11 function waitForEvent(target, event) { 12 return new Promise(resolve => target.addEventListener(event, resolve, {once: true})) 13 } 14 15 async function waitForMessage(target, type) { 16 const { data } = await waitForEvent(target, 'message'); 17 assert_equals(data.type, type, "Got the expected message"); 18 return data; 19 } 20 21 window.addEventListener('message', ({data}) => { 22 if (data.type == 'error') { 23 throw data; 24 } 25 }); 26 27 let last_id = 0; 28 async function new_opener_with_pip_window(t) { 29 // We cannot navigate this window, so do the tests in a popup. 30 const channelName = `pip channel ${++last_id}`; 31 32 const opener = window.open(`support/popup-opens-pip.html`); 33 await waitForEvent(opener, "load"); 34 assert_true(true, "Popup loaded"); 35 36 const pipChannel = new BroadcastChannel(channelName); 37 pipChannel.addEventListener('message', ({data}) => { 38 if (data.type == 'error') { 39 throw new Error(`${data.name}: ${data.message}`); 40 } 41 }); 42 43 await test_driver.bless('activate popup window', null, opener); 44 opener.postMessage({ type: 'request-pip', channelName }); 45 await waitForMessage(pipChannel, 'pip-ready'); 46 47 t.add_cleanup(function() { 48 pipChannel.close(); 49 opener.close(); 50 }); 51 52 return { opener, pipChannel }; 53 } 54 55 function evalInPIP(pipChannel, code, args = []) { 56 pipChannel.postMessage({ type: "exec", code: code.toString(), args }); 57 return waitForMessage(pipChannel, "exec-result"); 58 } 59 60 promise_test(async (t) => { 61 // Trivial test case, mostly a sanity-check for this test infrastructure 62 const { opener, pipChannel } = await new_opener_with_pip_window(t); 63 assert_true(true, 'Succeded in getting an opener with pip window'); 64 65 opener.postMessage({ type: 'close-pip' }); 66 // This also tests that a PIP window gets a pagehide 67 await waitForMessage(pipChannel, 'pip-pagehide'); 68 69 opener.postMessage({ type: 'get-pip-status' }); 70 const statusMsg = await waitForMessage(window, 'pip-status'); 71 assert_true(statusMsg.closed, 'Succeeded in closing the pip window'); 72 }, 'PIP window can be closed'); 73 74 promise_test(async (t) => { 75 const { opener, pipChannel } = await new_opener_with_pip_window(t); 76 77 opener.close(); 78 await waitForMessage(pipChannel, 'pip-pagehide'); 79 }, 'PIP window closes when opener closes'); 80 81 function waitTillPiPClosed(t, opener) { 82 let closed = false; 83 let shouldPing = true; 84 85 const listener = ({ data }) => { 86 if (data.type == "pip-status") { 87 closed = data.closed; 88 shouldPing = true; 89 } 90 } 91 window.addEventListener("message", listener); 92 93 const condition = () => { 94 if (shouldPing && !closed) { 95 opener.postMessage({ type: 'get-pip-status' }); 96 shouldPing = false; 97 } 98 return closed; 99 } 100 return t.step_wait(condition, "PiP window .closed becomes true") 101 .finally(() => { 102 window.removeEventListener("message", listener) 103 }) 104 } 105 106 promise_test(async (t) => { 107 const { opener, pipChannel } = await new_opener_with_pip_window(t); 108 109 // Per spec, #close-a-top-level-traversable doesn't set #is-closing and thus 110 // window.closed only becomes true when the traversable is destroyed. This happens 111 // via `afterAllUnloads` after pagehide. This is different for window.close(). 112 // https://github.com/whatwg/html/issues/11853 113 opener.postMessage({ type: 'navigate-pip', href: "/common/blank.html" }); 114 const statusMsg = await waitForMessage(pipChannel, 'pip-pagehide'); 115 assert_false(statusMsg.closed, "window.closed is false during pagehide"); 116 await waitTillPiPClosed(t, opener); 117 }, "window.closed becomes true after pagehide if not window.close() initiated"); 118 119 promise_test(async (t) => { 120 const { opener, pipChannel } = await new_opener_with_pip_window(t); 121 122 opener.postMessage({ type: 'navigate-pip', href: "about:blank#0" }); 123 124 opener.postMessage({ type: 'get-pip-status' }); 125 const statusMsg = await waitForMessage(window, 'pip-status'); 126 assert_false(statusMsg.closed, 'Same-page navigation did not close PIP'); 127 128 opener.postMessage({ type: 'navigate-pip', href: "/common/blank.html" }); 129 await waitForMessage(pipChannel, 'pip-pagehide'); 130 await waitTillPiPClosed(t, opener); 131 }, 'PIP window closes when navigated'); 132 133 promise_test(async (t) => { 134 const { opener, pipChannel } = await new_opener_with_pip_window(t); 135 136 await evalInPIP(pipChannel, () => window.name = "pipwindow" ) 137 138 const w = window.open("/common/blank.html", "pipwindow"); 139 await waitForMessage(pipChannel, "pip-pagehide"); 140 await waitTillPiPClosed(t, opener); 141 }, 'PIP window closes when navigated by name'); 142 143 promise_test(async (t) => { 144 const { opener, pipChannel } = await new_opener_with_pip_window(t); 145 146 opener.location = opener.location.href + "#0"; 147 148 opener.postMessage({ type: 'get-pip-status' }); 149 const statusMsg = await waitForMessage(window, 'pip-status'); 150 assert_false(statusMsg.closed, 'Same-page navigation of opener did not close PIP'); 151 152 opener.location = "/common/blank.html"; 153 await waitForMessage(pipChannel, 'pip-pagehide'); 154 }, 'PIP window closes when opener navigates'); 155 </script> 156 </body>