From 747de9127d3003263d04885d399629217406c76a Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 29 Dec 2016 14:07:51 -0500 Subject: [PATCH 1/8] Meta: add a service worker to the spec This allows the spec to be viewed offline. --- deploy.sh | 2 ++ index.bs | 7 +++++++ service-worker.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 service-worker.js diff --git a/deploy.sh b/deploy.sh index ec7eebfdf..d1643691b 100644 --- a/deploy.sh +++ b/deploy.sh @@ -75,6 +75,8 @@ else > $WEB_ROOT/index.intermediate.html node_modules/.bin/emu-algify --throwing-indicators < $WEB_ROOT/index.intermediate.html > $WEB_ROOT/index.html rm $WEB_ROOT/index.intermediate.html + + cp service-worker.js $WEB_ROOT/service-worker.js echo "Living standard output to $WEB_ROOT" fi diff --git a/index.bs b/index.bs index 56a2629c0..facb32f25 100644 --- a/index.bs +++ b/index.bs @@ -4308,3 +4308,10 @@ This standard is written by Domenic Denicola tyoshino@chromium.org). Per CC0, to the extent possible under law, the editor has waived all copyright and related or neighboring rights to this work. + + diff --git a/service-worker.js b/service-worker.js new file mode 100644 index 000000000..bf35493e4 --- /dev/null +++ b/service-worker.js @@ -0,0 +1,47 @@ +"use strict"; +// Largely based on https://css-tricks.com/serviceworker-for-offline/ + +const cacheKey = "v1"; +const toCache = [ + "/", + "https://resources.whatwg.org/standard.css", + "https://resources.whatwg.org/bikeshed.css", + "https://resources.whatwg.org/file-issue.js", + "https://resources.whatwg.org/commit-snapshot-shortcut-key.js", + "https://resources.whatwg.org/logo-streams.svg" +]; + +self.oninstall = e => { + e.waitUntil(caches.open(cacheKey).then(cache => cache.addAll(toCache))); +}; + +self.onfetch = e => { + if (e.request.method !== "GET") { + return; + } + + e.respondWith( + caches.match(e.request).then(cachedResponse => { + // Respond with the cached response if it exists, but still do the network fetch in order to refresh the cache. + // Ignore network fetch errors; they just mean we won't be able to cache. + const networkFetchPromise = fetch(e.request).then(refreshCacheFromNetworkResponse).catch(() => {}); + + return cachedResponse || networkFetchPromise; + }) + ); + + function refreshCacheFromNetworkResponse(response) { + const responseForCache = response.clone(); + + // Ignore any errors while caching. + caches.open(cacheKey).then(cache => cache.put(e.request, responseForCache)); + + return response; + } +}; + +self.onactivate = e => { + e.waitUntil(caches.keys().then(keys => { + return Promise.all(keys.filter(key => key !== cacheKey).map(key => caches.delete(key))); + })); +}; From bd4c97698fb84b166ceac97687fbcc8e14231ff4 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 29 Dec 2016 14:21:41 -0500 Subject: [PATCH 2/8] Switch to a network-then-cache strategy --- service-worker.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/service-worker.js b/service-worker.js index bf35493e4..c8cc63f83 100644 --- a/service-worker.js +++ b/service-worker.js @@ -21,23 +21,23 @@ self.onfetch = e => { } e.respondWith( - caches.match(e.request).then(cachedResponse => { - // Respond with the cached response if it exists, but still do the network fetch in order to refresh the cache. - // Ignore network fetch errors; they just mean we won't be able to cache. - const networkFetchPromise = fetch(e.request).then(refreshCacheFromNetworkResponse).catch(() => {}); - - return cachedResponse || networkFetchPromise; + // Since this is a Living Standard, it is imperative that you see the freshest content, so we use a + // network-then-cache strategy. + fetch(e.request).then(res => { + if (!res.ok) { + throw new Error(`${res.url} is responding with ${res.status}; falling back to cache if possible`); + } + + const responseForCache = res.clone(); + // Do not return this promise; it's OK if caching fails, and we don't want to block on it. + caches.open(cacheKey).then(cache => cache.put(e.request, responseForCache)); + + return res; + }) + .catch(() => { + return caches.match(e.request); }) ); - - function refreshCacheFromNetworkResponse(response) { - const responseForCache = response.clone(); - - // Ignore any errors while caching. - caches.open(cacheKey).then(cache => cache.put(e.request, responseForCache)); - - return response; - } }; self.onactivate = e => { From 8864ed64c125196b0d14a665f3d57d7937a2246a Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 29 Dec 2016 14:38:57 -0500 Subject: [PATCH 3/8] Add web app manifest --- deploy.sh | 2 ++ index.bs | 1 + manifest.json | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 manifest.json diff --git a/deploy.sh b/deploy.sh index d1643691b..3c724b962 100644 --- a/deploy.sh +++ b/deploy.sh @@ -77,6 +77,8 @@ else rm $WEB_ROOT/index.intermediate.html cp service-worker.js $WEB_ROOT/service-worker.js + cp manifest.json $WEB_ROOT/manifest.json + echo "Living standard output to $WEB_ROOT" fi diff --git a/index.bs b/index.bs index facb32f25..bae3f7029 100644 --- a/index.bs +++ b/index.bs @@ -60,6 +60,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT +

