|
4 | 4 | <meta charset="utf-8" /> |
5 | 5 | <meta name="viewport" content="width=device-width" /> |
6 | 6 | <script> |
7 | | - /* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */ |
8 | | - // From here: https://github.com/gzuidhof/coi-serviceworker |
| 7 | + /*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ |
| 8 | + let coepCredentialless = false; |
9 | 9 | if (typeof window === 'undefined') { |
10 | 10 | self.addEventListener('install', () => self.skipWaiting()); |
11 | | - self.addEventListener('activate', (e) => |
12 | | - e.waitUntil(self.clients.claim()) |
| 11 | + self.addEventListener('activate', (event) => |
| 12 | + event.waitUntil(self.clients.claim()) |
13 | 13 | ); |
14 | 14 |
|
15 | | - async function handleFetch(request) { |
16 | | - if ( |
17 | | - request.cache === 'only-if-cached' && |
18 | | - request.mode !== 'same-origin' |
19 | | - ) { |
| 15 | + self.addEventListener('message', (ev) => { |
| 16 | + if (!ev.data) { |
20 | 17 | return; |
| 18 | + } else if (ev.data.type === 'deregister') { |
| 19 | + self.registration |
| 20 | + .unregister() |
| 21 | + .then(() => { |
| 22 | + return self.clients.matchAll(); |
| 23 | + }) |
| 24 | + .then((clients) => { |
| 25 | + clients.forEach((client) => client.navigate(client.url)); |
| 26 | + }); |
| 27 | + } else if (ev.data.type === 'coepCredentialless') { |
| 28 | + coepCredentialless = ev.data.value; |
21 | 29 | } |
| 30 | + }); |
22 | 31 |
|
23 | | - if (request.mode === 'no-cors') { |
24 | | - // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7 |
25 | | - request = new Request(request.url, { |
26 | | - cache: request.cache, |
27 | | - credentials: 'omit', |
28 | | - headers: request.headers, |
29 | | - integrity: request.integrity, |
30 | | - destination: request.destination, |
31 | | - keepalive: request.keepalive, |
32 | | - method: request.method, |
33 | | - mode: request.mode, |
34 | | - redirect: request.redirect, |
35 | | - referrer: request.referrer, |
36 | | - referrerPolicy: request.referrerPolicy, |
37 | | - signal: request.signal |
38 | | - }); |
| 32 | + self.addEventListener('fetch', function (event) { |
| 33 | + const r = event.request; |
| 34 | + if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') { |
| 35 | + return; |
39 | 36 | } |
40 | 37 |
|
41 | | - let r = await fetch(request).catch((e) => console.error(e)); |
| 38 | + const request = |
| 39 | + coepCredentialless && r.mode === 'no-cors' |
| 40 | + ? new Request(r, { |
| 41 | + credentials: 'omit' |
| 42 | + }) |
| 43 | + : r; |
| 44 | + event.respondWith( |
| 45 | + fetch(request) |
| 46 | + .then((response) => { |
| 47 | + if (response.status === 0) { |
| 48 | + return response; |
| 49 | + } |
| 50 | + |
| 51 | + const newHeaders = new Headers(response.headers); |
| 52 | + newHeaders.set( |
| 53 | + 'Cross-Origin-Embedder-Policy', |
| 54 | + coepCredentialless ? 'credentialless' : 'require-corp' |
| 55 | + ); |
| 56 | + if (!coepCredentialless) { |
| 57 | + newHeaders.set( |
| 58 | + 'Cross-Origin-Resource-Policy', |
| 59 | + 'cross-origin' |
| 60 | + ); |
| 61 | + } |
| 62 | + newHeaders.set('Cross-Origin-Opener-Policy', 'same-origin'); |
| 63 | + |
| 64 | + return new Response(response.body, { |
| 65 | + status: response.status, |
| 66 | + statusText: response.statusText, |
| 67 | + headers: newHeaders |
| 68 | + }); |
| 69 | + }) |
| 70 | + .catch((e) => console.error(e)) |
| 71 | + ); |
| 72 | + }); |
| 73 | + } else { |
| 74 | + (() => { |
| 75 | + const reloadedBySelf = |
| 76 | + window.sessionStorage.getItem('coiReloadedBySelf'); |
| 77 | + window.sessionStorage.removeItem('coiReloadedBySelf'); |
| 78 | + const coepDegrading = reloadedBySelf == 'coepdegrade'; |
| 79 | + |
| 80 | + // You can customize the behavior of this script through a global `coi` variable. |
| 81 | + const coi = { |
| 82 | + shouldRegister: () => !reloadedBySelf, |
| 83 | + shouldDeregister: () => false, |
| 84 | + coepCredentialless: () => true, |
| 85 | + coepDegrade: () => true, |
| 86 | + doReload: () => window.location.reload(), |
| 87 | + quiet: false, |
| 88 | + ...window.coi |
| 89 | + }; |
| 90 | + |
| 91 | + const n = navigator; |
| 92 | + const controlling = n.serviceWorker && n.serviceWorker.controller; |
42 | 93 |
|
43 | | - if (r.status === 0) { |
44 | | - return r; |
| 94 | + // Record the failure if the page is served by serviceWorker. |
| 95 | + if (controlling && !window.crossOriginIsolated) { |
| 96 | + window.sessionStorage.setItem('coiCoepHasFailed', 'true'); |
45 | 97 | } |
| 98 | + const coepHasFailed = |
| 99 | + window.sessionStorage.getItem('coiCoepHasFailed'); |
46 | 100 |
|
47 | | - const headers = new Headers(r.headers); |
48 | | - headers.set('Cross-Origin-Embedder-Policy', 'credentialless'); // or: require-corp |
49 | | - headers.set('Cross-Origin-Opener-Policy', 'same-origin'); |
| 101 | + if (controlling) { |
| 102 | + // Reload only on the first failure. |
| 103 | + const reloadToDegrade = |
| 104 | + coi.coepDegrade() && |
| 105 | + !(coepDegrading || window.crossOriginIsolated); |
| 106 | + n.serviceWorker.controller.postMessage({ |
| 107 | + type: 'coepCredentialless', |
| 108 | + value: |
| 109 | + reloadToDegrade || (coepHasFailed && coi.coepDegrade()) |
| 110 | + ? false |
| 111 | + : coi.coepCredentialless() |
| 112 | + }); |
| 113 | + if (reloadToDegrade) { |
| 114 | + !coi.quiet && console.log('Reloading page to degrade COEP.'); |
| 115 | + window.sessionStorage.setItem('coiReloadedBySelf', 'coepdegrade'); |
| 116 | + coi.doReload('coepdegrade'); |
| 117 | + } |
50 | 118 |
|
51 | | - return new Response(r.body, { |
52 | | - status: r.status, |
53 | | - statusText: r.statusText, |
54 | | - headers |
55 | | - }); |
56 | | - } |
| 119 | + if (coi.shouldDeregister()) { |
| 120 | + n.serviceWorker.controller.postMessage({ type: 'deregister' }); |
| 121 | + } |
| 122 | + } |
57 | 123 |
|
58 | | - self.addEventListener('fetch', function (e) { |
59 | | - e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise) |
60 | | - }); |
61 | | - } else { |
62 | | - (async function () { |
63 | | - if (window.crossOriginIsolated !== false) return; |
64 | | - |
65 | | - let registration = await navigator.serviceWorker |
66 | | - .register(window.document.currentScript.src) |
67 | | - .catch((e) => |
68 | | - console.error('COOP/COEP Service Worker failed to register:', e) |
69 | | - ); |
70 | | - if (registration) { |
71 | | - console.log( |
72 | | - 'COOP/COEP Service Worker registered', |
73 | | - registration.scope |
74 | | - ); |
75 | | - |
76 | | - registration.addEventListener('updatefound', () => { |
| 124 | + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are |
| 125 | + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. |
| 126 | + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) |
| 127 | + return; |
| 128 | + |
| 129 | + if (!window.isSecureContext) { |
| 130 | + !coi.quiet && |
77 | 131 | console.log( |
78 | | - 'Reloading page to make use of updated COOP/COEP Service Worker.' |
| 132 | + 'COOP/COEP Service Worker not registered, a secure context is required.' |
79 | 133 | ); |
80 | | - window.location.reload(); |
81 | | - }); |
| 134 | + return; |
| 135 | + } |
82 | 136 |
|
83 | | - // If the registration is active, but it's not controlling the page |
84 | | - if (registration.active && !navigator.serviceWorker.controller) { |
85 | | - console.log( |
86 | | - 'Reloading page to make use of COOP/COEP Service Worker.' |
| 137 | + // In some environments (e.g. Firefox private mode) this won't be available |
| 138 | + if (!n.serviceWorker) { |
| 139 | + !coi.quiet && |
| 140 | + console.error( |
| 141 | + 'COOP/COEP Service Worker not registered, perhaps due to private mode.' |
87 | 142 | ); |
88 | | - window.location.reload(); |
89 | | - } |
| 143 | + return; |
90 | 144 | } |
| 145 | + |
| 146 | + n.serviceWorker.register(window.document.currentScript.src).then( |
| 147 | + (registration) => { |
| 148 | + !coi.quiet && |
| 149 | + console.log( |
| 150 | + 'COOP/COEP Service Worker registered', |
| 151 | + registration.scope |
| 152 | + ); |
| 153 | + |
| 154 | + registration.addEventListener('updatefound', () => { |
| 155 | + !coi.quiet && |
| 156 | + console.log( |
| 157 | + 'Reloading page to make use of updated COOP/COEP Service Worker.' |
| 158 | + ); |
| 159 | + window.sessionStorage.setItem( |
| 160 | + 'coiReloadedBySelf', |
| 161 | + 'updatefound' |
| 162 | + ); |
| 163 | + coi.doReload(); |
| 164 | + }); |
| 165 | + |
| 166 | + // If the registration is active, but it's not controlling the page |
| 167 | + if (registration.active && !n.serviceWorker.controller) { |
| 168 | + !coi.quiet && |
| 169 | + console.log( |
| 170 | + 'Reloading page to make use of COOP/COEP Service Worker.' |
| 171 | + ); |
| 172 | + window.sessionStorage.setItem( |
| 173 | + 'coiReloadedBySelf', |
| 174 | + 'notcontrolling' |
| 175 | + ); |
| 176 | + coi.doReload(); |
| 177 | + } |
| 178 | + }, |
| 179 | + (err) => { |
| 180 | + !coi.quiet && |
| 181 | + console.error( |
| 182 | + 'COOP/COEP Service Worker failed to register:', |
| 183 | + err |
| 184 | + ); |
| 185 | + } |
| 186 | + ); |
91 | 187 | })(); |
92 | 188 | } |
93 | 189 | </script> |
|
0 commit comments