@codingame/monaco-vscode-view-common-service-override 19.1.4 → 20.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/index.js +4 -4
  2. package/package.json +41 -42
  3. package/service-override/vs/workbench/contrib/webview/browser/pre/service-worker.js +481 -329
  4. package/vscode/src/vs/platform/actions/browser/actionViewItemService.d.ts +11 -5
  5. package/vscode/src/vs/workbench/api/browser/viewsExtensionPoint.js +58 -58
  6. package/vscode/src/vs/workbench/browser/actions/listCommands.js +4 -4
  7. package/vscode/src/vs/workbench/browser/actions/navigationActions.js +6 -6
  8. package/vscode/src/vs/workbench/browser/parts/editor/editor.contribution.js +176 -169
  9. package/vscode/src/vs/workbench/browser/parts/editor/editorConfiguration.js +8 -8
  10. package/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.js +12 -12
  11. package/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.js +5 -5
  12. package/vscode/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.js +3 -3
  13. package/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.d.ts +1 -1
  14. package/vscode/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.js +2 -2
  15. package/vscode/src/vs/workbench/contrib/customEditor/browser/customEditors.js +1 -1
  16. package/vscode/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.js +3 -3
  17. package/vscode/src/vs/workbench/contrib/customEditor/common/customEditor.js +1 -1
  18. package/vscode/src/vs/workbench/contrib/customEditor/common/extensionPoint.js +12 -12
  19. package/vscode/src/vs/workbench/contrib/languageStatus/browser/languageStatus.js +7 -7
  20. package/vscode/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.js +8 -8
  21. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.js +30 -30
  22. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.js +14 -14
  23. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.js +3 -3
  24. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorAccessibilityHelp.js +5 -5
  25. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/colors.js +13 -13
  26. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.d.ts +1 -1
  27. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.js +1 -1
  28. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.js +3 -3
  29. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.d.ts +2 -2
  30. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.js +12 -12
  31. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.js +6 -6
  32. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.d.ts +1 -1
  33. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.js +2 -2
  34. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.d.ts +1 -1
  35. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.js +2 -2
  36. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.js +1 -1
  37. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.d.ts +1 -1
  38. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.js +2 -2
  39. package/vscode/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.js +2 -2
  40. package/vscode/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.js +12 -12
  41. package/vscode/src/vs/workbench/contrib/sash/browser/sash.contribution.js +2 -2
  42. package/vscode/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.js +10 -10
  43. package/vscode/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.js +5 -5
  44. package/vscode/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree.js +3 -3
  45. package/vscode/src/vs/workbench/contrib/webview/browser/overlayWebview.d.ts +5 -3
  46. package/vscode/src/vs/workbench/contrib/webview/browser/overlayWebview.js +8 -4
  47. package/vscode/src/vs/workbench/contrib/webview/browser/themeing.d.ts +1 -1
  48. package/vscode/src/vs/workbench/contrib/webview/browser/webviewElement.d.ts +6 -3
  49. package/vscode/src/vs/workbench/contrib/webview/browser/webviewElement.js +15 -9
  50. package/vscode/src/vs/workbench/contrib/webview/browser/webviewFindWidget.js +1 -1
  51. package/vscode/src/vs/workbench/contrib/webview/browser/webviewService.d.ts +1 -1
  52. package/vscode/src/vs/workbench/contrib/webviewPanel/browser/webviewCommands.js +7 -7
  53. package/vscode/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.d.ts +2 -2
  54. package/vscode/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.js +1 -1
  55. package/vscode/src/vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution.js +2 -2
  56. package/vscode/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.js +3 -3
  57. package/vscode/src/vs/workbench/contrib/webviewView/browser/webviewViewService.d.ts +1 -1
  58. package/vscode/src/vs/workbench/services/editor/browser/editorResolverService.js +10 -10
  59. package/vscode/src/vs/workbench/services/history/browser/historyService.js +10 -10
  60. package/vscode/src/vs/workbench/services/progress/browser/progressService.js +9 -9
  61. package/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.d.ts +2 -2
  62. package/vscode/src/vs/workbench/services/untitled/common/untitledTextEditorHandler.js +2 -2
  63. package/vscode/src/vs/workbench/services/views/browser/viewDescriptorService.js +5 -5
@@ -1,371 +1,523 @@
1
- "use strict";
2
1
  /*---------------------------------------------------------------------------------------------
3
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
4
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
5
4
  *--------------------------------------------------------------------------------------------*/
5
+ //@ts-check
6
6
  /// <reference lib="webworker" />
