Path: blob/main/src/vs/platform/crossAppIpc/electron-main/crossAppIpcService.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as electron from 'electron';6import { TimeoutTimer } from '../../../base/common/async.js';7import { Emitter, Event } from '../../../base/common/event.js';8import { Disposable } from '../../../base/common/lifecycle.js';9import { createDecorator } from '../../instantiation/common/instantiation.js';10import { ILogService } from '../../log/common/log.js';1112export const ICrossAppIPCService = createDecorator<ICrossAppIPCService>('crossAppIPCService');1314export interface ICrossAppIPCMessage {15readonly type: string;16readonly data?: unknown;17}1819export interface ICrossAppIPCService {20readonly _serviceBrand: undefined;2122/** Whether the Electron crossAppIPC API is supported in this build. */23readonly isSupported: boolean;2425/** Whether initialize() has been called and successfully set up the IPC. */26readonly initialized: boolean;2728/** Whether the IPC connection is active. */29readonly connected: boolean;3031/** Whether this app is the IPC server (`true`) or client (`false`). Only meaningful when connected. */32readonly isServer: boolean;3334/** Fires when the peer connects. The boolean indicates whether this app is the server. */35readonly onDidConnect: Event<boolean /* isServer */>;3637/** Fires when the peer disconnects. The string is the disconnect reason. */38readonly onDidDisconnect: Event<string /* reason */>;3940/** Fires when a message is received from the peer. */41readonly onDidReceiveMessage: Event<ICrossAppIPCMessage>;4243/** Send a message to the peer. No-op if not connected. */44sendMessage(msg: ICrossAppIPCMessage): void;4546/** Initialize the IPC connection. Call once during startup. */47initialize(): void;48}4950/**51* Manages the single crossAppIPC connection for the entire application.52*/53export class CrossAppIPCService extends Disposable implements ICrossAppIPCService {5455declare readonly _serviceBrand: undefined;5657private ipc: Electron.CrossAppIPC | undefined;58private _connected = false;59private _isServer = false;60private readonly reconnectTimer = this._register(new TimeoutTimer());6162private readonly _onDidConnect = this._register(new Emitter<boolean>());63readonly onDidConnect: Event<boolean> = this._onDidConnect.event;6465private readonly _onDidDisconnect = this._register(new Emitter<string>());66readonly onDidDisconnect: Event<string> = this._onDidDisconnect.event;6768private readonly _onDidReceiveMessage = this._register(new Emitter<ICrossAppIPCMessage>());69readonly onDidReceiveMessage: Event<ICrossAppIPCMessage> = this._onDidReceiveMessage.event;7071get isSupported(): boolean {72const crossAppIPC: Electron.CrossAppIPCModule | undefined = (electron as typeof electron & { crossAppIPC?: Electron.CrossAppIPCModule }).crossAppIPC;73return crossAppIPC !== undefined;74}75get initialized(): boolean { return this.ipc !== undefined; }76get connected(): boolean { return this._connected; }77get isServer(): boolean { return this._isServer; }7879constructor(80@ILogService private readonly logService: ILogService,81) {82super();83}8485initialize(): void {86if (this.ipc) {87return; // Already initialized88}8990const crossAppIPC: Electron.CrossAppIPCModule | undefined = (electron as typeof electron & { crossAppIPC?: Electron.CrossAppIPCModule }).crossAppIPC;9192if (!crossAppIPC) {93this.logService.info('CrossAppIPCService: crossAppIPC not available');94return;95}9697const ipc = crossAppIPC.createCrossAppIPC();98this.ipc = ipc;99100ipc.on('connected', () => {101this._connected = true;102this._isServer = ipc.isServer;103this.logService.info(`CrossAppIPCService: connected (isServer=${ipc.isServer})`);104this._onDidConnect.fire(ipc.isServer);105});106107ipc.on('message', (messageEvent) => {108this._onDidReceiveMessage.fire(messageEvent.data as ICrossAppIPCMessage);109});110111ipc.on('disconnected', (reason) => {112this.logService.info(`CrossAppIPCService: disconnected (${reason})`);113this._connected = false;114this._isServer = false;115this._onDidDisconnect.fire(reason);116117// Reconnect to wait for the peer's next launch.118// Delay briefly to allow the old Mach bootstrap service to be119// deregistered before re-creating the server endpoint (macOS).120if (reason === 'peer-disconnected') {121this.reconnectTimer.cancelAndSet(() => ipc.connect(), 1000);122}123});124125ipc.connect();126this.logService.info('CrossAppIPCService: connecting to peer');127}128129sendMessage(msg: ICrossAppIPCMessage): void {130if (this.ipc?.connected) {131this.ipc.postMessage(msg);132}133}134135override dispose(): void {136this.ipc?.close();137super.dispose();138}139}140141142