vendor: 3,681 bytes, lines 1-129
1/* global self ReadableStream Response */ 2 3self.addEventListener('install', () => { 4 self.skipWaiting() 5}) 6 7self.addEventListener('activate', event => { 8 event.waitUntil(self.clients.claim()) 9}) 10 11const map = new Map() 12 13// This should be called once per download 14// Each event has a dataChannel that the data will be piped through 15self.onmessage = event => { 16 // We send a heartbeat every x second to keep the 17 // service worker alive if a transferable stream is not sent 18 if (event.data === 'ping') { 19 return 20 } 21 22 const data = event.data 23 const downloadUrl = data.url || self.registration.scope + Math.random() + '/' + (typeof data === 'string' ? data : data.filename) 24 const port = event.ports[0] 25 const metadata = new Array(3) // [stream, data, port] 26 27 metadata[1] = data 28 metadata[2] = port 29 30 // Note to self: 31 // old streamsaver v1.2.0 might still use `readableStream`... 32 // but v2.0.0 will always transfer the stream through MessageChannel #94 33 if (event.data.readableStream) { 34 metadata[0] = event.data.readableStream 35 } else if (event.data.transferringReadable) { 36 port.onmessage = evt => { 37 port.onmessage = null 38 metadata[0] = evt.data.readableStream 39 } 40 } else { 41 metadata[0] = createStream(port) 42 } 43 44 map.set(downloadUrl, metadata) 45 port.postMessage({ download: downloadUrl }) 46} 47 48function createStream (port) { 49 // ReadableStream is only supported by chrome 52 50 return new ReadableStream({ 51 start (controller) { 52 // When we receive data on the messageChannel, we write 53 port.onmessage = ({ data }) => { 54 if (data === 'end') { 55 return controller.close() 56 } 57 58 if (data === 'abort') { 59 controller.error('Aborted the download') 60 return 61 } 62 63 controller.enqueue(data) 64 } 65 }, 66 cancel (reason) { 67 console.log('user aborted', reason) 68 port.postMessage({ abort: true }) 69 } 70 }) 71} 72 73self.onfetch = event => { 74 const url = event.request.url 75 76 // this only works for Firefox 77 if (url.endsWith('/ping')) { 78 return event.respondWith(new Response('pong')) 79 } 80 81 const hijacke = map.get(url) 82 83 if (!hijacke) return null 84 85 const [ stream, data, port ] = hijacke 86 87 map.delete(url) 88 89 // Not comfortable letting any user control all headers 90 // so we only copy over the length & disposition 91 const responseHeaders = new Headers({ 92 'Content-Type': 'application/octet-stream; charset=utf-8', 93 94 // To be on the safe side, The link can be opened in a iframe. 95 // but octet-stream should stop it. 96 'Content-Security-Policy': "default-src 'none'", 97 'X-Content-Security-Policy': "default-src 'none'", 98 'X-WebKit-CSP': "default-src 'none'", 99 'X-XSS-Protection': '1; mode=block' 100 }) 101 102 let headers = new Headers(data.headers || {}) 103 104 if (headers.has('Content-Length')) { 105 responseHeaders.set('Content-Length', headers.get('Content-Length')) 106 } 107 108 if (headers.has('Content-Disposition')) { 109 responseHeaders.set('Content-Disposition', headers.get('Content-Disposition')) 110 } 111 112 // data, data.filename and size should not be used anymore 113 if (data.size) { 114 console.warn('Depricated') 115 responseHeaders.set('Content-Length', data.size) 116 } 117 118 let fileName = typeof data === 'string' ? data : data.filename 119 if (fileName) { 120 console.warn('Depricated') 121 // Make filename RFC5987 compatible 122 fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A') 123 responseHeaders.set('Content-Disposition', "attachment; filename*=UTF-8''" + fileName) 124 } 125 126 event.respondWith(new Response(stream, { headers: responseHeaders })) 127 128 port.postMessage({ debug: 'Download started' }) 129}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.