Javier López-Contreras | 6d1d72d | 2018-12-27 23:17:18 +0100 | [diff] [blame] | 1 | /*
|
| 2 | Copyright 2015 Google Inc. All Rights Reserved.
|
| 3 | Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 | you may not use this file except in compliance with the License.
|
| 5 | You may obtain a copy of the License at
|
| 6 | http://www.apache.org/licenses/LICENSE-2.0
|
| 7 | Unless required by applicable law or agreed to in writing, software
|
| 8 | distributed under the License is distributed on an "AS IS" BASIS,
|
| 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 10 | See the License for the specific language governing permissions and
|
| 11 | limitations under the License.
|
| 12 | */
|
| 13 |
|
| 14 | 'use strict';
|
| 15 |
|
| 16 | // Incrementing CACHE_VERSION will kick off the install event and force previously cached
|
| 17 | // resources to be cached again.
|
| 18 | const CACHE_VERSION = 1;
|
| 19 | let CURRENT_CACHES = {
|
| 20 | offline: 'offline-v' + CACHE_VERSION
|
| 21 | };
|
| 22 | const OFFLINE_URL = 'offline.html';
|
| 23 |
|
| 24 | function createCacheBustedRequest(url) {
|
| 25 | let request = new Request(url, {cache: 'reload'});
|
| 26 | // See https://fetch.spec.whatwg.org/#concept-request-mode
|
| 27 | // This is not yet supported in Chrome as of M48, so we need to explicitly check to see
|
| 28 | // if the cache: 'reload' option had any effect.
|
| 29 | if ('cache' in request) {
|
| 30 | return request;
|
| 31 | }
|
| 32 |
|
| 33 | // If {cache: 'reload'} didn't have any effect, append a cache-busting URL parameter instead.
|
| 34 | let bustedUrl = new URL(url, self.location.href);
|
| 35 | bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();
|
| 36 | return new Request(bustedUrl);
|
| 37 | }
|
| 38 |
|
| 39 | self.addEventListener('install', event => {
|
| 40 | event.waitUntil(
|
| 41 | // We can't use cache.add() here, since we want OFFLINE_URL to be the cache key, but
|
| 42 | // the actual URL we end up requesting might include a cache-busting parameter.
|
| 43 | fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {
|
| 44 | return caches.open(CURRENT_CACHES.offline).then(function(cache) {
|
| 45 | return cache.put(OFFLINE_URL, response);
|
| 46 | });
|
| 47 | })
|
| 48 | );
|
| 49 | });
|
| 50 |
|
| 51 | self.addEventListener('activate', event => {
|
| 52 | // Delete all caches that aren't named in CURRENT_CACHES.
|
| 53 | // While there is only one cache in this example, the same logic will handle the case where
|
| 54 | // there are multiple versioned caches.
|
| 55 | let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
|
| 56 | return CURRENT_CACHES[key];
|
| 57 | });
|
| 58 |
|
| 59 | event.waitUntil(
|
| 60 | caches.keys().then(cacheNames => {
|
| 61 | return Promise.all(
|
| 62 | cacheNames.map(cacheName => {
|
| 63 | if (expectedCacheNames.indexOf(cacheName) === -1) {
|
| 64 | // If this cache name isn't present in the array of "expected" cache names,
|
| 65 | // then delete it.
|
| 66 | console.log('Deleting out of date cache:', cacheName);
|
| 67 | return caches.delete(cacheName);
|
| 68 | }
|
| 69 | })
|
| 70 | );
|
| 71 | })
|
| 72 | );
|
| 73 | });
|
| 74 |
|
| 75 | self.addEventListener('fetch', event => {
|
| 76 | // We only want to call event.respondWith() if this is a navigation request
|
| 77 | // for an HTML page.
|
| 78 | // request.mode of 'navigate' is unfortunately not supported in Chrome
|
| 79 | // versions older than 49, so we need to include a less precise fallback,
|
| 80 | // which checks for a GET request with an Accept: text/html header.
|
| 81 | if (event.request.mode === 'navigate' ||
|
| 82 | (event.request.method === 'GET' &&
|
| 83 | event.request.headers.get('accept').includes('text/html'))) {
|
| 84 | console.log('Handling fetch event for', event.request.url);
|
| 85 | event.respondWith(
|
| 86 | fetch(event.request).catch(error => {
|
| 87 | // The catch is only triggered if fetch() throws an exception, which will most likely
|
| 88 | // happen due to the server being unreachable.
|
| 89 | // If fetch() returns a valid HTTP response with an response code in the 4xx or 5xx
|
| 90 | // range, the catch() will NOT be called. If you need custom handling for 4xx or 5xx
|
| 91 | // errors, see https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker/fallback-response
|
| 92 | console.log('Fetch failed; returning offline page instead.', error);
|
| 93 | return caches.match(OFFLINE_URL);
|
| 94 | })
|
| 95 | );
|
| 96 | }
|
| 97 |
|
| 98 | // If our if() condition is false, then this fetch handler won't intercept the request.
|
| 99 | // If there are any other fetch handlers registered, they will get a chance to call
|
| 100 | // event.respondWith(). If no fetch handlers call event.respondWith(), the request will be
|
| 101 | // handled by the browser as if there were no service worker involvement.
|
| 102 | });
|