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