UNPKG

monaco-editor-core

Version:
171 lines • 7.63 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { createTrustedTypesPolicy } from './trustedTypes.js'; import { onUnexpectedError } from '../common/errors.js'; import { COI } from '../common/network.js'; import { URI } from '../common/uri.js'; import { WebWorkerClient } from '../common/worker/webWorker.js'; import { Disposable, toDisposable } from '../common/lifecycle.js'; import { coalesce } from '../common/arrays.js'; import { getNLSLanguage, getNLSMessages } from '../../nls.js'; import { Emitter } from '../common/event.js'; import { getMonacoEnvironment } from './browser.js'; // Reuse the trusted types policy defined from worker bootstrap // when available. // Refs https://github.com/microsoft/vscode/issues/222193 let ttPolicy; // eslint-disable-next-line local/code-no-any-casts if (typeof self === 'object' && self.constructor && self.constructor.name === 'DedicatedWorkerGlobalScope' && globalThis.workerttPolicy !== undefined) { // eslint-disable-next-line local/code-no-any-casts ttPolicy = globalThis.workerttPolicy; } else { ttPolicy = createTrustedTypesPolicy('defaultWorkerFactory', { createScriptURL: value => value }); } function getWorker(descriptor, id) { const label = descriptor.label || 'anonymous' + id; // Option for hosts to overwrite the worker script (used in the standalone editor) const monacoEnvironment = getMonacoEnvironment(); if (monacoEnvironment) { if (typeof monacoEnvironment.getWorker === 'function') { return monacoEnvironment.getWorker('workerMain.js', label); } if (typeof monacoEnvironment.getWorkerUrl === 'function') { const workerUrl = monacoEnvironment.getWorkerUrl('workerMain.js', label); return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) : workerUrl, { name: label, type: 'module' }); } } const esmWorkerLocation = descriptor.esmModuleLocation; if (esmWorkerLocation) { const workerUrl = getWorkerBootstrapUrl(label, esmWorkerLocation.toString(true)); const worker = new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) : workerUrl, { name: label, type: 'module' }); return whenESMWorkerReady(worker); } throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } function getWorkerBootstrapUrl(label, workerScriptUrl) { if (/^((http:)|(https:)|(file:))/.test(workerScriptUrl) && workerScriptUrl.substring(0, globalThis.origin.length) !== globalThis.origin) { // this is the cross-origin case // i.e. the webpage is running at a different origin than where the scripts are loaded from } else { const start = workerScriptUrl.lastIndexOf('?'); const end = workerScriptUrl.lastIndexOf('#', start); const params = start > 0 ? new URLSearchParams(workerScriptUrl.substring(start + 1, ~end ? end : undefined)) : new URLSearchParams(); COI.addSearchParam(params, true, true); const search = params.toString(); if (!search) { workerScriptUrl = `${workerScriptUrl}#${label}`; } else { workerScriptUrl = `${workerScriptUrl}?${params.toString()}#${label}`; } } // In below blob code, we are using JSON.stringify to ensure the passed // in values are not breaking our script. The values may contain string // terminating characters (such as ' or "). const blob = new Blob([coalesce([ `/*${label}*/`, `globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(getNLSMessages())};`, `globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(getNLSLanguage())};`, `globalThis._VSCODE_FILE_ROOT = ${JSON.stringify(globalThis._VSCODE_FILE_ROOT)};`, `const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });`, `globalThis.workerttPolicy = ttPolicy;`, `await import(ttPolicy?.createScriptURL(${JSON.stringify(workerScriptUrl)}) ?? ${JSON.stringify(workerScriptUrl)});`, `globalThis.postMessage({ type: 'vscode-worker-ready' });`, `/*${label}*/` ]).join('')], { type: 'application/javascript' }); return URL.createObjectURL(blob); } function whenESMWorkerReady(worker) { return new Promise((resolve, reject) => { worker.onmessage = function (e) { if (e.data.type === 'vscode-worker-ready') { worker.onmessage = null; resolve(worker); } }; worker.onerror = reject; }); } function isPromiseLike(obj) { return !!obj && typeof obj.then === 'function'; } /** * A worker that uses HTML5 web workers so that is has * its own global scope and its own thread. */ class WebWorker extends Disposable { static { this.LAST_WORKER_ID = 0; } constructor(descriptorOrWorker) { super(); this._onMessage = this._register(new Emitter()); this.onMessage = this._onMessage.event; this._onError = this._register(new Emitter()); this.onError = this._onError.event; this.id = ++WebWorker.LAST_WORKER_ID; const workerOrPromise = (descriptorOrWorker instanceof Worker ? descriptorOrWorker : 'then' in descriptorOrWorker ? descriptorOrWorker : getWorker(descriptorOrWorker, this.id)); if (isPromiseLike(workerOrPromise)) { this.worker = workerOrPromise; } else { this.worker = Promise.resolve(workerOrPromise); } this.postMessage('-please-ignore-', []); // TODO: Eliminate this extra message const errorHandler = (ev) => { this._onError.fire(ev); }; this.worker.then((w) => { w.onmessage = (ev) => { this._onMessage.fire(ev.data); }; w.onmessageerror = (ev) => { this._onError.fire(ev); }; if (typeof w.addEventListener === 'function') { w.addEventListener('error', errorHandler); } }); this._register(toDisposable(() => { this.worker?.then(w => { w.onmessage = null; w.onmessageerror = null; w.removeEventListener('error', errorHandler); w.terminate(); }); this.worker = null; })); } getId() { return this.id; } postMessage(message, transfer) { this.worker?.then(w => { try { w.postMessage(message, transfer); } catch (err) { onUnexpectedError(err); onUnexpectedError(new Error(`FAILED to post message to worker`, { cause: err })); } }); } } export class WebWorkerDescriptor { constructor(esmModuleLocation, label) { this.esmModuleLocation = esmModuleLocation; this.label = label; } } export function createWebWorker(arg0, arg1) { const workerDescriptorOrWorker = (URI.isUri(arg0) ? new WebWorkerDescriptor(arg0, arg1) : arg0); return new WebWorkerClient(new WebWorker(workerDescriptorOrWorker)); } //# sourceMappingURL=webWorkerFactory.js.map