// Helper routine to find a client that matches a particular URL. Note, we // require that Client to be controlled to avoid false matches with other // about:blank windows the browser might have. The initial about:blank should // inherit the controller from its parent. async function getClientByURL(url) { let list = await clients.matchAll(); return list.find(client => client.url === url); } // Helper routine to perform a ping-pong with the given target client. We // expect the Client to respond with its location URL. async function pingPong(target) { function waitForPong() { return new Promise(resolve => { self.addEventListener('message', function onMessage(evt) { if (evt.data.type === 'PONG') { resolve(evt.data.location); } }); }); } target.postMessage({ type: 'PING' }) return await waitForPong(target); } addEventListener('fetch', async evt => { let url = new URL(evt.request.url); if (!url.searchParams.get('nested')) { return; } evt.respondWith(async function() { // Find the initial about:blank document. const client = await getClientByURL('about:blank'); if (!client) { return new Response('failure: could not find about:blank client'); } // If the nested frame is configured to support a ping-pong, then // ping it now to verify its message listener exists. We also // verify the Client's idea of its own location URL while we are doing // this. if (url.searchParams.get('ping')) { const loc = await pingPong(client); if (loc !== 'about:blank') { return new Response(`failure: got location {$loc}, expected about:blank`); } } // Finally, allow the nested frame to complete loading. We place the // Client ID we found for the initial about:blank in the body. return new Response(client.id); }()); }); addEventListener('message', evt => { if (evt.data.type !== 'GET_CLIENT_ID') { return; } evt.waitUntil(async function() { let url = new URL(evt.data.url); // Find the given Client by its URL. let client = await getClientByURL(evt.data.url); if (!client) { evt.source.postMessage({ type: 'GET_CLIENT_ID', result: `failure: could not find ${evt.data.url} client` }); return; } // If the Client supports a ping-pong, then do it now to verify // the message listener exists and its location matches the // Client object. if (url.searchParams.get('ping')) { let loc = await pingPong(client); if (loc !== evt.data.url) { evt.source.postMessage({ type: 'GET_CLIENT_ID', result: `failure: got location ${loc}, expected ${evt.data.url}` }); return; } } // Finally, send the client ID back. evt.source.postMessage({ type: 'GET_CLIENT_ID', result: client.id }); }()); });