var pendingPorts = []; var portResolves = []; onmessage = function(e) { var message = e.data; if ('port' in message) { var resolve = self.portResolves.shift(); if (resolve) resolve(message.port); else self.pendingPorts.push(message.port); } }; function fulfillPromise() { return new Promise(function(resolve) { // Make sure the oninstall/onactivate callback returns first. Promise.resolve().then(function() { var port = self.pendingPorts.shift(); if (port) resolve(port); else self.portResolves.push(resolve); }); }).then(function(port) { port.postMessage('SYNC'); return new Promise(function(resolve) { port.onmessage = function(e) { if (e.data == 'ACK') resolve(); }; }); }); } function rejectPromise() { return new Promise(function(resolve, reject) { // Make sure the oninstall/onactivate callback returns first. Promise.resolve().then(reject); }); } function stripScopeName(url) { return url.split('/').slice(-1)[0]; } oninstall = function(e) { switch (stripScopeName(self.location.href)) { case 'install-fulfilled': e.waitUntil(fulfillPromise()); break; case 'install-rejected': e.waitUntil(rejectPromise()); break; case 'install-multiple-fulfilled': e.waitUntil(fulfillPromise()); e.waitUntil(fulfillPromise()); break; case 'install-reject-precedence': // Three "extend lifetime promises" are needed to verify that the user // agent waits for all promises to settle even in the event of rejection. // The first promise is fulfilled on demand by the client, the second is // immediately scheduled for rejection, and the third is fulfilled on // demand by the client (but only after the first promise has been // fulfilled). // // User agents which simply expose `Promise.all` semantics in this case // (by entering the "redundant state" following the rejection of the // second promise but prior to the fulfillment of the third) can be // identified from the client context. e.waitUntil(fulfillPromise()); e.waitUntil(rejectPromise()); e.waitUntil(fulfillPromise()); break; } }; onactivate = function(e) { switch (stripScopeName(self.location.href)) { case 'activate-fulfilled': e.waitUntil(fulfillPromise()); break; case 'activate-rejected': e.waitUntil(rejectPromise()); break; } };