7
- const sw = self;
7
+
8
+ /** @type {ServiceWorkerGlobalScope} */
9
+ const sw = /** @type {any} */ (self);
10
+
8
11
  const VERSION = 4;
12
+
9
13
  const resourceCacheName = `vscode-resource-cache-${VERSION}`;
14
+
10
15
  const rootPath = sw.location.pathname.replace(/\/service-worker.js$/, '');
16
+
11
17
  const searchParams = new URL(location.toString()).searchParams;
18
+
12
19
  const remoteAuthority = searchParams.get('remoteAuthority');
20
+
21
+ /** @type {MessagePort|undefined} */
13
22
  let outerIframeMessagePort;
23
+
14
24
  /**
15
25
  * Origin used for resources
16
26
  */
17
27
  const resourceBaseAuthority = searchParams.get('vscode-resource-base-authority');
28
+
29
+
30
+ /** @type {number} */
18
31
  const resolveTimeout = 30_000;
32
+
33
+
34
+ /**
35
+ * @template T
36
+ * @typedef {{ status: 'ok', value: T } | { status: 'timeout' }} RequestStoreResult
37
+ */
38
+
39
+
40
+ /**
41
+ * @template T
42
+ * @typedef {{ resolve: (x: RequestStoreResult<T>) => void, promise: Promise<RequestStoreResult<T>> }} RequestStoreEntry
43
+ */
44
+
45
+
46
+ /**
47
+ * @template T
48
+ */
19
49
  class RequestStore {
20
- constructor() {
21
- this.map = new Map();
22
- this.requestPool = 0;
23
- }
24
- create() {
25
- const requestId = ++this.requestPool;
26
- let resolve;
27
- const promise = new Promise(r => resolve = r);
28
- const entry = { resolve: resolve, promise };
29
- this.map.set(requestId, entry);
30
- const dispose = () => {
31
- clearTimeout(timeout);
32
- const existingEntry = this.map.get(requestId);
33
- if (existingEntry === entry) {
34
- existingEntry.resolve({ status: 'timeout' });
35
- this.map.delete(requestId);
36
- }
37
- };
38
- const timeout = setTimeout(dispose, resolveTimeout);
39
- return { requestId, promise };
40
- }
41
- resolve(requestId, result) {
42
- const entry = this.map.get(requestId);
43
- if (!entry) {
44
- return false;
45
- }
46
- entry.resolve({ status: 'ok', value: result });
47
- this.map.delete(requestId);
48
- return true;
49
- }
50
+ constructor() {
51
+ /** @type {Map<number, RequestStoreEntry<T>>} */
52
+ this.map = new Map();
53
+ /** @type {number} */
54
+ this.requestPool = 0;
55
+ }
56
+
57
+ /**
58
+ * @returns {{ requestId: number, promise: Promise<RequestStoreResult<T>> }}
59
+ */
60
+ create() {
61
+ const requestId = ++this.requestPool;
62
+
63
+ /** @type {(x: RequestStoreResult<T>) => void} */
64
+ let resolve;
65
+ const promise = new Promise(r => resolve = r);
66
+
67
+ /** @type {RequestStoreEntry<T>} */
68
+ const entry = { resolve, promise };
69
+ this.map.set(requestId, entry);
70
+
71
+ const dispose = () => {
72
+ clearTimeout(timeout);
73
+ const existingEntry = this.map.get(requestId);
74
+ if (existingEntry === entry) {
75
+ existingEntry.resolve({ status: 'timeout' });
76
+ this.map.delete(requestId);
77
+ }
78
+ };
79
+ const timeout = setTimeout(dispose, resolveTimeout);
80
+ return { requestId, promise };
81
+ }
82
+
83
+ /**
84
+ * @param {number} requestId
85
+ * @param {T} result
86
+ * @returns {boolean}
87
+ */
88
+ resolve(requestId, result) {
89
+ const entry = this.map.get(requestId);
90
+ if (!entry) {
91
+ return false;
92
+ }
93
+ entry.resolve({ status: 'ok', value: result });
94
+ this.map.delete(requestId);
95
+ return true;
96
+ }
50
97
  }
98
+
51
99
  /**
52
100
  * Map of requested paths to responses.
53
101
  */
102
+ /** @type {RequestStore<ResourceResponse>} */
54
103
  const resourceRequestStore = new RequestStore();
104
+
55
105
  /**
56
106
  * Map of requested localhost origins to optional redirects.
57
107
  */
108
+ /** @type {RequestStore<string|undefined>} */
58
109
  const localhostRequestStore = new RequestStore();