Introduction

diff --git a/manifest.json b/manifest.json new file mode 100644 index 000000000..e6f4aa7a7 --- /dev/null +++ b/manifest.json @@ -0,0 +1,20 @@ +{ + "short_name": "Streams", + "name": "The WHATWG Streams Standard", + "icons": [ + { + "src": "https://resources.whatwg.org/logo-streams.svg", + "sizes": "any", + "type": "image/svg+xml" + }, + { + "src": "https://resources.whatwg.org/logo-streams.png", + "sizes": "500x500", + "type": "image/png" + } + ], + "start_url": "/", + "display": "browser", + "background_color": "#3c790a", + "theme_color": "#3c790a" +} From aef12ab1fe4b81b4ef4ed498f8bf52f1cb3d0b98 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 29 Dec 2016 14:39:37 -0500 Subject: [PATCH 4/8] Temporary commit for better testing --- deploy.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index 3c724b962..2fb7dc648 100644 --- a/deploy.sh +++ b/deploy.sh @@ -68,7 +68,8 @@ if [ $BRANCH != "master" ] ; then node_modules/.bin/emu-algify --throwing-indicators < $BRANCH_DIR/index.intermediate.html > $BRANCH_DIR/index.html rm $BRANCH_DIR/index.intermediate.html echo "Branch snapshot output to $WEB_ROOT/$BRANCHES_DIR/$BRANCH" -else +fi +#else # Living standard, if master curl https://api.csswg.org/bikeshed/ -f -F file=@index.bs \ -F md-Text-Macro="SNAPSHOT-LINK $SNAPSHOT_LINK" \ @@ -80,7 +81,7 @@ else cp manifest.json $WEB_ROOT/manifest.json echo "Living standard output to $WEB_ROOT" -fi +#fi echo "" find $WEB_ROOT -print From b26845f4abd80e56fde6276fc64423309e53f155 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Fri, 30 Dec 2016 12:58:24 -0500 Subject: [PATCH 5/8] Revert "Add web app manifest" This reverts commit c39097880d7c1cf11651bb1fbefa32715aaf11fe. --- deploy.sh | 2 -- index.bs | 1 - manifest.json | 20 -------------------- 3 files changed, 23 deletions(-) delete mode 100644 manifest.json diff --git a/deploy.sh b/deploy.sh index 2fb7dc648..891b2f213 100644 --- a/deploy.sh +++ b/deploy.sh @@ -78,8 +78,6 @@ fi rm $WEB_ROOT/index.intermediate.html cp service-worker.js $WEB_ROOT/service-worker.js - cp manifest.json $WEB_ROOT/manifest.json - echo "Living standard output to $WEB_ROOT" #fi diff --git a/index.bs b/index.bs index bae3f7029..facb32f25 100644 --- a/index.bs +++ b/index.bs @@ -60,7 +60,6 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT -

