Source code

Revision control

Copy as Markdown

Other Tools

// 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
});
}());
});