about-blank-replacement-worker.js (2923B)
1 // Helper routine to find a client that matches a particular URL. Note, we 2 // require that Client to be controlled to avoid false matches with other 3 // about:blank windows the browser might have. The initial about:blank should 4 // inherit the controller from its parent. 5 async function getClientByURL(url) { 6 let list = await clients.matchAll(); 7 return list.find(client => client.url === url); 8 } 9 10 // Helper routine to perform a ping-pong with the given target client. We 11 // expect the Client to respond with its location URL. 12 async function pingPong(target) { 13 function waitForPong() { 14 return new Promise(resolve => { 15 self.addEventListener('message', function onMessage(evt) { 16 if (evt.data.type === 'PONG') { 17 resolve(evt.data.location); 18 } 19 }); 20 }); 21 } 22 23 target.postMessage({ type: 'PING' }) 24 return await waitForPong(target); 25 } 26 27 addEventListener('fetch', async evt => { 28 let url = new URL(evt.request.url); 29 if (!url.searchParams.get('nested')) { 30 return; 31 } 32 33 evt.respondWith(async function() { 34 // Find the initial about:blank document. 35 const client = await getClientByURL('about:blank'); 36 if (!client) { 37 return new Response('failure: could not find about:blank client'); 38 } 39 40 // If the nested frame is configured to support a ping-pong, then 41 // ping it now to verify its message listener exists. We also 42 // verify the Client's idea of its own location URL while we are doing 43 // this. 44 if (url.searchParams.get('ping')) { 45 const loc = await pingPong(client); 46 if (loc !== 'about:blank') { 47 return new Response(`failure: got location {$loc}, expected about:blank`); 48 } 49 } 50 51 // Finally, allow the nested frame to complete loading. We place the 52 // Client ID we found for the initial about:blank in the body. 53 return new Response(client.id); 54 }()); 55 }); 56 57 addEventListener('message', evt => { 58 if (evt.data.type !== 'GET_CLIENT_ID') { 59 return; 60 } 61 62 evt.waitUntil(async function() { 63 let url = new URL(evt.data.url); 64 65 // Find the given Client by its URL. 66 let client = await getClientByURL(evt.data.url); 67 if (!client) { 68 evt.source.postMessage({ 69 type: 'GET_CLIENT_ID', 70 result: `failure: could not find ${evt.data.url} client` 71 }); 72 return; 73 } 74 75 // If the Client supports a ping-pong, then do it now to verify 76 // the message listener exists and its location matches the 77 // Client object. 78 if (url.searchParams.get('ping')) { 79 let loc = await pingPong(client); 80 if (loc !== evt.data.url) { 81 evt.source.postMessage({ 82 type: 'GET_CLIENT_ID', 83 result: `failure: got location ${loc}, expected ${evt.data.url}` 84 }); 85 return; 86 } 87 } 88 89 // Finally, send the client ID back. 90 evt.source.postMessage({ 91 type: 'GET_CLIENT_ID', 92 result: client.id 93 }); 94 }()); 95 });