Path: blob/main/src/vs/platform/browserView/node/playwrightChannel.ts
13397 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 { Event } from '../../../base/common/event.js';6import { Disposable, DisposableMap } from '../../../base/common/lifecycle.js';7import { IPCServer, IServerChannel } from '../../../base/parts/ipc/common/ipc.js';8import { IMainProcessService } from '../../ipc/common/mainProcessService.js';9import { ILogService } from '../../log/common/log.js';10import { IAgentNetworkFilterService } from '../../networkFilter/common/networkFilterService.js';11import { BrowserViewGroupRemoteService } from './browserViewGroupRemoteService.js';12import { PlaywrightService } from './playwrightService.js';1314/**15* IPC channel for the Playwright service.16*17* Each connected window gets its own {@link PlaywrightService},18* keyed by the opaque IPC connection context. The client sends an19* `__initialize` call with its numeric window ID before any other20* method calls, which eagerly creates the instance. When a window21* disconnects the instance is automatically disposed.22*/23export class PlaywrightChannel extends Disposable implements IServerChannel<string> {2425private readonly _instances = this._register(new DisposableMap<string, PlaywrightService>());26private readonly browserViewGroupRemoteService: BrowserViewGroupRemoteService;2728constructor(29ipcServer: IPCServer<string>,30mainProcessService: IMainProcessService,31private readonly logService: ILogService,32private readonly agentNetworkFilterService: IAgentNetworkFilterService,33) {34super();35this.browserViewGroupRemoteService = new BrowserViewGroupRemoteService(mainProcessService);36this._register(ipcServer.onDidRemoveConnection(c => {37this._instances.deleteAndDispose(c.ctx);38}));39}4041listen<T>(ctx: string, event: string): Event<T> {42const instance = this._instances.get(ctx);43if (!instance) {44throw new Error(`Window not initialized for context: ${ctx}`);45}46const source = (instance as unknown as Record<string, Event<unknown>>)[event];47if (typeof source !== 'function') {48throw new Error(`Event not found: ${event}`);49}50return source as Event<T>;51}5253call<T>(ctx: string, command: string, arg?: unknown): Promise<T> {54// Handle the one-time initialization call that creates the instance55if (command === '__initialize') {56if (typeof arg !== 'number') {57throw new Error(`Invalid argument for __initialize: expected window ID as number, got ${typeof arg}`);58}59if (!this._instances.has(ctx)) {60const windowId = arg as number;61this._instances.set(ctx, new PlaywrightService(windowId, this.browserViewGroupRemoteService, this.logService, this.agentNetworkFilterService));62}63return Promise.resolve(undefined as T);64}6566const instance = this._instances.get(ctx);67if (!instance) {68throw new Error(`Window not initialized for context: ${ctx}`);69}7071const target = (instance as unknown as Record<string, unknown>)[command];72if (typeof target !== 'function') {73throw new Error(`Method not found: ${command}`);74}7576const methodArgs = Array.isArray(arg) ? arg : [];77let res = target.apply(instance, methodArgs);78if (!(res instanceof Promise)) {79res = Promise.resolve(res);80}81return res;82}83}848586