59
- const unauthorized = () => new Response('Unauthorized', { status: 401, });
60
- const notFound = () => new Response('Not Found', { status: 404, });
61
- const methodNotAllowed = () => new Response('Method Not Allowed', { status: 405, });
62
- const requestTimeout = () => new Response('Request Timeout', { status: 408, });
110
+
111
+ const unauthorized = () =>
112
+ new Response('Unauthorized', { status: 401, });
113
+
114
+ const notFound = () =>
115
+ new Response('Not Found', { status: 404, });
116
+
117
+ const methodNotAllowed = () =>
118
+ new Response('Method Not Allowed', { status: 405, });
119
+
120
+ const requestTimeout = () =>
121
+ new Response('Request Timeout', { status: 408, });
122
+
63
123
  sw.addEventListener('message', async (event) => {
64
- if (!event.source) {
65
- return;
66
- }
67
- const source = event.source;
68
- switch (event.data.channel) {
69
- case 'version': {
70
- outerIframeMessagePort = event.ports[0];
71
- sw.clients.get(source.id).then(client => {
72
- if (client) {
73
- client.postMessage({
74
- channel: 'version',
75
- version: VERSION
76
- });
77
- }
78
- });
79
- return;
80
- }
81
- case 'did-load-resource': {
82
- const response = event.data.data;
83
- if (!resourceRequestStore.resolve(response.id, response)) {
84
- console.log('Could not resolve unknown resource', response.path);
85
- }
86
- return;
87
- }
88
- case 'did-load-localhost': {
89
- const data = event.data.data;
90
- if (!localhostRequestStore.resolve(data.id, data.location)) {
91
- console.log('Could not resolve unknown localhost', data.origin);
92
- }
93
- return;
94
- }
95
- default: {
96
- console.log('Unknown message');
97
- return;
98
- }
99
- }
124
+ if (!event.source) {
125
+ return;
126
+ }
127
+
128
+ /** @type {Client} */
129
+ const source = event.source;
130
+ switch (event.data.channel) {
131
+ case 'version': {
132
+ outerIframeMessagePort = event.ports[0];
133
+ sw.clients.get(source.id).then(client => {
134
+ if (client) {
135
+ client.postMessage({
136
+ channel: 'version',
137
+ version: VERSION
138
+ });
139
+ }
140
+ });
141
+ return;
142
+ }
143
+ case 'did-load-resource': {
144
+ /** @type {ResourceResponse} */
145
+ const response = event.data.data;
146
+ if (!resourceRequestStore.resolve(response.id, response)) {
147
+ console.log('Could not resolve unknown resource', response.path);
148
+ }
149
+ return;
150
+ }
151
+ case 'did-load-localhost': {
152
+ const data = event.data.data;
153
+ if (!localhostRequestStore.resolve(data.id, data.location)) {
154
+ console.log('Could not resolve unknown localhost', data.origin);
155
+ }
156
+ return;
157
+ }
158
+ default: {
159
+ console.log('Unknown message');
160
+ return;
161
+ }
162
+ }
100
163
  });