Introduction

diff --git a/manifest.json b/manifest.json deleted file mode 100644 index e6f4aa7a7..000000000 --- a/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "short_name": "Streams", - "name": "The WHATWG Streams Standard", - "icons": [ - { - "src": "https://resources.whatwg.org/logo-streams.svg", - "sizes": "any", - "type": "image/svg+xml" - }, - { - "src": "https://resources.whatwg.org/logo-streams.png", - "sizes": "500x500", - "type": "image/png" - } - ], - "start_url": "/", - "display": "browser", - "background_color": "#3c790a", - "theme_color": "#3c790a" -} From 2ad94882f75005110591268ac4dad34925bdeafb Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Fri, 30 Dec 2016 13:19:24 -0500 Subject: [PATCH 6/8] Do cache-then-network for everything but the main resource This is much snappier!! --- service-worker.js | 62 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/service-worker.js b/service-worker.js index c8cc63f83..4b32f1c3b 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,7 +1,6 @@ "use strict"; -// Largely based on https://css-tricks.com/serviceworker-for-offline/ -const cacheKey = "v1"; +const cacheKey = "v2"; const toCache = [ "/", "https://resources.whatwg.org/standard.css", @@ -20,24 +19,33 @@ self.onfetch = e => { return; } - e.respondWith( + if (needsToBeFresh(e.request)) { // Since this is a Living Standard, it is imperative that you see the freshest content, so we use a - // network-then-cache strategy. - fetch(e.request).then(res => { - if (!res.ok) { - throw new Error(`${res.url} is responding with ${res.status}; falling back to cache if possible`); - } - - const responseForCache = res.clone(); - // Do not return this promise; it's OK if caching fails, and we don't want to block on it. - caches.open(cacheKey).then(cache => cache.put(e.request, responseForCache)); - - return res; - }) - .catch(() => { - return caches.match(e.request); - }) - ); + // network-then-cache strategy for the main content. + e.respondWith( + fetch(e.request).then(res => { + refreshCacheFromNetworkResponse(e.request, res); + return res; + }) + .catch(() => { + return caches.match(e.request); + }) + ); + } else { + // For auxiliary resources, we can use a cache-then-network strategy; it is OK to not get the freshest. + e.respondWith( + caches.match(e.request).then(cachedResponse => { + const networkFetchPromise = fetch(e.request); + + // Ignore network fetch or caching errors; they just mean we won't be able to refresh the cache. + networkFetchPromise + .then(res => refreshCacheFromNetworkResponse(e.request, res)) + .catch(() => {}); + + return cachedResponse || networkFetchPromise; + }) + ); + } }; self.onactivate = e => { @@ -45,3 +53,19 @@ self.onactivate = e => { return Promise.all(keys.filter(key => key !== cacheKey).map(key => caches.delete(key))); })); }; + +function refreshCacheFromNetworkResponse(req, res) { + if (!res.ok) { + throw new Error(`${res.url} is responding with ${res.status}`); + } + + const resForCache = res.clone(); + + // Do not return this promise; it's OK if caching fails, and we don't want to block on it. + caches.open(cacheKey).then(cache => cache.put(req, resForCache)); +} + +function needsToBeFresh(req) { + const requestURL = new URL(req.url); + return requestURL.origin === location.origin && requestURL.pathname === "/"; +} From f2919bdacabbc81efdf67495530ece14940da444 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 12 Jan 2017 17:09:55 -0500 Subject: [PATCH 7/8] Add appropriate waitUntils + Jake's polyfill --- service-worker.js | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/service-worker.js b/service-worker.js index 4b32f1c3b..6196fa2fb 100644 --- a/service-worker.js +++ b/service-worker.js @@ -24,7 +24,7 @@ self.onfetch = e => { // network-then-cache strategy for the main content. e.respondWith( fetch(e.request).then(res => { - refreshCacheFromNetworkResponse(e.request, res); + e.waitUntil(refreshCacheFromNetworkResponse(e.request, res)); return res; }) .catch(() => { @@ -38,9 +38,11 @@ self.onfetch = e => { const networkFetchPromise = fetch(e.request); // Ignore network fetch or caching errors; they just mean we won't be able to refresh the cache. - networkFetchPromise - .then(res => refreshCacheFromNetworkResponse(e.request, res)) - .catch(() => {}); + e.waitUntil( + networkFetchPromise + .then(res => refreshCacheFromNetworkResponse(e.request, res)) + .catch(() => {}) + ); return cachedResponse || networkFetchPromise; }) @@ -61,11 +63,51 @@ function refreshCacheFromNetworkResponse(req, res) { const resForCache = res.clone(); - // Do not return this promise; it's OK if caching fails, and we don't want to block on it. - caches.open(cacheKey).then(cache => cache.put(req, resForCache)); + return caches.open(cacheKey).then(cache => cache.put(req, resForCache)); } function needsToBeFresh(req) { const requestURL = new URL(req.url); return requestURL.origin === location.origin && requestURL.pathname === "/"; } + +// From https://github.com/jakearchibald/async-waituntil-polyfill +// Apache 2 License: https://github.com/jakearchibald/async-waituntil-polyfill/blob/master/LICENSE +{ + const waitUntil = ExtendableEvent.prototype.waitUntil; + const respondWith = FetchEvent.prototype.respondWith; + const promisesMap = new WeakMap(); + + ExtendableEvent.prototype.waitUntil = function(promise) { + const extendableEvent = this; + let promises = promisesMap.get(extendableEvent); + + if (promises) { + promises.push(Promise.resolve(promise)); + return; + } + + promises = [Promise.resolve(promise)]; + promisesMap.set(extendableEvent, promises); + + // call original method + return waitUntil.call(extendableEvent, Promise.resolve().then(function processPromises() { + const len = promises.length; + + // wait for all to settle + return Promise.all(promises.map(p => p.catch(()=>{}))).then(() => { + // have new items been added? If so, wait again + if (promises.length != len) return processPromises(); + // we're done! + promisesMap.delete(extendableEvent); + // reject if one of the promises rejected + return Promise.all(promises); + }); + })); + }; + + FetchEvent.prototype.respondWith = function(promise) { + this.waitUntil(promise); + return respondWith.call(this, promise); + }; +} From 82a111ca0f0b6712310dbf96134b06f253e9f34a Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 12 Jan 2017 17:10:30 -0500 Subject: [PATCH 8/8] Revert "Temporary commit for better testing" This reverts commit aef12ab1fe4b81b4ef4ed498f8bf52f1cb3d0b98. --- deploy.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deploy.sh b/deploy.sh index 891b2f213..d1643691b 100644 --- a/deploy.sh +++ b/deploy.sh @@ -68,8 +68,7 @@ if [ $BRANCH != "master" ] ; then node_modules/.bin/emu-algify --throwing-indicators < $BRANCH_DIR/index.intermediate.html > $BRANCH_DIR/index.html rm $BRANCH_DIR/index.intermediate.html echo "Branch snapshot output to $WEB_ROOT/$BRANCHES_DIR/$BRANCH" -fi -#else +else # Living standard, if master curl https://api.csswg.org/bikeshed/ -f -F file=@index.bs \ -F md-Text-Macro="SNAPSHOT-LINK $SNAPSHOT_LINK" \ @@ -79,7 +78,7 @@ fi cp service-worker.js $WEB_ROOT/service-worker.js echo "Living standard output to $WEB_ROOT" -#fi +fi echo "" find $WEB_ROOT -print