Path: blob/main/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts
3296 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 { URI, UriComponents } from '../../../../base/common/uri.js';7import { IChannel } from '../../../../base/parts/ipc/common/ipc.js';8import { IExtensionHostDebugService, IOpenExtensionWindowResult } from '../../../../platform/debug/common/extensionHostDebug.js';9import { ExtensionHostDebugBroadcastChannel, ExtensionHostDebugChannelClient } from '../../../../platform/debug/common/extensionHostDebugIpc.js';10import { IFileService } from '../../../../platform/files/common/files.js';11import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';12import { ILogService } from '../../../../platform/log/common/log.js';13import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';14import { isFolderToOpen, isWorkspaceToOpen } from '../../../../platform/window/common/window.js';15import { IWorkspaceContextService, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, toWorkspaceIdentifier, hasWorkspaceFileExtension } from '../../../../platform/workspace/common/workspace.js';16import { IWorkspace, IWorkspaceProvider } from '../../../browser/web.api.js';17import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js';18import { IHostService } from '../../../services/host/browser/host.js';19import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';2021class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {2223private static readonly LAST_EXTENSION_DEVELOPMENT_WORKSPACE_KEY = 'debug.lastExtensionDevelopmentWorkspace';2425private workspaceProvider: IWorkspaceProvider;2627private readonly storageService: IStorageService;28private readonly fileService: IFileService;2930constructor(31@IRemoteAgentService remoteAgentService: IRemoteAgentService,32@IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService,33@ILogService logService: ILogService,34@IHostService hostService: IHostService,35@IWorkspaceContextService contextService: IWorkspaceContextService,36@IStorageService storageService: IStorageService,37@IFileService fileService: IFileService38) {39const connection = remoteAgentService.getConnection();40let channel: IChannel;41if (connection) {42channel = connection.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName);43} else {44// Extension host debugging not supported in serverless.45channel = { call: async () => undefined, listen: () => Event.None } as any;46}4748super(channel);4950this.storageService = storageService;51this.fileService = fileService;5253if (environmentService.options && environmentService.options.workspaceProvider) {54this.workspaceProvider = environmentService.options.workspaceProvider;55} else {56this.workspaceProvider = { open: async () => true, workspace: undefined, trusted: undefined };57logService.warn('Extension Host Debugging not available due to missing workspace provider.');58}5960// Reload window on reload request61this._register(this.onReload(event => {62if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {63hostService.reload();64}65}));6667// Close window on close request68this._register(this.onClose(event => {69if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {70hostService.close();71}72}));7374// Remember workspace as last used for extension development75// (unless this is API tests) to restore for a future session76if (environmentService.isExtensionDevelopment && !environmentService.extensionTestsLocationURI) {77const workspaceId = toWorkspaceIdentifier(contextService.getWorkspace());78if (isSingleFolderWorkspaceIdentifier(workspaceId) || isWorkspaceIdentifier(workspaceId)) {79const serializedWorkspace = isSingleFolderWorkspaceIdentifier(workspaceId) ? { folderUri: workspaceId.uri.toJSON() } : { workspaceUri: workspaceId.configPath.toJSON() };80storageService.store(BrowserExtensionHostDebugService.LAST_EXTENSION_DEVELOPMENT_WORKSPACE_KEY, JSON.stringify(serializedWorkspace), StorageScope.PROFILE, StorageTarget.MACHINE);81} else {82storageService.remove(BrowserExtensionHostDebugService.LAST_EXTENSION_DEVELOPMENT_WORKSPACE_KEY, StorageScope.PROFILE);83}84}85}8687override async openExtensionDevelopmentHostWindow(args: string[], _debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {8889// Add environment parameters required for debug to work90const environment = new Map<string, string>();9192const fileUriArg = this.findArgument('file-uri', args);93if (fileUriArg && !hasWorkspaceFileExtension(fileUriArg)) {94environment.set('openFile', fileUriArg);95}9697const copyArgs = [98'extensionDevelopmentPath',99'extensionTestsPath',100'extensionEnvironment',101'debugId',102'inspect-brk-extensions',103'inspect-extensions',104];105106for (const argName of copyArgs) {107const value = this.findArgument(argName, args);108if (value) {109environment.set(argName, value);110}111}112113// Find out which workspace to open debug window on114let debugWorkspace: IWorkspace = undefined;115const folderUriArg = this.findArgument('folder-uri', args);116if (folderUriArg) {117debugWorkspace = { folderUri: URI.parse(folderUriArg) };118} else {119const fileUriArg = this.findArgument('file-uri', args);120if (fileUriArg && hasWorkspaceFileExtension(fileUriArg)) {121debugWorkspace = { workspaceUri: URI.parse(fileUriArg) };122}123}124125const extensionTestsPath = this.findArgument('extensionTestsPath', args);126if (!debugWorkspace && !extensionTestsPath) {127const lastExtensionDevelopmentWorkspace = this.storageService.get(BrowserExtensionHostDebugService.LAST_EXTENSION_DEVELOPMENT_WORKSPACE_KEY, StorageScope.PROFILE);128if (lastExtensionDevelopmentWorkspace) {129try {130const serializedWorkspace: { workspaceUri?: UriComponents; folderUri?: UriComponents } = JSON.parse(lastExtensionDevelopmentWorkspace);131if (serializedWorkspace.workspaceUri) {132debugWorkspace = { workspaceUri: URI.revive(serializedWorkspace.workspaceUri) };133} else if (serializedWorkspace.folderUri) {134debugWorkspace = { folderUri: URI.revive(serializedWorkspace.folderUri) };135}136} catch (error) {137// ignore138}139}140}141142// Validate workspace exists143if (debugWorkspace) {144const debugWorkspaceResource = isFolderToOpen(debugWorkspace) ? debugWorkspace.folderUri : isWorkspaceToOpen(debugWorkspace) ? debugWorkspace.workspaceUri : undefined;145if (debugWorkspaceResource) {146const workspaceExists = await this.fileService.exists(debugWorkspaceResource);147if (!workspaceExists) {148debugWorkspace = undefined;149}150}151}152153// Open debug window as new window. Pass arguments over.154const success = await this.workspaceProvider.open(debugWorkspace, {155reuse: false, // debugging always requires a new window156payload: Array.from(environment.entries()) // mandatory properties to enable debugging157});158159return { success };160}161162private findArgument(key: string, args: string[]): string | undefined {163for (const a of args) {164const k = `--${key}=`;165if (a.indexOf(k) === 0) {166return a.substring(k.length);167}168}169170return undefined;171}172}173174registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService, InstantiationType.Delayed);175176177