popup-opens-pip.html (1803B)
1 <script> 2 function get_pip_code(channelName) { 3 return ` 4 const channel = new BroadcastChannel("${channelName}"); 5 6 window.addEventListener("pagehide", () => { 7 channel.postMessage({ type: "pip-pagehide", closed: window.closed }); 8 }); 9 10 channel.addEventListener("message", async ({data}) => { 11 if (data.type == 'exec') { 12 const { code, args = [] } = data; 13 try { 14 // indirect eval call to evaluate in global scope 15 const fn = (0, eval)('(' + code + ')'); 16 const value = await fn(...args); 17 channel.postMessage({ type: "exec-result", value }); 18 } catch (err) { 19 channel.postMessage({ type: "error", name: err?.name, message: err?.message, stack: err?.stack }); 20 } 21 } else { 22 channel.postMessage({ type: "error", message: "unknown message " + data.type }); 23 } 24 }); 25 26 channel.postMessage({ type: "pip-ready" }); 27 `; 28 } 29 30 let pipWindow = null; 31 32 async function action(data) { 33 if (data.type == 'request-pip') { 34 pipWindow = await documentPictureInPicture.requestWindow(); 35 36 const script = pipWindow.document.createElement('script'); 37 script.innerHTML = get_pip_code(data.channelName); 38 pipWindow.document.body.append(script); 39 } else if (data.type == 'close-pip') { 40 pipWindow.close(); 41 } else if (data.type == 'navigate-pip') { 42 pipWindow.location = data.href; 43 } else if (data.type == 'get-pip-status') { 44 window.opener.postMessage({ 45 type: 'pip-status', 46 closed: pipWindow.closed 47 }); 48 } else { 49 throw new Error("Unknown message"); 50 } 51 } 52 53 window.addEventListener("message", async ({data}) => { 54 try { 55 await action(data); 56 } catch (e) { 57 window.opener.postMessage({ 58 msg: `Unknown error ${e.name}: ${e.message} at ${e.stack}`, 59 type: 'error' 60 }) 61 } 62 }) 63 </script>