@codingame/monaco-vscode-workbench-service-override 2.2.0-next.1

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.
@@ -0,0 +1,1230 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" style="width: 100%; height: 100%;">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+
7
+ <!-- Disable pinch zooming -->
8
+ <meta name="viewport"
9
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
10
+ </head>
11
+
12
+ <body style="margin: 0; overflow: hidden; width: 100%; height: 100%; overscroll-behavior-x: none;" role="document">
13
+ <!-- TODO: Remove additional script tag once Firefox is fixed https://bugzilla.mozilla.org/show_bug.cgi?id=1737882 -->
14
+ <script></script>
15
+ <script async type="module">
16
+ // @ts-check
17
+ /// <reference lib="dom" />
18
+
19
+ const isSafari = (
20
+ navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
21
+ navigator.userAgent &&
22
+ navigator.userAgent.indexOf('CriOS') === -1 &&
23
+ navigator.userAgent.indexOf('FxiOS') === -1
24
+ );
25
+
26
+ const isFirefox = (
27
+ navigator.userAgent &&
28
+ navigator.userAgent.indexOf('Firefox') >= 0
29
+ );
30
+
31
+ const searchParams = new URL(location.toString()).searchParams;
32
+ const ID = searchParams.get('id');
33
+ const webviewOrigin = searchParams.get('origin');
34
+ const onElectron = searchParams.get('platform') === 'electron';
35
+ const disableServiceWorker = searchParams.has('disableServiceWorker');
36
+ const expectedWorkerVersion = parseInt(searchParams.get('swVersion'));
37
+ const serviceWorkerUri = searchParams.get('serviceWorkerUri');
38
+ const fakeHtmlUri = searchParams.get('fakeHtmlUri');
39
+
40
+ /**
41
+ * Use polling to track focus of main webview and iframes within the webview
42
+ *
43
+ * @param {Object} handlers
44
+ * @param {() => void} handlers.onFocus
45
+ * @param {() => void} handlers.onBlur
46
+ */
47
+ const trackFocus = ({ onFocus, onBlur }) => {
48
+ const interval = 250;
49
+ let isFocused = document.hasFocus();
50
+ setInterval(() => {
51
+ const isCurrentlyFocused = document.hasFocus();
52
+ if (isCurrentlyFocused === isFocused) {
53
+ return;
54
+ }
55
+ isFocused = isCurrentlyFocused;
56
+ if (isCurrentlyFocused) {
57
+ onFocus();
58
+ } else {
59
+ onBlur();
60
+ }
61
+ }, interval);
62
+ };
63
+
64
+ const getActiveFrame = () => {
65
+ return /** @type {HTMLIFrameElement | undefined} */ (document.getElementById('active-frame'));
66
+ };
67
+
68
+ const getPendingFrame = () => {
69
+ return /** @type {HTMLIFrameElement | undefined} */ (document.getElementById('pending-frame'));
70
+ };
71
+
72
+ /**
73
+ * @template T
74
+ * @param {T | undefined | null} obj
75
+ * @return {T}
76
+ */
77
+ function assertIsDefined(obj) {
78
+ if (typeof obj === 'undefined' || obj === null) {
79
+ throw new Error('Found unexpected null');
80
+ }
81
+ return obj;
82
+ }
83
+
84
+ const vscodePostMessageFuncName = '__vscode_post_message__';
85
+
86
+ const defaultStyles = document.createElement('style');
87
+ defaultStyles.id = '_defaultStyles';
88
+ defaultStyles.textContent = `
89
+ html {
90
+ scrollbar-color: var(--vscode-scrollbarSlider-background) var(--vscode-editor-background);
91
+ }
92
+
93
+ body {
94
+ overscroll-behavior-x: none;
95
+ background-color: transparent;
96
+ color: var(--vscode-editor-foreground);
97
+ font-family: var(--vscode-font-family);
98
+ font-weight: var(--vscode-font-weight);
99
+ font-size: var(--vscode-font-size);
100
+ margin: 0;
101
+ padding: 0 20px;
102
+ }
103
+
104
+ img, video {
105
+ max-width: 100%;
106
+ max-height: 100%;
107
+ }
108
+
109
+ a, a code {
110
+ color: var(--vscode-textLink-foreground);
111
+ }
112
+
113
+ a:hover {
114
+ color: var(--vscode-textLink-activeForeground);
115
+ }
116
+
117
+ a:focus,
118
+ input:focus,
119
+ select:focus,
120
+ textarea:focus {
121
+ outline: 1px solid -webkit-focus-ring-color;
122
+ outline-offset: -1px;
123
+ }
124
+
125
+ code {
126
+ font-family: var(--monaco-monospace-font);
127
+ color: var(--vscode-textPreformat-foreground);
128
+ background-color: var(--vscode-textPreformat-background);
129
+ padding: 1px 3px;
130
+ border-radius: 4px;
131
+ }
132
+
133
+ blockquote {
134
+ background: var(--vscode-textBlockQuote-background);
135
+ border-color: var(--vscode-textBlockQuote-border);
136
+ }
137
+
138
+ kbd {
139
+ background-color: var(--vscode-keybindingLabel-background);
140
+ color: var(--vscode-keybindingLabel-foreground);
141
+ border-style: solid;
142
+ border-width: 1px;
143
+ border-radius: 3px;
144
+ border-color: var(--vscode-keybindingLabel-border);
145
+ border-bottom-color: var(--vscode-keybindingLabel-bottomBorder);
146
+ box-shadow: inset 0 -1px 0 var(--vscode-widget-shadow);
147
+ vertical-align: middle;
148
+ padding: 1px 3px;
149
+ }
150
+
151
+ ::-webkit-scrollbar {
152
+ width: 10px;
153
+ height: 10px;
154
+ }
155
+
156
+ ::-webkit-scrollbar-corner {
157
+ background-color: var(--vscode-editor-background);
158
+ }
159
+
160
+ ::-webkit-scrollbar-thumb {
161
+ background-color: var(--vscode-scrollbarSlider-background);
162
+ }
163
+ ::-webkit-scrollbar-thumb:hover {
164
+ background-color: var(--vscode-scrollbarSlider-hoverBackground);
165
+ }
166
+ ::-webkit-scrollbar-thumb:active {
167
+ background-color: var(--vscode-scrollbarSlider-activeBackground);
168
+ }
169
+ ::highlight(find-highlight) {
170
+ background-color: var(--vscode-editor-findMatchHighlightBackground);
171
+ }
172
+ ::highlight(current-find-highlight) {
173
+ background-color: var(--vscode-editor-findMatchBackground);
174
+ }`;
175
+
176
+ /**
177
+ * @param {boolean} allowMultipleAPIAcquire
178
+ * @param {*} [state]
179
+ * @return {string}
180
+ */
181
+ function getVsCodeApiScript(allowMultipleAPIAcquire, state) {
182
+ const encodedState = state ? encodeURIComponent(state) : undefined;
183
+ return /* js */`
184
+ globalThis.acquireVsCodeApi = (function() {
185
+ const originalPostMessage = window.parent['${vscodePostMessageFuncName}'].bind(window.parent);
186
+ const doPostMessage = (channel, data, transfer) => {
187
+ originalPostMessage(channel, data, transfer);
188
+ };
189
+
190
+ let acquired = false;
191
+
192
+ let state = ${state ? `JSON.parse(decodeURIComponent("${encodedState}"))` : undefined};
193
+
194
+ return () => {
195
+ if (acquired && !${allowMultipleAPIAcquire}) {
196
+ throw new Error('An instance of the VS Code API has already been acquired');
197
+ }
198
+ acquired = true;
199
+ return Object.freeze({
200
+ postMessage: function(message, transfer) {
201
+ doPostMessage('onmessage', { message, transfer }, transfer);
202
+ },
203
+ setState: function(newState) {
204
+ state = newState;
205
+ doPostMessage('do-update-state', JSON.stringify(newState));
206
+ return newState;
207
+ },
208
+ getState: function() {
209
+ return state;
210
+ }
211
+ });
212
+ };
213
+ })();
214
+ delete window.parent;
215
+ delete window.top;
216
+ delete window.frameElement;
217
+ `;
218
+ }
219
+
220
+ /** @type {Promise<void>} */
221
+ const workerReady = new Promise((resolve, reject) => {
222
+ if (disableServiceWorker) {
223
+ return resolve();
224
+ }
225
+
226
+ if (!areServiceWorkersEnabled()) {
227
+ return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.'));
228
+ }
229
+
230
+ const swPath = encodeURI(`${serviceWorkerUri}?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`);
231
+ navigator.serviceWorker.register(swPath)
232
+ .then(async registration => {
233
+ /**
234
+ * @param {MessageEvent} event
235
+ */
236
+ const versionHandler = async (event) => {
237
+ if (event.data.channel !== 'version') {
238
+ return;
239
+ }
240
+
241
+ navigator.serviceWorker.removeEventListener('message', versionHandler);
242
+ if (event.data.version === expectedWorkerVersion) {
243
+ return resolve();
244
+ } else {
245
+ console.log(`Found unexpected service worker version. Found: ${event.data.version}. Expected: ${expectedWorkerVersion}`);
246
+ console.log(`Attempting to reload service worker`);
247
+
248
+ // If we have the wrong version, try once (and only once) to unregister and re-register
249
+ // Note that `.update` doesn't seem to work desktop electron at the moment so we use
250
+ // `unregister` and `register` here.
251
+ return registration.unregister()
252
+ .then(() => navigator.serviceWorker.register(swPath))
253
+ .finally(() => { resolve(); });
254
+ }
255
+ };
256
+ navigator.serviceWorker.addEventListener('message', versionHandler);
257
+
258
+ const postVersionMessage = (/** @type {ServiceWorker} */ controller) => {
259
+ controller.postMessage({ channel: 'version' });
260
+ };
261
+
262
+ // At this point, either the service worker is ready and
263
+ // became our controller, or we need to wait for it.
264
+ // Note that navigator.serviceWorker.controller could be a
265
+ // controller from a previously loaded service worker.
266
+ const currentController = navigator.serviceWorker.controller;
267
+ if (currentController?.scriptURL.endsWith(swPath)) {
268
+ // service worker already loaded & ready to receive messages
269
+ postVersionMessage(currentController);
270
+ } else {
271
+ if (currentController) {
272
+ console.log(`Found unexpected service worker controller. Found: ${currentController.scriptURL}. Expected: ${swPath}. Waiting for controllerchange.`);
273
+ } else {
274
+ console.log(`No service worker controller found. Waiting for controllerchange.`);
275
+ }
276
+
277
+ // Either there's no controlling service worker, or it's an old one.
278
+ // Wait for it to change before posting the message
279
+ const onControllerChange = () => {
280
+ navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
281
+ if (navigator.serviceWorker.controller) {
282
+ postVersionMessage(navigator.serviceWorker.controller);
283
+ } else {
284
+ return reject(new Error('No controller found.'));
285
+ }
286
+ };
287
+ navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
288
+ }
289
+ }).catch(error => {
290
+ if (!onElectron && error instanceof Error && error.message.includes('user denied permission')) {
291
+ return reject(new Error(`Could not register service worker. Please make sure third party cookies are enabled: ${error}`));
292
+ }
293
+ return reject(new Error(`Could not register service worker: ${error}.`));
294
+ });
295
+ });
296
+
297
+ /**
298
+ * @type {import('../webviewMessages').WebviewHostMessaging}
299
+ */
300
+ const hostMessaging = new class HostMessaging {
301
+
302
+ constructor() {
303
+ this.channel = new MessageChannel();
304
+
305
+ /** @type {Map<string, Array<(event: MessageEvent, data: any) => void>>} */
306
+ this.handlers = new Map();
307
+
308
+ this.channel.port1.onmessage = (e) => {
309
+ const channel = e.data.channel;
310
+ const handlers = this.handlers.get(channel);
311
+ if (handlers) {
312
+ for (const handler of handlers) {
313
+ handler(e, e.data.args);
314
+ }
315
+ } else {
316
+ console.log('no handler for ', e);
317
+ }
318
+ };
319
+ }
320
+
321
+ postMessage(channel, data, transfer) {
322
+ this.channel.port1.postMessage({ channel, data }, transfer);
323
+ }
324
+
325
+ onMessage(channel, handler) {
326
+ let handlers = this.handlers.get(channel);
327
+ if (!handlers) {
328
+ handlers = [];
329
+ this.handlers.set(channel, handlers);
330
+ }
331
+ handlers.push(handler);
332
+ }
333
+
334
+ async signalReady() {
335
+ const start = (/** @type {string} */ parentOrigin) => {
336
+ window.parent.postMessage({ target: ID, channel: 'webview-ready', data: {} }, parentOrigin, [this.channel.port2]);
337
+ };
338
+
339
+ const parentOrigin = searchParams.get('parentOrigin');
340
+
341
+ return start(parentOrigin);
342
+ }
343
+ }();
344
+
345
+ const unloadMonitor = new class {
346
+
347
+ constructor() {
348
+ this.confirmBeforeClose = 'keyboardOnly';
349
+ this.isModifierKeyDown = false;
350
+
351
+ hostMessaging.onMessage('set-confirm-before-close', (_e, data) => {
352
+ this.confirmBeforeClose = data;
353
+ });
354
+
355
+ hostMessaging.onMessage('content', (_e, data) => {
356
+ this.confirmBeforeClose = data.confirmBeforeClose;
357
+ });
358
+
359
+ window.addEventListener('beforeunload', (event) => {
360
+ if (onElectron) {
361
+ return;
362
+ }
363
+
364
+ switch (this.confirmBeforeClose) {
365
+ case 'always': {
366
+ event.preventDefault();
367
+ event.returnValue = '';
368
+ return '';
369
+ }
370
+ case 'never': {
371
+ break;
372
+ }
373
+ case 'keyboardOnly':
374
+ default: {
375
+ if (this.isModifierKeyDown) {
376
+ event.preventDefault();
377
+ event.returnValue = '';
378
+ return '';
379
+ }
380
+ break;
381
+ }
382
+ }
383
+ });
384
+ }
385
+
386
+ onIframeLoaded(/** @type {HTMLIFrameElement} */ frame) {
387
+ assertIsDefined(frame.contentWindow).addEventListener('keydown', e => {
388
+ this.isModifierKeyDown = e.metaKey || e.ctrlKey || e.altKey;
389
+ });
390
+
391
+ assertIsDefined(frame.contentWindow).addEventListener('keyup', () => {
392
+ this.isModifierKeyDown = false;
393
+ });
394
+ }
395
+ };
396
+
397
+ // state
398
+ let firstLoad = true;
399
+ /** @type {any} */
400
+ let loadTimeout;
401
+ let styleVersion = 0;
402
+
403
+ /** @type {Array<{ readonly message: any, transfer?: ArrayBuffer[] }>} */
404
+ let pendingMessages = [];
405
+
406
+ const initData = {
407
+ /** @type {number | undefined} */
408
+ initialScrollProgress: undefined,
409
+
410
+ /** @type {{ [key: string]: string } | undefined} */
411
+ styles: undefined,
412
+
413
+ /** @type {string | undefined} */
414
+ activeTheme: undefined,
415
+
416
+ /** @type {string | undefined} */
417
+ themeId: undefined,
418
+
419
+ /** @type {string | undefined} */
420
+ themeLabel: undefined,
421
+
422
+ /** @type {boolean} */
423
+ screenReader: false,
424
+
425
+ /** @type {boolean} */
426
+ reduceMotion: false,
427
+ };
428
+
429
+ if (!disableServiceWorker) {
430
+ hostMessaging.onMessage('did-load-resource', (_event, data) => {
431
+ assertIsDefined(navigator.serviceWorker.controller).postMessage({ channel: 'did-load-resource', data }, data.data?.buffer ? [data.data.buffer] : []);
432
+ });
433
+
434
+ hostMessaging.onMessage('did-load-localhost', (_event, data) => {
435
+ assertIsDefined(navigator.serviceWorker.controller).postMessage({ channel: 'did-load-localhost', data });
436
+ });
437
+
438
+ navigator.serviceWorker.addEventListener('message', event => {
439
+ switch (event.data.channel) {
440
+ case 'load-resource':
441
+ case 'load-localhost':
442
+ hostMessaging.postMessage(event.data.channel, event.data);
443
+ return;
444
+ }
445
+ });
446
+ }
447
+
448
+ /**
449
+ * @param {HTMLDocument?} document
450
+ * @param {HTMLElement?} body
451
+ */
452
+ const applyStyles = (document, body) => {
453
+ if (!document) {
454
+ return;
455
+ }
456
+
457
+ if (body) {
458
+ body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast', 'vscode-high-contrast-light', 'vscode-reduce-motion', 'vscode-using-screen-reader');
459
+
460
+ if (initData.activeTheme) {
461
+ body.classList.add(initData.activeTheme);
462
+ if (initData.activeTheme === 'vscode-high-contrast-light') {
463
+ // backwards compatibility
464
+ body.classList.add('vscode-high-contrast');
465
+ }
466
+ }
467
+
468
+ if (initData.reduceMotion) {
469
+ body.classList.add('vscode-reduce-motion');
470
+ }
471
+
472
+ if (initData.screenReader) {
473
+ body.classList.add('vscode-using-screen-reader');
474
+ }
475
+
476
+ body.dataset.vscodeThemeKind = initData.activeTheme;
477
+ /** @deprecated data-vscode-theme-name will be removed, use data-vscode-theme-id instead */
478
+ body.dataset.vscodeThemeName = initData.themeLabel || '';
479
+ body.dataset.vscodeThemeId = initData.themeId || '';
480
+ }
481
+
482
+ if (initData.styles) {
483
+ const documentStyle = document.documentElement.style;
484
+
485
+ // Remove stale properties
486
+ for (let i = documentStyle.length - 1; i >= 0; i--) {
487
+ const property = documentStyle[i];
488
+
489
+ // Don't remove properties that the webview might have added separately
490
+ if (property && property.startsWith('--vscode-')) {
491
+ documentStyle.removeProperty(property);
492
+ }
493
+ }
494
+
495
+ // Re-add new properties
496
+ for (const [variable, value] of Object.entries(initData.styles)) {
497
+ documentStyle.setProperty(`--${variable}`, value);
498
+ }
499
+ }
500
+ };
501
+
502
+ /**
503
+ * @param {MouseEvent} event
504
+ */
505
+ const handleInnerClick = (event) => {
506
+ if (!event?.view?.document) {
507
+ return;
508
+ }
509
+
510
+ const baseElement = event.view.document.querySelector('base');
511
+
512
+ for (const pathElement of event.composedPath()) {
513
+ /** @type {any} */
514
+ const node = pathElement;
515
+ if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) {
516
+ if (node.getAttribute('href') === '#') {
517
+ event.view.scrollTo(0, 0);
518
+ } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href === baseElement.href + node.hash))) {
519
+ const fragment = node.hash.slice(1);
520
+ const decodedFragment = decodeURIComponent(fragment);
521
+ const scrollTarget = event.view.document.getElementById(fragment) ?? event.view.document.getElementById(decodedFragment);
522
+ if (scrollTarget) {
523
+ scrollTarget.scrollIntoView();
524
+ } else if (decodedFragment.toLowerCase() === 'top') {
525
+ event.view.scrollTo(0, 0);
526
+ }
527
+ } else {
528
+ hostMessaging.postMessage('did-click-link', { uri: node.href.baseVal || node.href });
529
+ }
530
+ event.preventDefault();
531
+ return;
532
+ }
533
+ }
534
+ };
535
+
536
+ /**
537
+ * @param {MouseEvent} event
538
+ */
539
+ const handleAuxClick = (event) => {
540
+ // Prevent middle clicks opening a broken link in the browser
541
+ if (!event?.view?.document) {
542
+ return;
543
+ }
544
+
545
+ if (event.button === 1) {
546
+ for (const pathElement of event.composedPath()) {
547
+ /** @type {any} */
548
+ const node = pathElement;
549
+ if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) {
550
+ event.preventDefault();
551
+ return;
552
+ }
553
+ }
554
+ }
555
+ };
556
+
557
+ /**
558
+ * @param {KeyboardEvent} e
559
+ */
560
+ const handleInnerKeydown = (e) => {
561
+ // If the keypress would trigger a browser event, such as copy or paste,
562
+ // make sure we block the browser from dispatching it. Instead VS Code
563
+ // handles these events and will dispatch a copy/paste back to the webview
564
+ // if needed
565
+ if (isUndoRedo(e) || isPrint(e) || isFindEvent(e) || isSaveEvent(e)) {
566
+ e.preventDefault();
567
+ } else if (isCopyPasteOrCut(e)) {
568
+ if (onElectron) {
569
+ e.preventDefault();
570
+ } else {
571
+ return; // let the browser handle this
572
+ }
573
+ } else if (!onElectron && (isCloseTab(e) || isNewWindow(e))) {
574
+ // Prevent Ctrl+W closing window / Ctrl+N opening new window in PWA.
575
+ // (No effect in a regular browser tab.)
576
+ e.preventDefault();
577
+ }
578
+
579
+ hostMessaging.postMessage('did-keydown', {
580
+ key: e.key,
581
+ keyCode: e.keyCode,
582
+ code: e.code,
583
+ shiftKey: e.shiftKey,
584
+ altKey: e.altKey,
585
+ ctrlKey: e.ctrlKey,
586
+ metaKey: e.metaKey,
587
+ repeat: e.repeat
588
+ });
589
+ };
590
+ /**
591
+ * @param {KeyboardEvent} e
592
+ */
593
+ const handleInnerKeyup = (e) => {
594
+ hostMessaging.postMessage('did-keyup', {
595
+ key: e.key,
596
+ keyCode: e.keyCode,
597
+ code: e.code,
598
+ shiftKey: e.shiftKey,
599
+ altKey: e.altKey,
600
+ ctrlKey: e.ctrlKey,
601
+ metaKey: e.metaKey,
602
+ repeat: e.repeat
603
+ });
604
+ };
605
+
606
+ /**
607
+ * @param {KeyboardEvent} e
608
+ * @return {boolean}
609
+ */
610
+ function isCopyPasteOrCut(e) {
611
+ const hasMeta = e.ctrlKey || e.metaKey;
612
+ // 45: keyCode of "Insert"
613
+ const shiftInsert = e.shiftKey && e.keyCode === 45;
614
+ // 67, 86, 88: keyCode of "C", "V", "X"
615
+ return (hasMeta && [67, 86, 88].includes(e.keyCode)) || shiftInsert;
616
+ }
617
+
618
+ /**
619
+ * @param {KeyboardEvent} e
620
+ * @return {boolean}
621
+ */
622
+ function isUndoRedo(e) {
623
+ const hasMeta = e.ctrlKey || e.metaKey;
624
+ // 90, 89: keyCode of "Z", "Y"
625
+ return hasMeta && [90, 89].includes(e.keyCode);
626
+ }
627
+
628
+ /**
629
+ * @param {KeyboardEvent} e
630
+ * @return {boolean}
631
+ */
632
+ function isPrint(e) {
633
+ const hasMeta = e.ctrlKey || e.metaKey;
634
+ // 80: keyCode of "P"
635
+ return hasMeta && e.keyCode === 80;
636
+ }
637
+
638
+ /**
639
+ * @param {KeyboardEvent} e
640
+ * @return {boolean}
641
+ */
642
+ function isFindEvent(e) {
643
+ const hasMeta = e.ctrlKey || e.metaKey;
644
+ // 70: keyCode of "F"
645
+ return hasMeta && e.keyCode === 70;
646
+ }
647
+
648
+ /**
649
+ * @param {KeyboardEvent} e
650
+ * @return {boolean}
651
+ */
652
+ function isSaveEvent(e) {
653
+ const hasMeta = e.ctrlKey || e.metaKey;
654
+ // 83: keyCode of "S"
655
+ return hasMeta && e.keyCode === 83;
656
+ }
657
+
658
+ /**
659
+ * @param {KeyboardEvent} e
660
+ * @return {boolean}
661
+ */
662
+ function isCloseTab(e) {
663
+ const hasMeta = e.ctrlKey || e.metaKey;
664
+ // 87: keyCode of "W"
665
+ return hasMeta && e.keyCode === 87;
666
+ }
667
+
668
+ /**
669
+ * @param {KeyboardEvent} e
670
+ * @return {boolean}
671
+ */
672
+ function isNewWindow(e) {
673
+ const hasMeta = e.ctrlKey || e.metaKey;
674
+ // 78: keyCode of "N"
675
+ return hasMeta && e.keyCode === 78;
676
+ }
677
+
678
+ let isHandlingScroll = false;
679
+
680
+ /**
681
+ * @param {WheelEvent} event
682
+ */
683
+ const handleWheel = (event) => {
684
+ if (isHandlingScroll) {
685
+ return;
686
+ }
687
+
688
+ hostMessaging.postMessage('did-scroll-wheel', {
689
+ deltaMode: event.deltaMode,
690
+ deltaX: event.deltaX,
691
+ deltaY: event.deltaY,
692
+ deltaZ: event.deltaZ,
693
+ detail: event.detail,
694
+ type: event.type
695
+ });
696
+ };
697
+
698
+ /**
699
+ * @param {Event} event
700
+ */
701
+ const handleInnerScroll = (event) => {
702
+ if (isHandlingScroll) {
703
+ return;
704
+ }
705
+
706
+ const target = /** @type {HTMLDocument | null} */ (event.target);
707
+ const currentTarget = /** @type {Window | null} */ (event.currentTarget);
708
+ if (!currentTarget || !target?.body) {
709
+ return;
710
+ }
711
+
712
+ const progress = currentTarget.scrollY / target.body.clientHeight;
713
+ if (isNaN(progress)) {
714
+ return;
715
+ }
716
+
717
+ isHandlingScroll = true;
718
+ window.requestAnimationFrame(() => {
719
+ try {
720
+ hostMessaging.postMessage('did-scroll', { scrollYPercentage: progress });
721
+ } catch (e) {
722
+ // noop
723
+ }
724
+ isHandlingScroll = false;
725
+ });
726
+ };
727
+
728
+ function handleInnerDragStartEvent(/** @type {DragEvent} */ e) {
729
+ if (e.defaultPrevented) {
730
+ // Extension code has already handled this event
731
+ return;
732
+ }
733
+
734
+ if (!e.dataTransfer || e.shiftKey) {
735
+ return;
736
+ }
737
+
738
+ // Only handle drags from outside editor for now
739
+ if (e.dataTransfer.items.length && Array.prototype.every.call(e.dataTransfer.items, item => item.kind === 'file')) {
740
+ hostMessaging.postMessage('drag-start', undefined);
741
+ }
742
+ }
743
+
744
+ /**
745
+ * @param {() => void} callback
746
+ */
747
+ function onDomReady(callback) {
748
+ if (document.readyState === 'interactive' || document.readyState === 'complete') {
749
+ callback();
750
+ } else {
751
+ document.addEventListener('DOMContentLoaded', callback);
752
+ }
753
+ }
754
+
755
+ function areServiceWorkersEnabled() {
756
+ try {
757
+ return !!navigator.serviceWorker;
758
+ } catch (e) {
759
+ return false;
760
+ }
761
+ }
762
+
763
+ /**
764
+ * @param {import('../webviewMessages').UpdateContentEvent} data
765
+ * @return {string}
766
+ */
767
+ function toContentHtml(data) {
768
+ const options = data.options;
769
+ const text = data.contents;
770
+ const newDocument = new DOMParser().parseFromString(text, 'text/html');
771
+
772
+ newDocument.querySelectorAll('a').forEach(a => {
773
+ if (!a.title) {
774
+ const href = a.getAttribute('href');
775
+ if (typeof href === 'string') {
776
+ a.title = href;
777
+ }
778
+ }
779
+ });
780
+
781
+ // Set default aria role
782
+ if (!newDocument.body.hasAttribute('role')) {
783
+ newDocument.body.setAttribute('role', 'document');
784
+ }
785
+
786
+ // Inject default script
787
+ if (options.allowScripts) {
788
+ const defaultScript = newDocument.createElement('script');
789
+ defaultScript.id = '_vscodeApiScript';
790
+ defaultScript.textContent = getVsCodeApiScript(options.allowMultipleAPIAcquire, data.state);
791
+ newDocument.head.prepend(defaultScript);
792
+ }
793
+
794
+ // Inject default styles
795
+ newDocument.head.prepend(defaultStyles.cloneNode(true));
796
+
797
+ applyStyles(newDocument, newDocument.body);
798
+
799
+ // Strip out unsupported http-equiv tags
800
+ for (const metaElement of Array.from(newDocument.querySelectorAll('meta'))) {
801
+ const httpEquiv = metaElement.getAttribute('http-equiv');
802
+ if (httpEquiv && !/^(content-security-policy|default-style|content-type)$/i.test(httpEquiv)) {
803
+ console.warn(`Removing unsupported meta http-equiv: ${httpEquiv}`);
804
+ metaElement.remove();
805
+ }
806
+ }
807
+
808
+ // Check for CSP
809
+ const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]');
810
+ if (!csp) {
811
+ hostMessaging.postMessage('no-csp-found', undefined);
812
+ } else {
813
+ try {
814
+ // Attempt to rewrite CSPs that hardcode old-style resource endpoint
815
+ const cspContent = csp.getAttribute('content');
816
+ if (cspContent) {
817
+ const newCsp = cspContent.replace(/(vscode-webview-resource|vscode-resource):(?=(\s|;|$))/g, data.cspSource);
818
+ csp.setAttribute('content', newCsp);
819
+ }
820
+ } catch (e) {
821
+ console.error(`Could not rewrite csp: ${e}`);
822
+ }
823
+ }
824
+
825
+ // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off
826
+ // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden
827
+ return '<!DOCTYPE html>\n' + newDocument.documentElement.outerHTML;
828
+ }
829
+
830
+ // Also forward events before the contents of the webview have loaded
831
+ window.addEventListener('keydown', handleInnerKeydown);
832
+ window.addEventListener('keyup', handleInnerKeyup);
833
+ window.addEventListener('dragenter', handleInnerDragStartEvent);
834
+ window.addEventListener('dragover', handleInnerDragStartEvent);
835
+
836
+ onDomReady(() => {
837
+ if (!document.body) {
838
+ return;
839
+ }
840
+
841
+ hostMessaging.onMessage('styles', (_event, data) => {
842
+ ++styleVersion;
843
+
844
+ initData.styles = data.styles;
845
+ initData.activeTheme = data.activeTheme;
846
+ initData.themeLabel = data.themeLabel;
847
+ initData.themeId = data.themeId;
848
+ initData.reduceMotion = data.reduceMotion;
849
+ initData.screenReader = data.screenReader;
850
+
851
+ const target = getActiveFrame();
852
+ if (!target) {
853
+ return;
854
+ }
855
+
856
+ if (target.contentDocument) {
857
+ applyStyles(target.contentDocument, target.contentDocument.body);
858
+ }
859
+ });
860
+
861
+ // propagate focus
862
+ hostMessaging.onMessage('focus', () => {
863
+ const activeFrame = getActiveFrame();
864
+ if (!activeFrame || !activeFrame.contentWindow) {
865
+ // Focus the top level webview instead
866
+ window.focus();
867
+ return;
868
+ }
869
+
870
+ if (document.activeElement === activeFrame) {
871
+ // We are already focused on the iframe (or one of its children) so no need
872
+ // to refocus.
873
+ return;
874
+ }
875
+
876
+ activeFrame.contentWindow.focus();
877
+ });
878
+
879
+ // update iframe-contents
880
+ let updateId = 0;
881
+ hostMessaging.onMessage('content', async (_event, /** @type {import('../webviewMessages').UpdateContentEvent} */ data) => {
882
+ const currentUpdateId = ++updateId;
883
+ try {
884
+ await workerReady;
885
+ } catch (e) {
886
+ console.error(`Webview fatal error: ${e}`);
887
+ hostMessaging.postMessage('fatal-error', { message: e + '' });
888
+ return;
889
+ }
890
+
891
+ if (currentUpdateId !== updateId) {
892
+ return;
893
+ }
894
+
895
+ const options = data.options;
896
+ const newDocument = toContentHtml(data);
897
+
898
+ const initialStyleVersion = styleVersion;
899
+
900
+ const frame = getActiveFrame();
901
+ const wasFirstLoad = firstLoad;
902
+ // keep current scrollY around and use later
903
+ /** @type {(body: HTMLElement, window: Window) => void} */
904
+ let setInitialScrollPosition;
905
+ if (firstLoad) {
906
+ firstLoad = false;
907
+ setInitialScrollPosition = (body, window) => {
908
+ if (typeof initData.initialScrollProgress === 'number' && !isNaN(initData.initialScrollProgress)) {
909
+ if (window.scrollY === 0) {
910
+ window.scroll(0, body.clientHeight * initData.initialScrollProgress);
911
+ }
912
+ }
913
+ };
914
+ } else {
915
+ const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? assertIsDefined(frame.contentWindow).scrollY : 0;
916
+ setInitialScrollPosition = (body, window) => {
917
+ if (window.scrollY === 0) {
918
+ window.scroll(0, scrollY);
919
+ }
920
+ };
921
+ }
922
+
923
+ // Clean up old pending frames and set current one as new one
924
+ const previousPendingFrame = getPendingFrame();
925
+ if (previousPendingFrame) {
926
+ previousPendingFrame.setAttribute('id', '');
927
+ document.body.removeChild(previousPendingFrame);
928
+ }
929
+ if (!wasFirstLoad) {
930
+ pendingMessages = [];
931
+ }
932
+
933
+ const newFrame = document.createElement('iframe');
934
+ newFrame.title = data.title;
935
+ newFrame.setAttribute('id', 'pending-frame');
936
+ newFrame.setAttribute('frameborder', '0');
937
+
938
+ const sandboxRules = new Set(['allow-same-origin', 'allow-pointer-lock']);
939
+ if (options.allowScripts) {
940
+ sandboxRules.add('allow-scripts');
941
+ sandboxRules.add('allow-downloads');
942
+ }
943
+ if (options.allowForms) {
944
+ sandboxRules.add('allow-forms');
945
+ }
946
+ newFrame.setAttribute('sandbox', Array.from(sandboxRules).join(' '));
947
+
948
+ const allowRules = ['cross-origin-isolated;', 'autoplay;'];
949
+ if (!isFirefox && options.allowScripts) {
950
+ allowRules.push('clipboard-read;', 'clipboard-write;');
951
+ }
952
+ newFrame.setAttribute('allow', allowRules.join(' '));
953
+ // We should just be able to use srcdoc, but I wasn't
954
+ // seeing the service worker applying properly.
955
+ // Fake load an empty on the correct origin and then write real html
956
+ // into it to get around this.
957
+ const fakeUrlParams = new URLSearchParams({ id: ID });
958
+ if (globalThis.crossOriginIsolated) {
959
+ fakeUrlParams.set('vscode-coi', '3'); /*COOP+COEP*/
960
+ }
961
+ newFrame.src = `${fakeHtmlUri}?${fakeUrlParams.toString()}`;
962
+
963
+ newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden';
964
+ document.body.appendChild(newFrame);
965
+
966
+ newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown);
967
+ newFrame.contentWindow.addEventListener('keyup', handleInnerKeyup);
968
+
969
+ /**
970
+ * @param {Document} contentDocument
971
+ */
972
+ function onFrameLoaded(contentDocument) {
973
+ // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=978325
974
+ setTimeout(() => {
975
+ contentDocument.open();
976
+ contentDocument.write(newDocument);
977
+ contentDocument.close();
978
+ hookupOnLoadHandlers(newFrame);
979
+
980
+ if (initialStyleVersion !== styleVersion) {
981
+ applyStyles(contentDocument, contentDocument.body);
982
+ }
983
+ }, 0);
984
+ }
985
+
986
+ if (!options.allowScripts && isSafari) {
987
+ // On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired: https://bugs.webkit.org/show_bug.cgi?id=33604
988
+ // Use polling instead.
989
+ const interval = setInterval(() => {
990
+ // If the frame is no longer mounted, loading has stopped
991
+ if (!newFrame.parentElement) {
992
+ clearInterval(interval);
993
+ return;
994
+ }
995
+
996
+ const contentDocument = assertIsDefined(newFrame.contentDocument);
997
+ if (contentDocument.location.pathname.endsWith('/fake.html') && contentDocument.readyState !== 'loading') {
998
+ clearInterval(interval);
999
+ onFrameLoaded(contentDocument);
1000
+ }
1001
+ }, 10);
1002
+ } else {
1003
+ assertIsDefined(newFrame.contentWindow).addEventListener('DOMContentLoaded', e => {
1004
+ const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined;
1005
+ onFrameLoaded(assertIsDefined(contentDocument));
1006
+ });
1007
+ }
1008
+
1009
+ /**
1010
+ * @param {Document} contentDocument
1011
+ * @param {Window} contentWindow
1012
+ */
1013
+ const onLoad = (contentDocument, contentWindow) => {
1014
+ if (contentDocument && contentDocument.body) {
1015
+ // Workaround for https://github.com/microsoft/vscode/issues/12865
1016
+ // check new scrollY and reset if necessary
1017
+ setInitialScrollPosition(contentDocument.body, contentWindow);
1018
+ }
1019
+
1020
+ const newFrame = getPendingFrame();
1021
+ if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) {
1022
+ const wasFocused = document.hasFocus();
1023
+ const oldActiveFrame = getActiveFrame();
1024
+ if (oldActiveFrame) {
1025
+ document.body.removeChild(oldActiveFrame);
1026
+ }
1027
+ // Styles may have changed since we created the element. Make sure we re-style
1028
+ if (initialStyleVersion !== styleVersion) {
1029
+ applyStyles(newFrame.contentDocument, newFrame.contentDocument.body);
1030
+ }
1031
+ newFrame.setAttribute('id', 'active-frame');
1032
+ newFrame.style.visibility = 'visible';
1033
+
1034
+ contentWindow.addEventListener('scroll', handleInnerScroll);
1035
+ contentWindow.addEventListener('wheel', handleWheel);
1036
+
1037
+ if (wasFocused) {
1038
+ contentWindow.focus();
1039
+ }
1040
+
1041
+ pendingMessages.forEach((message) => {
1042
+ contentWindow.postMessage(message.message, window.origin, message.transfer);
1043
+ });
1044
+ pendingMessages = [];
1045
+ }
1046
+ };
1047
+
1048
+ /**
1049
+ * @param {HTMLIFrameElement} newFrame
1050
+ */
1051
+ function hookupOnLoadHandlers(newFrame) {
1052
+ clearTimeout(loadTimeout);
1053
+ loadTimeout = undefined;
1054
+ loadTimeout = setTimeout(() => {
1055
+ clearTimeout(loadTimeout);
1056
+ loadTimeout = undefined;
1057
+ onLoad(assertIsDefined(newFrame.contentDocument), assertIsDefined(newFrame.contentWindow));
1058
+ }, 200);
1059
+
1060
+ const contentWindow = assertIsDefined(newFrame.contentWindow);
1061
+
1062
+ contentWindow.addEventListener('load', function (e) {
1063
+ const contentDocument = /** @type {Document} */ (e.target);
1064
+
1065
+ if (loadTimeout) {
1066
+ clearTimeout(loadTimeout);
1067
+ loadTimeout = undefined;
1068
+ onLoad(contentDocument, this);
1069
+ }
1070
+ });
1071
+
1072
+ // Bubble out various events
1073
+ contentWindow.addEventListener('click', handleInnerClick);
1074
+ contentWindow.addEventListener('auxclick', handleAuxClick);
1075
+ contentWindow.addEventListener('keydown', handleInnerKeydown);
1076
+ contentWindow.addEventListener('keyup', handleInnerKeyup);
1077
+ contentWindow.addEventListener('contextmenu', e => {
1078
+ if (e.defaultPrevented) {
1079
+ // Extension code has already handled this event
1080
+ return;
1081
+ }
1082
+
1083
+ e.preventDefault();
1084
+
1085
+ /** @type { Record<string, boolean>} */
1086
+ let context = {};
1087
+
1088
+ /** @type {HTMLElement | null} */
1089
+ let el = e.target;
1090
+ while (true) {
1091
+ if (!el) {
1092
+ break;
1093
+ }
1094
+
1095
+ // Search self/ancestors for the closest context data attribute
1096
+ el = el.closest('[data-vscode-context]');
1097
+ if (!el) {
1098
+ break;
1099
+ }
1100
+
1101
+ try {
1102
+ context = { ...JSON.parse(el.dataset.vscodeContext), ...context };
1103
+ } catch (e) {
1104
+ console.error(`Error parsing 'data-vscode-context' as json`, el, e);
1105
+ }
1106
+
1107
+ el = el.parentElement;
1108
+ }
1109
+
1110
+ hostMessaging.postMessage('did-context-menu', {
1111
+ clientX: e.clientX,
1112
+ clientY: e.clientY,
1113
+ context: context
1114
+ });
1115
+ });
1116
+
1117
+ contentWindow.addEventListener('dragenter', handleInnerDragStartEvent);
1118
+ contentWindow.addEventListener('dragover', handleInnerDragStartEvent);
1119
+
1120
+ unloadMonitor.onIframeLoaded(newFrame);
1121
+ }
1122
+ });
1123
+
1124
+ // propagate vscode-context-menu-visible class
1125
+ hostMessaging.onMessage('set-context-menu-visible', (_event, data) => {
1126
+ const target = getActiveFrame();
1127
+ if (target && target.contentDocument) {
1128
+ target.contentDocument.body.classList.toggle('vscode-context-menu-visible', data.visible);
1129
+ }
1130
+ });
1131
+
1132
+ hostMessaging.onMessage('set-title', async (_event, data) => {
1133
+ const target = getActiveFrame();
1134
+ if (target) {
1135
+ target.title = data;
1136
+ }
1137
+ });
1138
+
1139
+ // Forward message to the embedded iframe
1140
+ hostMessaging.onMessage('message', (_event, data) => {
1141
+ const pending = getPendingFrame();
1142
+ if (!pending) {
1143
+ const target = getActiveFrame();
1144
+ if (target) {
1145
+ assertIsDefined(target.contentWindow).postMessage(data.message, window.origin, data.transfer);
1146
+ return;
1147
+ }
1148
+ }
1149
+ pendingMessages.push(data);
1150
+ });
1151
+
1152
+ hostMessaging.onMessage('initial-scroll-position', (_event, progress) => {
1153
+ initData.initialScrollProgress = progress;
1154
+ });
1155
+
1156
+ hostMessaging.onMessage('execCommand', (_event, data) => {
1157
+ const target = getActiveFrame();
1158
+ if (!target) {
1159
+ return;
1160
+ }
1161
+ assertIsDefined(target.contentDocument).execCommand(data);
1162
+ });
1163
+
1164
+ /** @type {string | undefined} */
1165
+ let lastFindValue = undefined;
1166
+
1167
+ hostMessaging.onMessage('find', (_event, data) => {
1168
+ const target = getActiveFrame();
1169
+ if (!target) {
1170
+ return;
1171
+ }
1172
+
1173
+ if (!data.previous && lastFindValue !== data.value && target.contentWindow) {
1174
+ // Reset selection so we start search at the head of the last search
1175
+ const selection = target.contentWindow.getSelection();
1176
+ if (selection) {
1177
+ selection.collapse(selection.anchorNode);
1178
+ }
1179
+ }
1180
+ lastFindValue = data.value;
1181
+
1182
+ const didFind = (/** @type {any} */ (target.contentWindow)).find(
1183
+ data.value,
1184
+ /* caseSensitive*/ false,
1185
+ /* backwards*/ data.previous,
1186
+ /* wrapAround*/ true,
1187
+ /* wholeWord */ false,
1188
+ /* searchInFrames*/ false,
1189
+ false);
1190
+ hostMessaging.postMessage('did-find', didFind);
1191
+ });
1192
+
1193
+ hostMessaging.onMessage('find-stop', (_event, data) => {
1194
+ const target = getActiveFrame();
1195
+ if (!target) {
1196
+ return;
1197
+ }
1198
+
1199
+ lastFindValue = undefined;
1200
+
1201
+ if (!data.clearSelection && target.contentWindow) {
1202
+ const selection = target.contentWindow.getSelection();
1203
+ if (selection) {
1204
+ for (let i = 0; i < selection.rangeCount; i++) {
1205
+ selection.removeRange(selection.getRangeAt(i));
1206
+ }
1207
+ }
1208
+ }
1209
+ });
1210
+
1211
+ trackFocus({
1212
+ onFocus: () => hostMessaging.postMessage('did-focus', undefined),
1213
+ onBlur: () => hostMessaging.postMessage('did-blur', undefined)
1214
+ });
1215
+
1216
+ (/** @type {any} */ (window))[vscodePostMessageFuncName] = (/** @type {string} */ command, /** @type {any} */ data) => {
1217
+ switch (command) {
1218
+ case 'onmessage':
1219
+ case 'do-update-state':
1220
+ hostMessaging.postMessage(command, data);
1221
+ break;
1222
+ }
1223
+ };
1224
+
1225
+ hostMessaging.signalReady();
1226
+ });
1227
+ </script>
1228
+ </body>
1229
+
1230
+ </html>