164
+
101
165
  sw.addEventListener('fetch', (event) => {
102
- const requestUrl = new URL(event.request.url);
103
- if (typeof resourceBaseAuthority === 'string' && requestUrl.protocol === 'https:' && requestUrl.hostname.endsWith('.' + resourceBaseAuthority)) {
104
- switch (event.request.method) {
105
- case 'GET':
106
- case 'HEAD': {
107
- const firstHostSegment = requestUrl.hostname.slice(0, requestUrl.hostname.length - (resourceBaseAuthority.length + 1));
108
- const scheme = firstHostSegment.split('+', 1)[0];
109
- const authority = firstHostSegment.slice(scheme.length + 1); // may be empty
110
- return event.respondWith(processResourceRequest(event, {
111
- scheme,
112
- authority,
113
- path: requestUrl.pathname,
114
- query: requestUrl.search.replace(/^\?/, ''),
115
- }));
116
- }
117
- default: {
118
- return event.respondWith(methodNotAllowed());
119
- }
120
- }
121
- }
122
- // If we're making a request against the remote authority, we want to go
123
- // through VS Code itself so that we are authenticated properly. If the
124
- // service worker is hosted on the same origin we will have cookies and
125
- // authentication will not be an issue.
126
- if (requestUrl.origin !== sw.origin && requestUrl.host === remoteAuthority) {
127
- switch (event.request.method) {
128
- case 'GET':
129
- case 'HEAD': {
130
- return event.respondWith(processResourceRequest(event, {
131
- path: requestUrl.pathname,
132
- scheme: requestUrl.protocol.slice(0, requestUrl.protocol.length - 1),
133
- authority: requestUrl.host,
134
- query: requestUrl.search.replace(/^\?/, ''),
135
- }));
136
- }
137
- default: {
138
- return event.respondWith(methodNotAllowed());
139
- }
140
- }
141
- }
142
- // See if it's a localhost request
143
- if (requestUrl.origin !== sw.origin && requestUrl.host.match(/^(localhost|127.0.0.1|0.0.0.0):(\d+)$/)) {
144
- return event.respondWith(processLocalhostRequest(event, requestUrl));
145
- }
166
+ const requestUrl = new URL(event.request.url);
167
+ if (typeof resourceBaseAuthority === 'string' && requestUrl.protocol === 'https:' && requestUrl.hostname.endsWith('.' + resourceBaseAuthority)) {
168
+ switch (event.request.method) {
169
+ case 'GET':
170
+ case 'HEAD': {
171
+ const firstHostSegment = requestUrl.hostname.slice(0, requestUrl.hostname.length - (resourceBaseAuthority.length + 1));
172
+ const scheme = firstHostSegment.split('+', 1)[0];
173
+ const authority = firstHostSegment.slice(scheme.length + 1); // may be empty
174
+ return event.respondWith(processResourceRequest(event, {
175
+ scheme,
176
+ authority,
177
+ path: requestUrl.pathname,
178
+ query: requestUrl.search.replace(/^\?/, ''),
179
+ }));
180
+ }
181
+ default: {
182
+ return event.respondWith(methodNotAllowed());
183
+ }
184
+ }
185
+ }
186
+
187
+ // If we're making a request against the remote authority, we want to go
188
+ // through VS Code itself so that we are authenticated properly. If the
189
+ // service worker is hosted on the same origin we will have cookies and
190
+ // authentication will not be an issue.
191
+ if (requestUrl.origin !== sw.origin && requestUrl.host === remoteAuthority) {
192
+ switch (event.request.method) {
193
+ case 'GET':
194
+ case 'HEAD': {
195
+ return event.respondWith(processResourceRequest(event, {
196
+ path: requestUrl.pathname,
197
+ scheme: requestUrl.protocol.slice(0, requestUrl.protocol.length - 1),
198
+ authority: requestUrl.host,
199
+ query: requestUrl.search.replace(/^\?/, ''),
200
+ }));
201
+ }
202
+ default: {
203
+ return event.respondWith(methodNotAllowed());
204
+ }
205
+ }
206
+ }
207
+
208
+ // See if it's a localhost request
209
+ if (requestUrl.origin !== sw.origin && requestUrl.host.match(/^(localhost|127.0.0.1|0.0.0.0):(\d+)$/)) {
210
+ return event.respondWith(processLocalhostRequest(event, requestUrl));
211
+ }
146
212
  });
213
+
147
214
  sw.addEventListener('install', (event) => {
148
- event.waitUntil(sw.skipWaiting()); // Activate worker immediately
215
+ event.waitUntil(sw.skipWaiting()); // Activate worker immediately
149
216
  });
217
+
150
218
  sw.addEventListener('activate', (event) => {
151
- event.waitUntil(sw.clients.claim()); // Become available to all pages
219
+ event.waitUntil(sw.clients.claim()); // Become available to all pages
152
220
  });
