"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkerExtProcessService = void 0;
const tslib_1 = require("tslib");
const di_1 = require("@opensumi/di");
const utils_1 = require("@opensumi/ide-components/lib/utils");
const ide_connection_1 = require("@opensumi/ide-connection");
const message_port_1 = require("@opensumi/ide-connection/lib/common/connection/drivers/message-port");
const ide_core_browser_1 = require("@opensumi/ide-core-browser");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const common_1 = require("../common");
const protocols_1 = require("../common/vscode/protocols");
const loader_1 = require("./loader");
const main_thread_api_impl_1 = require("./sumi/main.thread.api.impl");
const main_thread_api_impl_2 = require("./vscode/api/main.thread.api.impl");
const main_thread_api_shared_impl_1 = require("./vscode/api/main.thread.api.shared-impl");
const workerHostIframe_1 = require("./workerHostIframe");
const { posix } = ide_core_common_1.path;
let WorkerExtProcessService = class WorkerExtProcessService extends ide_core_common_1.Disposable {
    constructor() {
        super(...arguments);
        this.ready = new ide_core_browser_1.Deferred();
        this._extHostUpdated = new ide_core_browser_1.Deferred();
        this.extensions = [];
        this.apiFactoryDisposable = new ide_core_common_1.DisposableStore();
    }
    disposeApiFactory() {
        var _a, _b;
        this.apiFactoryDisposable.clear();
        if (this.protocol) {
            (_b = (_a = this.protocol).dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
        }
        if (this.connection) {
            this.connection.dispose();
        }
    }
    disposeProcess() {
        this.disposeApiFactory();
    }
    async activate(ignoreCors) {
        this.protocol = await this.createExtProcess(ignoreCors);
        if (this.protocol) {
            this.ready.resolve();
            this.logger.log('[Worker Host] init worker thread api proxy');
            const apiProxy = (0, main_thread_api_shared_impl_1.initSharedAPIProxy)(this.protocol, this.injector);
            this.apiFactoryDisposable.add(apiProxy);
            this.apiFactoryDisposable.add((0, ide_core_common_1.toDisposable)((0, main_thread_api_impl_2.initWorkerThreadAPIProxy)(this.protocol, this.injector, this)));
            this.apiFactoryDisposable.add((0, ide_core_common_1.toDisposable)((0, main_thread_api_impl_1.createSumiAPIFactory)(this.protocol, this.injector)));
            await apiProxy.setup();
            await this.getProxy().$updateExtHostData();
            this._extHostUpdated.resolve();
        }
        return this.protocol;
    }
    async activeExtension(extension, isWebExtension) {
        var _a, _b;
        const { extendConfig, packageJSON, id } = extension;
        // 对使用 kaitian.js 的老插件兼容
        // 因为可能存在即用了 kaitian.js 作为入口，又注册了 sumiContributes 贡献点的插件
        if ((_a = extendConfig === null || extendConfig === void 0 ? void 0 : extendConfig.worker) === null || _a === void 0 ? void 0 : _a.main) {
            (0, utils_1.warning)(false, `[Deprecated warning]: ${id}: kaitian.js is deprecated, please use \`package.json#sumiContributes\` instead`);
            await this.doActivateExtension(extension);
            return;
        }
        if (
        // 激活 workerMain 相关部分
        (packageJSON.sumiContributes && ((_b = extension.contributes) === null || _b === void 0 ? void 0 : _b.workerMain)) ||
            // 激活 packageJSON.browser 相关部分
            (isWebExtension && packageJSON.browser)) {
            await this.doActivateExtension(extension);
        }
    }
    async doActivateExtension(extension) {
        if (this.appConfig.extWorkerHost) {
            // 只有当 proxy.$updateExtHostData 调用之后才可以开始激活插件
            await this._extHostUpdated.promise;
            await this.getProxy().$activateExtension(extension.id);
        }
    }
    async updateExtensionData(extensions) {
        this.extensions = extensions;
        if (this.protocol) {
            this.getProxy().$updateExtHostData();
        }
    }
    getExtension(extensionId) {
        return this.extensions.find((e) => e.id === extensionId);
    }
    // 以下三个方法是给插件进程调用的 rpc call
    async $activateExtension(extensionPath) {
        const extension = this.extensions.find((n) => n.workerScriptPath === extensionPath);
        if (extension) {
            await extension.activate();
        }
    }
    async $getExtensions() {
        return this.extensions.map((extension) => {
            if (extension.contributes && extension.contributes.workerMain) {
                return this.getWorkerExtensionProps(extension, extension.contributes.workerMain);
            }
            else if (extension.packageJSON.browser) {
                return this.getWorkerExtensionProps(extension, extension.packageJSON.browser);
            }
            else if (extension.extendConfig && extension.extendConfig.worker && extension.extendConfig.worker.main) {
                return this.getWorkerExtensionProps(extension, extension.extendConfig.worker.main);
            }
            else {
                return extension.toJSON();
            }
        });
    }
    async $getStaticServicePath() {
        return this.appConfig.staticServicePath || `http://${window.location.hostname}:8000`;
    }
    async createExtProcess(ignoreCors) {
        const workerSrc = (0, loader_1.getWorkerBootstrapUrl)(this.appConfig.extWorkerHost, 'ExtensionWorkerHost', ignoreCors);
        return await this.initWorkerProtocol(workerSrc);
    }
    async initWorkerProtocol(workerUrl) {
        this.logger.log('[Worker Host] init web worker extension host');
        return new Promise((resolve, reject) => {
            const ready = new ide_core_browser_1.Deferred();
            if (this.appConfig.useIframeWrapWorkerHost) {
                const { iframe, extHostUuid } = (0, workerHostIframe_1.startInsideIframe)(workerUrl);
                const func = (event) => {
                    if (event.source !== iframe.contentWindow) {
                        return;
                    }
                    if (event.data.kaitianWebWorkerExtHostId !== extHostUuid) {
                        return;
                    }
                    if (event.data.data instanceof MessagePort) {
                        ready.resolve(event.data.data);
                        return;
                    }
                };
                window.addEventListener('message', func);
                this.apiFactoryDisposable.add({
                    dispose: () => {
                        window.removeEventListener('message', func);
                        iframe.remove();
                    },
                });
                ready.promise.then((port) => {
                    resolve(this.createProtocol(port));
                });
            }
            else {
                try {
                    const extendWorkerHost = new Worker(workerUrl, { name: 'KaitianWorkerExtensionHost' });
                    extendWorkerHost.onmessage = (e) => {
                        if (e.data instanceof MessagePort) {
                            ready.resolve(e.data);
                        }
                    };
                    extendWorkerHost.onerror = (err) => {
                        reject(err);
                    };
                    this.apiFactoryDisposable.add({
                        dispose: () => {
                            extendWorkerHost.terminate();
                            extendWorkerHost.onmessage = null;
                            extendWorkerHost.onerror = null;
                        },
                    });
                    ready.promise.then((port) => {
                        resolve(this.createProtocol(port));
                    });
                }
                catch (err) {
                    reject(err);
                }
            }
        });
    }
    createProtocol(port) {
        this.connection = new message_port_1.MessagePortConnection(port);
        const protocol = new ide_connection_1.SumiConnectionMultiplexer(this.connection, {
            timeout: this.appConfig.rpcMessageTimeout,
            name: 'worker-ext-host',
            io: (0, ide_connection_1.createExtMessageIO)(protocols_1.knownProtocols),
        });
        this.logger.log('[Worker Host] web worker extension host ready');
        return protocol;
    }
    getActivatedExtensions() {
        return this.getProxy().$getActivatedExtensions();
    }
    getProxy() {
        return this.protocol.getProxy(common_1.WorkerHostAPIIdentifier.ExtWorkerHostExtensionService);
    }
    getWorkerExtensionProps(extension, workerMain) {
        let entryScript = workerMain;
        // 有部分 web extension 在申明 browser 入口字段的时候，不会带上文件后缀，导致 fetch 获取文件 404
        if (!entryScript.endsWith('.js')) {
            entryScript += '.js';
        }
        // 这里路径遵循 posix 方式，fsPath 会自动根据平台转换
        const workerScriptPath = new ide_core_browser_1.URI(extension.extensionLocation.with({
            path: posix.join(extension.extensionLocation.path, entryScript),
        })).toString();
        return Object.assign({}, extension.toJSON(), { workerScriptPath });
    }
    getSpawnOptions() {
        return {};
    }
    dispose() {
        super.dispose();
        this.disposeApiFactory();
    }
};
exports.WorkerExtProcessService = WorkerExtProcessService;
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.AppConfig),
    tslib_1.__metadata("design:type", Object)
], WorkerExtProcessService.prototype, "appConfig", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.ILogger),
    tslib_1.__metadata("design:type", Object)
], WorkerExtProcessService.prototype, "logger", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(di_1.INJECTOR_TOKEN),
    tslib_1.__metadata("design:type", di_1.Injector)
], WorkerExtProcessService.prototype, "injector", void 0);
exports.WorkerExtProcessService = WorkerExtProcessService = tslib_1.__decorate([
    (0, di_1.Injectable)()
], WorkerExtProcessService);
//# sourceMappingURL=extension-worker.service.js.map