Compare commits
1 commit
main
...
joao/web-w
Author | SHA1 | Date | |
---|---|---|---|
d95ed670c1 |
|
@ -29,7 +29,7 @@ import { generateUuid } from 'vs/base/common/uuid';
|
|||
import { canceled, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { NewWorkerMessage, TerminateWorkerMessage } from 'vs/workbench/services/extensions/common/polyfillNestedWorker.protocol';
|
||||
import { WorkerMessage, WorkerMessageType } from 'vs/workbench/services/extensions/common/polyfillNestedWorker.protocol';
|
||||
|
||||
export interface IWebWorkerExtensionHostInitData {
|
||||
readonly autoStart: boolean;
|
||||
|
@ -149,7 +149,8 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
}
|
||||
|
||||
private async _startInsideIframe(webWorkerExtensionHostIframeSrc: string): Promise<IMessagePassingProtocol> {
|
||||
const emitter = this._register(new Emitter<VSBuffer>());
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const barrier = new Barrier();
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('class', 'web-worker-ext-host-iframe');
|
||||
|
@ -159,8 +160,10 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
const vscodeWebWorkerExtHostId = generateUuid();
|
||||
iframe.setAttribute('src', `${webWorkerExtensionHostIframeSrc}&vscodeWebWorkerExtHostId=${vscodeWebWorkerExtHostId}`);
|
||||
|
||||
const barrier = new Barrier();
|
||||
let port!: MessagePort;
|
||||
const iframeLoaded = new Promise(c => iframe.onload = c);
|
||||
this._layoutService.container.appendChild(iframe);
|
||||
this._register(toDisposable(() => iframe.remove()));
|
||||
|
||||
let barrierError: Error | null = null;
|
||||
let barrierHasError = false;
|
||||
let startTimeout: any = null;
|
||||
|
@ -174,8 +177,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
barrier.open();
|
||||
};
|
||||
|
||||
const resolveBarrier = (messagePort: MessagePort) => {
|
||||
port = messagePort;
|
||||
const resolveBarrier = () => {
|
||||
clearTimeout(startTimeout);
|
||||
barrier.open();
|
||||
};
|
||||
|
@ -188,9 +190,11 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
if (event.source !== iframe.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.vscodeWebWorkerExtHostId !== vscodeWebWorkerExtHostId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.error) {
|
||||
const { name, message, stack } = event.data.error;
|
||||
const err = new Error();
|
||||
|
@ -199,27 +203,33 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
err.stack = stack;
|
||||
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
|
||||
}
|
||||
const { data } = event.data;
|
||||
if (barrier.isOpen() || !(data instanceof MessagePort)) {
|
||||
console.warn('UNEXPECTED message', event);
|
||||
const err = new Error('UNEXPECTED message');
|
||||
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
|
||||
|
||||
if (barrier.isOpen()) {
|
||||
console.warn('UNEXPECTED message after worker ready', event);
|
||||
return;
|
||||
}
|
||||
resolveBarrier(data);
|
||||
|
||||
if (event.data.data.type === WorkerMessageType.Ready) {
|
||||
resolveBarrier();
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn('UNEXPECTED message', event);
|
||||
const err = new Error('UNEXPECTED message');
|
||||
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
|
||||
}));
|
||||
|
||||
this._layoutService.container.appendChild(iframe);
|
||||
this._register(toDisposable(() => iframe.remove()));
|
||||
|
||||
// await MessagePort and use it to directly communicate
|
||||
// with the worker extension host
|
||||
await iframeLoaded;
|
||||
iframe.contentWindow!.postMessage(port2, '*', [port2]);
|
||||
await barrier.wait();
|
||||
|
||||
if (barrierHasError) {
|
||||
throw barrierError;
|
||||
}
|
||||
|
||||
port.onmessage = (event) => {
|
||||
const emitter = this._register(new Emitter<VSBuffer>());
|
||||
|
||||
port1.onmessage = (event) => {
|
||||
const { data } = event;
|
||||
if (!(data instanceof ArrayBuffer)) {
|
||||
console.warn('UNKNOWN data received', data);
|
||||
|
@ -233,7 +243,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
onMessage: emitter.event,
|
||||
send: vsbuf => {
|
||||
const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength);
|
||||
port.postMessage(data, [data]);
|
||||
port1.postMessage(data, [data]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -241,37 +251,25 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
}
|
||||
|
||||
private async _startOutsideIframe(): Promise<IMessagePassingProtocol> {
|
||||
const emitter = new Emitter<VSBuffer>();
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const barrier = new Barrier();
|
||||
let port!: MessagePort;
|
||||
|
||||
const nestedWorker = new Map<string, Worker>();
|
||||
|
||||
const name = this._environmentService.debugRenderer && this._environmentService.debugExtensionHost ? 'DebugWorkerExtensionHost' : 'WorkerExtensionHost';
|
||||
const worker = new DefaultWorkerFactory(name).create(
|
||||
const worker = this._register(new DefaultWorkerFactory(name).create(
|
||||
'vs/workbench/services/extensions/worker/extensionHostWorker',
|
||||
(data: MessagePort | NewWorkerMessage | TerminateWorkerMessage | any) => {
|
||||
|
||||
if (data instanceof MessagePort) {
|
||||
// receiving a message port which is used to communicate
|
||||
// with the web worker extension host
|
||||
if (barrier.isOpen()) {
|
||||
console.warn('UNEXPECTED message', data);
|
||||
this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, 'received a message port AFTER opening the barrier']);
|
||||
return;
|
||||
}
|
||||
port = data;
|
||||
(data: WorkerMessage | any /* this `any` is a hack */) => {
|
||||
if (data?.type === WorkerMessageType.Ready) {
|
||||
barrier.open();
|
||||
|
||||
|
||||
} else if (data?.type === '_newWorker') {
|
||||
} else if (data?.type === WorkerMessageType.NewWorker) {
|
||||
// receiving a message to create a new nested/child worker
|
||||
const worker = new Worker((ttPolicyNestedWorker?.createScriptURL(data.url) ?? data.url) as string, data.options);
|
||||
worker.postMessage(data.port, [data.port]);
|
||||
worker.onerror = console.error.bind(console);
|
||||
nestedWorker.set(data.id, worker);
|
||||
|
||||
} else if (data?.type === '_terminateWorker') {
|
||||
} else if (data?.type === WorkerMessageType.TerminateWorker) {
|
||||
// receiving a message to terminate nested/child worker
|
||||
if (nestedWorker.has(data.id)) {
|
||||
nestedWorker.get(data.id)!.terminate();
|
||||
|
@ -293,14 +291,14 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, event.message || event.error]);
|
||||
}
|
||||
}
|
||||
);
|
||||
));
|
||||
|
||||
// await MessagePort and use it to directly communicate
|
||||
// with the worker extension host
|
||||
worker.postMessage(port2 as any /* this `any` is a hack */, [port2 as any /* this `any` is a hack */]);
|
||||
await barrier.wait();
|
||||
|
||||
port.onmessage = (event) => {
|
||||
const { data } = event;
|
||||
const emitter = this._register(new Emitter<VSBuffer>());
|
||||
|
||||
port1.onmessage = ({ data }) => {
|
||||
if (!(data instanceof ArrayBuffer)) {
|
||||
console.warn('UNKNOWN data received', data);
|
||||
this._onDidExit.fire([77, 'UNKNOWN data received']);
|
||||
|
@ -310,16 +308,11 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength)));
|
||||
};
|
||||
|
||||
|
||||
// keep for cleanup
|
||||
this._register(emitter);
|
||||
this._register(worker);
|
||||
|
||||
const protocol: IMessagePassingProtocol = {
|
||||
onMessage: emitter.event,
|
||||
send: vsbuf => {
|
||||
const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength);
|
||||
port.postMessage(data, [data]);
|
||||
port1.postMessage(data, [data]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,9 +3,18 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const enum WorkerMessageType {
|
||||
Ready = '_workerReady',
|
||||
NewWorker = '_newWorker',
|
||||
TerminateWorker = '_terminateWorker'
|
||||
}
|
||||
|
||||
export interface WorkerReadyMessage {
|
||||
type: WorkerMessageType.Ready;
|
||||
}
|
||||
|
||||
export interface NewWorkerMessage {
|
||||
type: '_newWorker';
|
||||
type: WorkerMessageType.NewWorker;
|
||||
id: string;
|
||||
port: any /* MessagePort */;
|
||||
url: string;
|
||||
|
@ -13,6 +22,8 @@ export interface NewWorkerMessage {
|
|||
}
|
||||
|
||||
export interface TerminateWorkerMessage {
|
||||
type: '_terminateWorker';
|
||||
type: WorkerMessageType.TerminateWorker;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type WorkerMessage = WorkerReadyMessage | NewWorkerMessage | TerminateWorkerMessage;
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'vs/workbench/api/common/extHost.common.services';
|
|||
import 'vs/workbench/api/worker/extHost.worker.services';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WorkerMessageType } from 'vs/workbench/services/extensions/common/polyfillNestedWorker.protocol';
|
||||
|
||||
//#region --- Define, capture, and override some globals
|
||||
|
||||
|
@ -161,16 +162,11 @@ class ExtensionWorker {
|
|||
// protocol
|
||||
readonly protocol: IMessagePassingProtocol;
|
||||
|
||||
constructor() {
|
||||
|
||||
const channel = new MessageChannel();
|
||||
constructor(port: MessagePort) {
|
||||
const emitter = new Emitter<VSBuffer>();
|
||||
let terminating = false;
|
||||
|
||||
// send over port2, keep port1
|
||||
nativePostMessage(channel.port2, [channel.port2]);
|
||||
|
||||
channel.port1.onmessage = event => {
|
||||
port.onmessage = event => {
|
||||
const { data } = event;
|
||||
if (!(data instanceof ArrayBuffer)) {
|
||||
console.warn('UNKNOWN data received', data);
|
||||
|
@ -194,7 +190,7 @@ class ExtensionWorker {
|
|||
send: vsbuf => {
|
||||
if (!terminating) {
|
||||
const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength);
|
||||
channel.port1.postMessage(data, [data]);
|
||||
port.postMessage(data, [data]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -219,18 +215,23 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
|
|||
|
||||
let onTerminate = (reason: string) => nativeClose();
|
||||
|
||||
export function create(): void {
|
||||
const res = new ExtensionWorker();
|
||||
export function create(): { onmessage: (message: any) => void } {
|
||||
performance.mark(`code/extHost/willConnectToRenderer`);
|
||||
connectToRenderer(res.protocol).then(data => {
|
||||
performance.mark(`code/extHost/didWaitForInitData`);
|
||||
const extHostMain = new ExtensionHostMain(
|
||||
data.protocol,
|
||||
data.initData,
|
||||
hostUtil,
|
||||
null,
|
||||
);
|
||||
return {
|
||||
onmessage(port: MessagePort) {
|
||||
const res = new ExtensionWorker(port);
|
||||
nativePostMessage({ type: WorkerMessageType.Ready });
|
||||
connectToRenderer(res.protocol).then(data => {
|
||||
performance.mark(`code/extHost/didWaitForInitData`);
|
||||
const extHostMain = new ExtensionHostMain(
|
||||
data.protocol,
|
||||
data.initData,
|
||||
hostUtil,
|
||||
null,
|
||||
);
|
||||
|
||||
onTerminate = (reason: string) => extHostMain.terminate(reason);
|
||||
});
|
||||
onTerminate = (reason: string) => extHostMain.terminate(reason);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data: blob:; script-src 'unsafe-eval' 'sha256-cb2sg39EJV8ABaSNFfWu/ou8o1xVXYK7jp90oZ9vpcg=' http: https:; connect-src http: https: ws: wss:" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data: blob:; script-src 'unsafe-eval' 'sha256-K1g7FCERTTCfRLLmc8iHekkF33UnX2QZNtYpH4uQo0E=' http: https:; connect-src http: https: ws: wss:" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
@ -47,7 +47,7 @@
|
|||
window.parent.postMessage({
|
||||
vscodeWebWorkerExtHostId,
|
||||
data
|
||||
}, '*', [data]);
|
||||
}, '*');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,8 @@
|
|||
console.error(event.message, event.error);
|
||||
sendError(event.error);
|
||||
};
|
||||
|
||||
window.addEventListener('message', event => worker.postMessage(event.data, event.ports));
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
sendError(err);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data: blob:; script-src 'unsafe-eval' 'sha256-cb2sg39EJV8ABaSNFfWu/ou8o1xVXYK7jp90oZ9vpcg=' https:; connect-src https: wss:" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data: blob:; script-src 'unsafe-eval' 'sha256-K1g7FCERTTCfRLLmc8iHekkF33UnX2QZNtYpH4uQo0E=' https:; connect-src https: wss:" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
@ -47,7 +47,7 @@
|
|||
window.parent.postMessage({
|
||||
vscodeWebWorkerExtHostId,
|
||||
data
|
||||
}, '*', [data]);
|
||||
}, '*');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,8 @@
|
|||
console.error(event.message, event.error);
|
||||
sendError(event.error);
|
||||
};
|
||||
|
||||
window.addEventListener('message', event => worker.postMessage(event.data, event.ports));
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
sendError(err);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NewWorkerMessage, TerminateWorkerMessage } from 'vs/workbench/services/extensions/common/polyfillNestedWorker.protocol';
|
||||
import { NewWorkerMessage, TerminateWorkerMessage, WorkerMessageType } from 'vs/workbench/services/extensions/common/polyfillNestedWorker.protocol';
|
||||
|
||||
declare function postMessage(data: any, transferables?: Transferable[]): void;
|
||||
|
||||
|
@ -75,7 +75,7 @@ export class NestedWorker extends EventTarget implements Worker {
|
|||
const id = blobUrl; // works because blob url is unique, needs ID pool otherwise
|
||||
|
||||
const msg: NewWorkerMessage = {
|
||||
type: '_newWorker',
|
||||
type: WorkerMessageType.NewWorker,
|
||||
id,
|
||||
port: channel.port2,
|
||||
url: blobUrl,
|
||||
|
@ -87,7 +87,7 @@ export class NestedWorker extends EventTarget implements Worker {
|
|||
this.postMessage = channel.port1.postMessage.bind(channel.port1);
|
||||
this.terminate = () => {
|
||||
const msg: TerminateWorkerMessage = {
|
||||
type: '_terminateWorker',
|
||||
type: WorkerMessageType.TerminateWorker,
|
||||
id
|
||||
};
|
||||
channel.port1.postMessage(msg);
|
||||
|
|
Loading…
Reference in a new issue