153
- async function processResourceRequest(event, requestUrlComponents) {
154
- let client = await sw.clients.get(event.clientId);
155
- if (!client) {
156
- client = await getWorkerClientForId(event.clientId);
157
- if (!client) {
158
- console.error('Could not find inner client for request');
159
- return notFound();
160
- }
161
- }
162
- const webviewId = getWebviewIdForClient(client);
163
- // Refs https://github.com/microsoft/vscode/issues/244143
164
- // With PlzDedicatedWorker, worker subresources and blob wokers
165
- // will use clients different from the window client.
166
- // Since we cannot different a worker main resource from a worker subresource
167
- // we will use message channel to the outer iframe provided at the time
168
- // of service worker controller version initialization.
169
- if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
170
- console.error('Could not resolve webview id');
171
- return notFound();
172
- }
173
- const shouldTryCaching = (event.request.method === 'GET');
174
- const resolveResourceEntry = (result, cachedResponse) => {
175
- if (result.status === 'timeout') {
176
- return requestTimeout();
177
- }
178
- const entry = result.value;
179
- if (entry.status === 304) { // Not modified
180
- if (cachedResponse) {
181
- return cachedResponse.clone();
182
- }
183
- else {
184
- throw new Error('No cache found');
185
- }
186
- }
187
- if (entry.status === 401) {
188
- return unauthorized();
189
- }
190
- if (entry.status !== 200) {
191
- return notFound();
192
- }
193
- const commonHeaders = {
194
- 'Access-Control-Allow-Origin': '*',
195
- };
196
- const byteLength = entry.data.byteLength;
197
- const range = event.request.headers.get('range');
198
- if (range) {
199
- // To support seeking for videos, we need to handle range requests
200
- const bytes = range.match(/^bytes\=(\d+)\-(\d+)?$/g);
201
- if (bytes) {
202
- // TODO: Right now we are always reading the full file content. This is a bad idea
203
- // for large video files :)
204
- const start = Number(bytes[1]);
205
- const end = Number(bytes[2]) || byteLength - 1;
206
- return new Response(entry.data.slice(start, end + 1), {
207
- status: 206,
208
- headers: {
209
- ...commonHeaders,
210
- 'Content-range': `bytes 0-${end}/${byteLength}`,
211
- }
212
- });
213
- }
214
- else {
215
- // We don't understand the requested bytes
216
- return new Response(null, {
217
- status: 416,
218
- headers: {
219
- ...commonHeaders,
220
- 'Content-range': `*/${byteLength}`
221
- }
222
- });
223
- }
224
- }
225
- const headers = {
226
- ...commonHeaders,
227
- 'Content-Type': entry.mime,
228
- 'Content-Length': byteLength.toString(),
229
- };
230
- if (entry.etag) {
231
- headers['ETag'] = entry.etag;
232
- headers['Cache-Control'] = 'no-cache';
233
- }
234
- if (entry.mtime) {
235
- headers['Last-Modified'] = new Date(entry.mtime).toUTCString();
236
- }
237
- // support COI requests, see network.ts#COI.getHeadersFromQuery(...)
238
- const coiRequest = new URL(event.request.url).searchParams.get('vscode-coi');
239
- if (coiRequest === '3') {
240
- headers['Cross-Origin-Opener-Policy'] = 'same-origin';
241
- headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
242
- }
243
- else if (coiRequest === '2') {
244
- headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
245
- }
246
- else if (coiRequest === '1') {
247
- headers['Cross-Origin-Opener-Policy'] = 'same-origin';
248
- }
249
- const response = new Response(entry.data, {
250
- status: 200,
251
- headers
252
- });
253
- if (shouldTryCaching && entry.etag) {
254
- caches.open(resourceCacheName).then(cache => {
255
- return cache.put(event.request, response);
256
- });
257
- }
258
- return response.clone();
259
- };
260
- let cached;
261
- if (shouldTryCaching) {
262
- const cache = await caches.open(resourceCacheName);
263
- cached = await cache.match(event.request);
264
- }
265
- const { requestId, promise } = resourceRequestStore.create();
266
- if (webviewId) {
267
- const parentClients = await getOuterIframeClient(webviewId);
268
- if (!parentClients.length) {
269
- console.log('Could not find parent client for request');
270
- return notFound();
271
- }
272
- for (const parentClient of parentClients) {
273
- parentClient.postMessage({
274
- channel: 'load-resource',
275
- id: requestId,
276
- scheme: requestUrlComponents.scheme,
277
- authority: requestUrlComponents.authority,
278
- path: requestUrlComponents.path,
279
- query: requestUrlComponents.query,
280
- ifNoneMatch: cached?.headers.get('ETag'),
281
- });
282
- }
283
- }
284
- else if (client.type === 'worker' || client.type === 'sharedworker') {
285
- outerIframeMessagePort?.postMessage({
286
- channel: 'load-resource',
287
- id: requestId,
288
- scheme: requestUrlComponents.scheme,
289
- authority: requestUrlComponents.authority,
290
- path: requestUrlComponents.path,
291
- query: requestUrlComponents.query,
292
- ifNoneMatch: cached?.headers.get('ETag'),
293
- });
294
- }
295
- return promise.then(entry => resolveResourceEntry(entry, cached));
221
+
222
+
223
+ /**
224
+ * @typedef {Object} ResourceRequestUrlComponents
225
+ * @property {string} scheme
226
+ * @property {string} authority
227
+ * @property {string} path
228
+ * @property {string} query
229
+ */
230
+
231
+ /**
232
+ * @param {FetchEvent} event
233
+ * @param {ResourceRequestUrlComponents} requestUrlComponents
234
+ * @returns {Promise<Response>}
235
+ */
236
+ async function processResourceRequest(
237
+ event,
238
+ requestUrlComponents
239
+ ) {
240
+ let client = await sw.clients.get(event.clientId);
241
+ if (!client) {
242
+ client = await getWorkerClientForId(event.clientId);
243
+ if (!client) {
244
+ console.error('Could not find inner client for request');
245
+ return notFound();
246
+ }
247
+ }
248
+
249
+ const webviewId = getWebviewIdForClient(client);
250
+
251
+ // Refs https://github.com/microsoft/vscode/issues/244143
252
+ // With PlzDedicatedWorker, worker subresources and blob wokers
253
+ // will use clients different from the window client.
254
+ // Since we cannot different a worker main resource from a worker subresource
255
+ // we will use message channel to the outer iframe provided at the time
256
+ // of service worker controller version initialization.
257
+ if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
258
+ console.error('Could not resolve webview id');
259
+ return notFound();
260
+ }
261
+
262
+ const shouldTryCaching = (event.request.method === 'GET');
263
+
264
+ /**
265
+ * @param {RequestStoreResult<ResourceResponse>} result
266
+ * @param {Response|undefined} cachedResponse
267
+ * @returns {Response}
268
+ */
269
+ const resolveResourceEntry = (result, cachedResponse) => {
270
+ if (result.status === 'timeout') {
271
+ return requestTimeout();
272
+ }
273
+
274
+ const entry = result.value;
275
+ if (entry.status === 304) { // Not modified
276
+ if (cachedResponse) {
277
+ return cachedResponse.clone();
278
+ } else {
279
+ throw new Error('No cache found');
280
+ }
281
+ }
282
+
283
+ if (entry.status === 401) {
284
+ return unauthorized();
285
+ }
286
+
287
+ if (entry.status !== 200) {
288
+ return notFound();
289
+ }
290
+
291
+ /** @type {Record<string, string>} */
292
+ const commonHeaders = {
293
+ 'Access-Control-Allow-Origin': '*',
294
+ };
295
+
296
+ const byteLength = entry.data.byteLength;
297
+
298
+ const range = event.request.headers.get('range');
299
+ if (range) {
300
+ // To support seeking for videos, we need to handle range requests
301
+ const bytes = range.match(/^bytes\=(\d+)\-(\d+)?$/g);
302
+ if (bytes) {
303
+ // TODO: Right now we are always reading the full file content. This is a bad idea
304
+ // for large video files :)
305
+
306
+ const start = Number(bytes[1]);
307
+ const end = Number(bytes[2]) || byteLength - 1;
308
+ return new Response(entry.data.slice(start, end + 1), {
309
+ status: 206,
310
+ headers: {
311
+ ...commonHeaders,
312
+ 'Content-range': `bytes 0-${end}/${byteLength}`,
313
+ }
314
+ });
315
+ } else {
316
+ // We don't understand the requested bytes
317
+ return new Response(null, {
318
+ status: 416,
319
+ headers: {
320
+ ...commonHeaders,
321
+ 'Content-range': `*/${byteLength}`
322
+ }
323
+ });
324
+ }
325
+ }
326
+
327
+ /** @type {Record<string, string>} */
328
+ const headers = {
329
+ ...commonHeaders,
330
+ 'Content-Type': entry.mime,
331
+ 'Content-Length': byteLength.toString(),
332
+ };
333
+
334
+ if (entry.etag) {
335
+ headers['ETag'] = entry.etag;
336
+ headers['Cache-Control'] = 'no-cache';
337
+ }
338
+ if (entry.mtime) {
339
+ headers['Last-Modified'] = new Date(entry.mtime).toUTCString();
340
+ }
341
+
342
+ // support COI requests, see network.ts#COI.getHeadersFromQuery(...)
343
+ const coiRequest = new URL(event.request.url).searchParams.get('vscode-coi');
344
+ if (coiRequest === '3') {
345
+ headers['Cross-Origin-Opener-Policy'] = 'same-origin';
346
+ headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
347
+ } else if (coiRequest === '2') {
348
+ headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
349
+ } else if (coiRequest === '1') {
350
+ headers['Cross-Origin-Opener-Policy'] = 'same-origin';
351
+ }
352
+
353
+ const response = new Response(entry.data, {
354
+ status: 200,
355
+ headers
356
+ });
357
+
358
+ if (shouldTryCaching && entry.etag) {
359
+ caches.open(resourceCacheName).then(cache => {
360
+ return cache.put(event.request, response);
361
+ });
362
+ }
363
+ return response.clone();
364
+ };
365
+
366
+ /** @type {Response|undefined} */
367
+ let cached;
368
+ if (shouldTryCaching) {
369
+ const cache = await caches.open(resourceCacheName);
370
+ cached = await cache.match(event.request);
371
+ }
372
+
373
+ const { requestId, promise } = resourceRequestStore.create();
374
+
375
+ if (webviewId) {
376
+ const parentClients = await getOuterIframeClient(webviewId);
377
+ if (!parentClients.length) {
378
+ console.log('Could not find parent client for request');
379
+ return notFound();
380
+ }
381
+
382
+ for (const parentClient of parentClients) {
383
+ parentClient.postMessage({
384
+ channel: 'load-resource',
385
+ id: requestId,
386
+ scheme: requestUrlComponents.scheme,
387
+ authority: requestUrlComponents.authority,
388
+ path: requestUrlComponents.path,
389
+ query: requestUrlComponents.query,
390
+ ifNoneMatch: cached?.headers.get('ETag'),
391
+ });
392
+ }
393
+ } else if (client.type === 'worker' || client.type === 'sharedworker') {
394
+ outerIframeMessagePort?.postMessage({
395
+ channel: 'load-resource',
396
+ id: requestId,
397
+ scheme: requestUrlComponents.scheme,
398
+ authority: requestUrlComponents.authority,
399
+ path: requestUrlComponents.path,
400
+ query: requestUrlComponents.query,
401
+ ifNoneMatch: cached?.headers.get('ETag'),
402
+ });
403
+ }
404
+
405
+ return promise.then(entry => resolveResourceEntry(entry, cached));
296
406
  }
