From 5d35e32b7bc705a10ea7c86fe16a961d832d986b Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 18 Dec 2023 10:03:21 +0100 Subject: [PATCH] RPC Protocol ClientProxyHandler Init Events #13172 * add other dependencies * make sure to not start initialization multiple times --- .../plugin-ext/src/common/proxy-handler.ts | 9 +++-- .../plugin-ext/src/common/rpc-protocol.ts | 4 +-- .../src/hosted/browser/worker/worker-main.ts | 12 ++++--- .../src/main/browser/main-context.ts | 13 +++---- .../src/plugin/node/debug/debug.spec.ts | 2 +- .../plugin-ext/src/plugin/plugin-context.ts | 34 +++++++++++++------ 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts index 0e2fe42cae60e..335f50c129429 100644 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ b/packages/plugin-ext/src/common/proxy-handler.ts @@ -38,6 +38,7 @@ export interface InvocationHandlerOptions extends RpcHandlerOptions { export class ClientProxyHandler implements ProxyHandler { private rpcDeferred: Deferred = new Deferred(); private isRpcInitialized = false; + private isInitializing = false; readonly id: string; private readonly channelProvider: () => Promise; @@ -50,12 +51,14 @@ export class ClientProxyHandler implements ProxyHandler { } initializeRpc(): void { - if (!this.isRpcInitialized) { + if (!this.isRpcInitialized && !this.isInitializing) { + this.isInitializing = true; const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; this.channelProvider().then(channel => { const rpc = new RpcProtocol(channel, undefined, clientOptions); this.rpcDeferred.resolve(rpc); this.isRpcInitialized = true; + this.isInitializing = false; if (this.onInitialize) { this.onInitialize(); } @@ -64,7 +67,9 @@ export class ClientProxyHandler implements ProxyHandler { } get(target: any, name: string, receiver: any): any { - this.initializeRpc(); + if (!this.isRpcInitialized) { + this.initializeRpc(); + } if (target[name] || name.charCodeAt(0) !== 36 /* CharCode.DollarSign */) { return target[name]; diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index 187b03bcdb31d..999b4513fdc9e 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -85,7 +85,6 @@ export class RPCProtocolImpl implements RPCProtocol { private readonly multiplexer: ChannelMultiplexer; private readonly encoder = new MsgPackMessageEncoder(); private readonly decoder = new MsgPackMessageDecoder(); - private readonly onInitializeEmitter: Emitter = new Emitter(); private readonly toDispose = new DisposableCollection( Disposable.create(() => { /* mark as no disposed */ }) @@ -96,7 +95,6 @@ export class RPCProtocolImpl implements RPCProtocol { this.toDispose.push(Disposable.create(() => this.proxies.clear())); this.toDispose.push(Disposable.create(() => this.handler.clear())); this.toDispose.push(Disposable.create(() => this.remoteDependencies.clear())); - this.toDispose.push(this.onInitializeEmitter); } dispose(): void { @@ -176,7 +174,7 @@ export class RPCProtocolImpl implements RPCProtocol { return instance; } - initialize(proxyId: ProxyIdentifier): void { + protected initialize(proxyId: ProxyIdentifier): void { /* make sure proxy exists */ this.getProxy(proxyId); /* init */ diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts index caae9776e2f18..18c9c7f6879ac 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts @@ -18,7 +18,7 @@ import 'reflect-metadata'; import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; import * as theia from '@theia/plugin'; -import { emptyPlugin, MAIN_RPC_CONTEXT, Plugin, TerminalServiceExt } from '../../../common/plugin-api-rpc'; +import { emptyPlugin, MAIN_RPC_CONTEXT, Plugin, TerminalServiceExt, PLUGIN_RPC_CONTEXT } from '../../../common/plugin-api-rpc'; import { ExtPluginApi } from '../../../common/plugin-ext-api-contribution'; import { getPluginId, PluginMetadata } from '../../../common/plugin-protocol'; import { RPCProtocolImpl } from '../../../common/rpc-protocol'; @@ -208,12 +208,14 @@ const handler = { }; ctx['theia'] = new Proxy(Object.create(null), handler); -rpc.set(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, pluginManager); +rpc.set(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, pluginManager, + new Set([PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, PLUGIN_RPC_CONTEXT.STORAGE_MAIN, + PLUGIN_RPC_CONTEXT.WEBVIEWS_MAIN])); rpc.set(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT, editorsAndDocuments); -rpc.set(MAIN_RPC_CONTEXT.WORKSPACE_EXT, workspaceExt); -rpc.set(MAIN_RPC_CONTEXT.PREFERENCE_REGISTRY_EXT, preferenceRegistryExt); +rpc.set(MAIN_RPC_CONTEXT.WORKSPACE_EXT, workspaceExt, new Set([PLUGIN_RPC_CONTEXT.WORKSPACE_MAIN, PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN])); +rpc.set(MAIN_RPC_CONTEXT.PREFERENCE_REGISTRY_EXT, preferenceRegistryExt, new Set([PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.WORKSPACE_MAIN])); rpc.set(MAIN_RPC_CONTEXT.STORAGE_EXT, storageProxy); -rpc.set(MAIN_RPC_CONTEXT.WEBVIEWS_EXT, webviewExt); +rpc.set(MAIN_RPC_CONTEXT.WEBVIEWS_EXT, webviewExt, new Set([PLUGIN_RPC_CONTEXT.WEBVIEWS_MAIN, PLUGIN_RPC_CONTEXT.WORKSPACE_MAIN])); function isElectron(): boolean { if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) { diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index 7e1925bb2a43d..bec123b6b6872 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -111,13 +111,14 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN, notebookEditorsMain); const notebookDocumentsMain = new NotebookDocumentsMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain); - rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, notebookDocumentsMain, notebookEditorsMain)); + rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, notebookDocumentsMain, notebookEditorsMain), + new Set([MAIN_RPC_CONTEXT.NOTEBOOKS_EXT, MAIN_RPC_CONTEXT.NOTEBOOK_EDITORS_EXT, MAIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_EXT])); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, new NotebookKernelsMainImpl(rpc, container)); const bulkEditService = container.get(MonacoBulkEditService); const monacoEditorService = container.get(MonacoEditorService); const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, bulkEditService, monacoEditorService); - rpc.set(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN, editorsMain); + rpc.set(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN, editorsMain, new Set([MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT, MAIN_RPC_CONTEXT.DOCUMENTS_EXT])); // start listening only after all clients are subscribed to events editorsAndDocuments.listen(); @@ -132,7 +133,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, notificationMain); const testingMain = new TestingMainImpl(rpc, container, commandRegistryMain); - rpc.set(PLUGIN_RPC_CONTEXT.TESTING_MAIN, testingMain); + rpc.set(PLUGIN_RPC_CONTEXT.TESTING_MAIN, testingMain, new Set([MAIN_RPC_CONTEXT.TESTING_EXT, MAIN_RPC_CONTEXT.COMMAND_REGISTRY_EXT])); const terminalMain = new TerminalServiceMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.TERMINAL_MAIN, terminalMain); @@ -152,10 +153,10 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.WEBVIEWS_MAIN, webviewsMain); const customEditorsMain = new CustomEditorsMainImpl(rpc, container, webviewsMain); - rpc.set(PLUGIN_RPC_CONTEXT.CUSTOM_EDITORS_MAIN, customEditorsMain); + rpc.set(PLUGIN_RPC_CONTEXT.CUSTOM_EDITORS_MAIN, customEditorsMain, new Set([MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, MAIN_RPC_CONTEXT.WEBVIEWS_EXT])); const webviewViewsMain = new WebviewViewsMainImpl(rpc, container, webviewsMain); - rpc.set(PLUGIN_RPC_CONTEXT.WEBVIEW_VIEWS_MAIN, webviewViewsMain); + rpc.set(PLUGIN_RPC_CONTEXT.WEBVIEW_VIEWS_MAIN, webviewViewsMain, new Set([MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, MAIN_RPC_CONTEXT.WEBVIEWS_EXT])); const storageMain = new StorageMainImpl(container); rpc.set(PLUGIN_RPC_CONTEXT.STORAGE_MAIN, storageMain); @@ -167,7 +168,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.TASKS_MAIN, tasksMain); const debugMain = new DebugMainImpl(rpc, connectionMain, container); - rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain); + rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain, new Set([MAIN_RPC_CONTEXT.DEBUG_EXT, MAIN_RPC_CONTEXT.CONNECTION_EXT])); const fs = new FileSystemMainImpl(rpc, container); const fsEventService = new MainFileSystemEventService(rpc, container); diff --git a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts index 6ec0666cb39ad..16b2c330ac252 100644 --- a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts +++ b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts @@ -34,7 +34,7 @@ describe('Debug API', () => { }, dispose(): void { // Nothing - }, + } }; const debug = new DebugExtImpl(mockRPCProtocol); diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 76a6a87da7c89..0088d67c608e0 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -272,31 +272,43 @@ export function createAPIFactory( const notificationExt = rpc.set(MAIN_RPC_CONTEXT.NOTIFICATION_EXT, new NotificationExtImpl(rpc)); const editors = rpc.set(MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT, new TextEditorsExtImpl(rpc, editorsAndDocumentsExt)); const documents = rpc.set(MAIN_RPC_CONTEXT.DOCUMENTS_EXT, new DocumentsExtImpl(rpc, editorsAndDocumentsExt)); - const notebooksExt = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT, new NotebooksExtImpl(rpc, commandRegistry, editorsAndDocumentsExt, documents)); + const notebooksExt = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT, new NotebooksExtImpl(rpc, commandRegistry, editorsAndDocumentsExt, documents), + new Set([PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN, + PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN])); const notebookEditors = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_EDITORS_EXT, new NotebookEditorsExtImpl(notebooksExt)); - const notebookRenderers = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_EXT, new NotebookRenderersExtImpl(rpc, notebooksExt)); - const notebookKernels = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_KERNELS_EXT, new NotebookKernelsExtImpl(rpc, notebooksExt, commandRegistry)); + const notebookRenderers = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_EXT, new NotebookRenderersExtImpl(rpc, notebooksExt), + new Set([PLUGIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, + PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN])); + const notebookKernels = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_KERNELS_EXT, new NotebookKernelsExtImpl(rpc, notebooksExt, commandRegistry), + new Set([PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, + PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN])); const notebookDocuments = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_EXT, new NotebookDocumentsExtImpl(notebooksExt)); const statusBarMessageRegistryExt = new StatusBarMessageRegistryExt(rpc); const terminalExt = rpc.set(MAIN_RPC_CONTEXT.TERMINAL_EXT, new TerminalServiceExtImpl(rpc)); const outputChannelRegistryExt = rpc.set(MAIN_RPC_CONTEXT.OUTPUT_CHANNEL_REGISTRY_EXT, new OutputChannelRegistryExtImpl(rpc)); - const treeViewsExt = rpc.set(MAIN_RPC_CONTEXT.TREE_VIEWS_EXT, new TreeViewsExtImpl(rpc, commandRegistry)); - const tasksExt = rpc.set(MAIN_RPC_CONTEXT.TASKS_EXT, new TasksExtImpl(rpc, terminalExt)); + const treeViewsExt = rpc.set(MAIN_RPC_CONTEXT.TREE_VIEWS_EXT, new TreeViewsExtImpl(rpc, commandRegistry), + new Set([PLUGIN_RPC_CONTEXT.TREE_VIEWS_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN])); + const tasksExt = rpc.set(MAIN_RPC_CONTEXT.TASKS_EXT, new TasksExtImpl(rpc, terminalExt), new Set([PLUGIN_RPC_CONTEXT.TASKS_MAIN, PLUGIN_RPC_CONTEXT.TERMINAL_MAIN])); const connectionExt = rpc.set(MAIN_RPC_CONTEXT.CONNECTION_EXT, new ConnectionImpl(rpc.getProxy(PLUGIN_RPC_CONTEXT.CONNECTION_MAIN))); const fileSystemExt = rpc.set(MAIN_RPC_CONTEXT.FILE_SYSTEM_EXT, new FileSystemExtImpl(rpc)); - const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry, fileSystemExt)); + const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry, fileSystemExt), + new Set([PLUGIN_RPC_CONTEXT.LANGUAGES_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, PLUGIN_RPC_CONTEXT.FILE_SYSTEM_MAIN])); const extHostFileSystemEvent = rpc.set(MAIN_RPC_CONTEXT.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpc, editorsAndDocumentsExt)); - const scmExt = rpc.set(MAIN_RPC_CONTEXT.SCM_EXT, new ScmExtImpl(rpc, commandRegistry)); + const scmExt = rpc.set(MAIN_RPC_CONTEXT.SCM_EXT, new ScmExtImpl(rpc, commandRegistry), new Set([PLUGIN_RPC_CONTEXT.SCM_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN])); const decorationsExt = rpc.set(MAIN_RPC_CONTEXT.DECORATIONS_EXT, new DecorationsExtImpl(rpc)); const labelServiceExt = rpc.set(MAIN_RPC_CONTEXT.LABEL_SERVICE_EXT, new LabelServiceExtImpl(rpc)); - const timelineExt = rpc.set(MAIN_RPC_CONTEXT.TIMELINE_EXT, new TimelineExtImpl(rpc, commandRegistry)); + const timelineExt = rpc.set(MAIN_RPC_CONTEXT.TIMELINE_EXT, new TimelineExtImpl(rpc, commandRegistry), + new Set([PLUGIN_RPC_CONTEXT.TIMELINE_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN])); const themingExt = rpc.set(MAIN_RPC_CONTEXT.THEMING_EXT, new ThemingExtImpl(rpc)); - const commentsExt = rpc.set(MAIN_RPC_CONTEXT.COMMENTS_EXT, new CommentsExtImpl(rpc, commandRegistry, documents)); + const commentsExt = rpc.set(MAIN_RPC_CONTEXT.COMMENTS_EXT, new CommentsExtImpl(rpc, commandRegistry, documents), + new Set([PLUGIN_RPC_CONTEXT.COMMENTS_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN, PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN])); const tabsExt = rpc.set(MAIN_RPC_CONTEXT.TABS_EXT, new TabsExtImpl(rpc)); - const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt, workspaceExt)); + const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt, workspaceExt), + new Set([PLUGIN_RPC_CONTEXT.CUSTOM_EDITORS_MAIN, PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN])); const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt)); const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl()); - const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry)); + const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry), + new Set([PLUGIN_RPC_CONTEXT.TESTING_MAIN, PLUGIN_RPC_CONTEXT.COMMAND_REGISTRY_MAIN])); rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt); return function (plugin: InternalPlugin): typeof theia {