297
- async function processLocalhostRequest(event, requestUrl) {
298
- const client = await sw.clients.get(event.clientId);
299
- if (!client) {
300
- // This is expected when requesting resources on other localhost ports
301
- // that are not spawned by vs code
302
- return fetch(event.request);
303
- }
304
- const webviewId = getWebviewIdForClient(client);
305
- // Refs https://github.com/microsoft/vscode/issues/244143
306
- // With PlzDedicatedWorker, worker subresources and blob wokers
307
- // will use clients different from the window client.
308
- // Since we cannot different a worker main resource from a worker subresource
309
- // we will use message channel to the outer iframe provided at the time
310
- // of service worker controller version initialization.
311
- if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
312
- console.error('Could not resolve webview id');
313
- return fetch(event.request);
314
- }
315
- const origin = requestUrl.origin;
316
- const resolveRedirect = async (result) => {
317
- if (result.status !== 'ok' || !result.value) {
318
- return fetch(event.request);
319
- }
320
- const redirectOrigin = result.value;
321
- const location = event.request.url.replace(new RegExp(`^${requestUrl.origin}(/|$)`), `${redirectOrigin}$1`);
322
- return new Response(null, {
323
- status: 302,
324
- headers: {
325
- Location: location
326
- }
327
- });
328
- };
329
- const { requestId, promise } = localhostRequestStore.create();
330
- if (webviewId) {
331
- const parentClients = await getOuterIframeClient(webviewId);
332
- if (!parentClients.length) {
333
- console.log('Could not find parent client for request');
334
- return notFound();
335
- }
336
- for (const parentClient of parentClients) {
337
- parentClient.postMessage({
338
- channel: 'load-localhost',
339
- origin: origin,
340
- id: requestId,
341
- });
342
- }
343
- }
344
- else if (client.type === 'worker' || client.type === 'sharedworker') {
345
- outerIframeMessagePort?.postMessage({
346
- channel: 'load-localhost',
347
- origin: origin,
348
- id: requestId,
349
- });
350
- }
351
- return promise.then(resolveRedirect);
407
+
408
+ /**
409
+ * @param {FetchEvent} event
410
+ * @param {URL} requestUrl
411
+ * @returns {Promise<Response>}
412
+ */
413
+ async function processLocalhostRequest(
414
+ event,
415
+ requestUrl
416
+ ) {
417
+ const client = await sw.clients.get(event.clientId);
418
+ if (!client) {
419
+ // This is expected when requesting resources on other localhost ports
420
+ // that are not spawned by vs code
421
+ return fetch(event.request);
422
+ }
423
+ const webviewId = getWebviewIdForClient(client);
424
+ // Refs https://github.com/microsoft/vscode/issues/244143
425
+ // With PlzDedicatedWorker, worker subresources and blob wokers
426
+ // will use clients different from the window client.
427
+ // Since we cannot different a worker main resource from a worker subresource
428
+ // we will use message channel to the outer iframe provided at the time
429
+ // of service worker controller version initialization.
430
+ if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
431
+ console.error('Could not resolve webview id');
432
+ return fetch(event.request);
433
+ }
434
+
435
+ const origin = requestUrl.origin;
436
+
437
+ /**
438
+ * @param {RequestStoreResult<string|undefined>} result
439
+ * @returns {Promise<Response>}
440
+ */
441
+ const resolveRedirect = async function (result) {
442
+ if (result.status !== 'ok' || !result.value) {
443
+ return fetch(event.request);
444
+ }
445
+
446
+ const redirectOrigin = result.value;
447
+ const location = event.request.url.replace(new RegExp(`^${requestUrl.origin}(/|$)`), `${redirectOrigin}$1`);
448
+ return new Response(null, {
449
+ status: 302,
450
+ headers: {
451
+ Location: location
452
+ }
453
+ });
454
+ };
455
+
456
+ const { requestId, promise } = localhostRequestStore.create();
457
+ if (webviewId) {
458
+ const parentClients = await getOuterIframeClient(webviewId);
459
+ if (!parentClients.length) {
460
+ console.log('Could not find parent client for request');
461
+ return notFound();
462
+ }
463
+ for (const parentClient of parentClients) {
464
+ parentClient.postMessage({
465
+ channel: 'load-localhost',
466
+ origin: origin,
467
+ id: requestId,
468
+ });
469
+ }
470
+ } else if (client.type === 'worker' || client.type === 'sharedworker') {
471
+ outerIframeMessagePort?.postMessage({
472
+ channel: 'load-localhost',
473
+ origin: origin,
474
+ id: requestId,
475
+ });
476
+ }
477
+
478
+ return promise.then(resolveRedirect);
352
479
  }
480
+
481
+ /**
482
+ * @param {Client} client
483
+ * @returns {string|null}
484
+ */
353
485
  function getWebviewIdForClient(client) {
354
- const requesterClientUrl = new URL(client.url);
355
- return requesterClientUrl.searchParams.get('id');
486
+ const requesterClientUrl = new URL(client.url);
487
+ return requesterClientUrl.searchParams.get('id');
356
488
  }
489
+
490
+ /**
491
+ * @param {string} webviewId
492
+ * @returns {Promise<Client[]>}
493
+ */
357
494
  async function getOuterIframeClient(webviewId) {
358
- const allClients = await sw.clients.matchAll({ includeUncontrolled: true });
359
- return allClients.filter(client => {
360
- const clientUrl = new URL(client.url);
361
- return clientUrl.searchParams.get('id') === webviewId;
362
- });
495
+ const allClients = await sw.clients.matchAll({ includeUncontrolled: true });
496
+ return allClients.filter(client => {
497
+ const clientUrl = new URL(client.url);
498
+ return clientUrl.searchParams.get('id') === webviewId;
499
+ });
363
500
  }
501
+
502
+ /**
503
+ * @param {string} clientId
504
+ * @returns {Promise<Client|undefined>}
505
+ */
364
506
  async function getWorkerClientForId(clientId) {
365
- const allDedicatedWorkerClients = await sw.clients.matchAll({ type: 'worker' });
366
- const allSharedWorkerClients = await sw.clients.matchAll({ type: 'sharedworker' });
367
- const allWorkerClients = [...allDedicatedWorkerClients, ...allSharedWorkerClients];
368
- return allWorkerClients.find(client => {
369
- return client.id === clientId;
370
- });
507
+ const allDedicatedWorkerClients = await sw.clients.matchAll({ type: 'worker' });
508
+ const allSharedWorkerClients = await sw.clients.matchAll({ type: 'sharedworker' });
509
+ const allWorkerClients = [...allDedicatedWorkerClients, ...allSharedWorkerClients];
510
+ return allWorkerClients.find(client => {
511
+ return client.id === clientId;
512
+ });
371
513
  }
514
+
515
+
516
+ /**
517
+ * @typedef {(
518
+ * | { readonly status: 200, id: number, path: string, mime: string, data: Uint8Array, etag: string|undefined, mtime: number|undefined }
519
+ * | { readonly status: 304, id: number, path: string, mime: string, mtime: number|undefined }
520
+ * | { readonly status: 401, id: number, path: string }
521
+ * | { readonly status: 404, id: number, path: string }
522
+ * )} ResourceResponse
523
+ */