diff --git a/src/vs/platform/extensionManagement/common/implicitActivationEvents.ts b/src/vs/platform/extensionManagement/common/implicitActivationEvents.ts index 3894c9c08278f..ecd1c43fb1d0a 100644 --- a/src/vs/platform/extensionManagement/common/implicitActivationEvents.ts +++ b/src/vs/platform/extensionManagement/common/implicitActivationEvents.ts @@ -51,6 +51,14 @@ export class ImplicitActivationEventsImpl { } const activationEvents: string[] = (Array.isArray(desc.activationEvents) ? desc.activationEvents.slice(0) : []); + + for (let i = 0; i < activationEvents.length; i++) { + // TODO@joao: there's no easy way to contribute this + if (activationEvents[i] === 'onUri') { + activationEvents[i] = `onUri:${ExtensionIdentifier.toKey(desc.identifier)}`; + } + } + if (!desc.contributes) { // no implicit activation events return activationEvents; diff --git a/src/vs/platform/extensions/common/extensionHostStarter.ts b/src/vs/platform/extensions/common/extensionHostStarter.ts index a5c71cc9b1ab4..8a10562398c38 100644 --- a/src/vs/platform/extensions/common/extensionHostStarter.ts +++ b/src/vs/platform/extensions/common/extensionHostStarter.ts @@ -29,7 +29,7 @@ export interface IExtensionHostStarter { onDynamicExit(id: string): Event<{ code: number; signal: string }>; createExtensionHost(): Promise<{ id: string }>; - start(id: string, opts: IExtensionHostProcessOptions): Promise; + start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number | undefined }>; enableInspectPort(id: string): Promise; kill(id: string): Promise; diff --git a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts index 19fcb4dd70a66..fec3b6eded6d6 100644 --- a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -97,11 +97,12 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter return { id }; } - async start(id: string, opts: IExtensionHostProcessOptions): Promise { + async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number | undefined }> { if (this._shutdown) { throw canceled(); } - this._getExtHost(id).start({ + const extHost = this._getExtHost(id); + extHost.start({ ...opts, type: 'extensionHost', entryPoint: 'vs/workbench/api/node/extensionHostProcess', @@ -111,6 +112,8 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter forceAllocationsToV8Sandbox: true, correlationId: id }); + const pid = await Event.toPromise(extHost.onSpawn); + return { pid }; } async enableInspectPort(id: string): Promise { diff --git a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts index f2276db039658..6bd1d52b9864e 100644 --- a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts +++ b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts @@ -156,6 +156,9 @@ export class UtilityProcess extends Disposable { private readonly _onMessage = this._register(new Emitter()); readonly onMessage = this._onMessage.event; + private readonly _onSpawn = this._register(new Emitter()); + readonly onSpawn = this._onSpawn.event; + private readonly _onExit = this._register(new Emitter()); readonly onExit = this._onExit.event; @@ -303,6 +306,7 @@ export class UtilityProcess extends Disposable { } this.log('successfully created', Severity.Info); + this._onSpawn.fire(process.pid); })); // Exit diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index c9791cc150b32..a46d6748d0b6c 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -362,11 +362,13 @@ export class WebClientServer { return void res.end('Not found'); } + const webWorkerExtensionHostIframeScriptSHA = 'sha256-75NYUUvf+5++1WbfCZOV3PSWxBhONpaxwx+mkOFRv/Y='; + const cspDirectives = [ 'default-src \'self\';', 'img-src \'self\' https: data: blob:;', 'media-src \'self\';', - `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} 'sha256-fh3TwPMflhsEIpR8g1OYTIMVWhXTLcjQ9kh2tIpmv54=' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html + `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html 'child-src \'self\';', `frame-src 'self' https://*.vscode-cdn.net data:;`, 'worker-src \'self\' data: blob:;', diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 404f8f7d9c0cd..0f16b2d3261e3 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -1213,8 +1213,8 @@ class SyncedActivationEventsReader implements IActivationEventsReader { this.addActivationEvents(activationEvents); } - public readActivationEvents(extensionDescription: Readonly): string[] | undefined { - return this._map.get(extensionDescription.identifier); + public readActivationEvents(extensionDescription: Readonly): string[] { + return this._map.get(extensionDescription.identifier) ?? []; } public addActivationEvents(activationEvents: { [extensionId: string]: string[] }): void { diff --git a/src/vs/workbench/api/test/common/extHostExtensionActivator.test.ts b/src/vs/workbench/api/test/common/extHostExtensionActivator.test.ts index 0f738f3892d7b..535efb51ef7d0 100644 --- a/src/vs/workbench/api/test/common/extHostExtensionActivator.test.ts +++ b/src/vs/workbench/api/test/common/extHostExtensionActivator.test.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier, IExtensionDescription, IRelaxedExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionsActivator, IExtensionsActivatorHost } from 'vs/workbench/api/common/extHostExtensionActivator'; -import { ExtensionDescriptionRegistry, basicActivationEventsReader } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { ExtensionDescriptionRegistry, IActivationEventsReader } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ExtensionActivationReason, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtensionsActivator', () => { @@ -248,6 +248,12 @@ suite('ExtensionsActivator', () => { } } + const basicActivationEventsReader: IActivationEventsReader = { + readActivationEvents: (extensionDescription: IExtensionDescription): string[] => { + return extensionDescription.activationEvents ?? []; + } + }; + function createActivator(host: IExtensionsActivatorHost, extensionDescriptions: IExtensionDescription[], otherHostExtensionDescriptions: IExtensionDescription[] = []): ExtensionsActivator { const registry = new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions); const globalRegistry = new ExtensionDescriptionRegistry(basicActivationEventsReader, extensionDescriptions.concat(otherHostExtensionDescriptions)); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index a7ce017ab9fee..8360a183c264d 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -40,6 +40,7 @@ export interface IWebWorkerExtensionHostDataProvider { export class WebWorkerExtensionHost extends Disposable implements IExtensionHost { + public readonly pid = null; public readonly remoteAuthority = null; public extensions: ExtensionHostExtensions | null = null; diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index e1308458a4007..2822c8c949ba6 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -33,8 +33,9 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionDescriptionRegistryLock, ExtensionDescriptionRegistrySnapshot, IActivationEventsReader, LockableExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; -import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensionHostKind'; -import { IExtensionHostManager, createExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker } from 'vs/workbench/services/extensions/common/extensionHostKind'; +import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; @@ -42,6 +43,7 @@ import { ExtensionRunningLocationTracker, filterExtensionIdentifiers } from 'vs/ import { ActivationKind, ActivationTimes, ExtensionActivationReason, ExtensionHostStartup, ExtensionPointContribution, IExtensionHost, IExtensionService, IExtensionsStatus, IInternalExtensionService, IMessage, IResponsiveStateChangeEvent, IWillActivateEvent, WillStopExtensionHostsEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionsProposedApi } from 'vs/workbench/services/extensions/common/extensionsProposedApi'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { LazyCreateExtensionHostManager } from 'vs/workbench/services/extensions/common/lazyCreateExtensionHostManager'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkActivateWorkspaceContainsExtension, checkGlobFileExists } from 'vs/workbench/services/extensions/common/workspaceContains'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -371,12 +373,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx let shouldActivateReason: string | null = null; let hasWorkspaceContains = false; const activationEvents = this._activationEventReader.readActivationEvents(extensionDescription); - for (let activationEvent of activationEvents) { - // TODO@joao: there's no easy way to contribute this - if (activationEvent === 'onUri') { - activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; - } - + for (const activationEvent of activationEvents) { if (this._allRequestedActivateEvents.has(activationEvent)) { // This activation event was fired before the extension was added shouldActivate = true; @@ -753,6 +750,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx const processManager: IExtensionHostManager = this._doCreateExtensionHostManager(extensionHost, initialActivationEvents); processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)); processManager.onDidChangeResponsiveState((responsiveState) => { + this._logService.info(`Extension host (${processManager.friendyName}) is ${responsiveState === ResponsiveState.Responsive ? 'responsive' : 'unresponsive'}.`); this._onDidChangeResponsiveChange.fire({ extensionHostKind: processManager.kind, isResponsive: responsiveState === ResponsiveState.Responsive, @@ -765,7 +763,11 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } protected _doCreateExtensionHostManager(extensionHost: IExtensionHost, initialActivationEvents: string[]): IExtensionHostManager { - return createExtensionHostManager(this._instantiationService, extensionHost, initialActivationEvents, this._acquireInternalAPI(extensionHost)); + const internalExtensionService = this._acquireInternalAPI(extensionHost); + if (extensionHost.startup === ExtensionHostStartup.Lazy && initialActivationEvents.length === 0) { + return this._instantiationService.createInstance(LazyCreateExtensionHostManager, extensionHost, internalExtensionService); + } + return this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService); } private _onExtensionHostCrashOrExit(extensionHost: IExtensionHostManager, code: number, signal: string | null): void { @@ -781,7 +783,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } protected _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void { - console.error(`Extension host (${extensionHostKindToString(extensionHost.kind)}) terminated unexpectedly. Code: ${code}, Signal: ${signal}`); + console.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. Code: ${code}, Signal: ${signal}`); if (extensionHost.kind === ExtensionHostKind.LocalProcess) { this._doStopExtensionHosts(); } else if (extensionHost.kind === ExtensionHostKind.Remote) { @@ -817,7 +819,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx try { const info = await this._getExtensionHostExitInfoWithTimeout(reconnectionToken); if (info) { - this._logService.error(`Extension host (${extensionHostKindToString(extensionHost.kind)}) terminated unexpectedly with code ${info.code}.`); + this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly with code ${info.code}.`); } this._logExtensionHostCrash(extensionHost); @@ -852,9 +854,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } if (activatedExtensions.length > 0) { - this._logService.error(`Extension host (${extensionHostKindToString(extensionHost.kind)}) terminated unexpectedly. The following extensions were running: ${activatedExtensions.map(id => id.value).join(', ')}`); + this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. The following extensions were running: ${activatedExtensions.map(id => id.value).join(', ')}`); } else { - this._logService.error(`Extension host (${extensionHostKindToString(extensionHost.kind)}) terminated unexpectedly. No extensions were activated.`); + this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. No extensions were activated.`); } } diff --git a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts index 96b940c9ef0d7..c25c9a7ca4798 100644 --- a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts @@ -79,18 +79,11 @@ export class ExtensionDescriptionRegistry implements IReadOnlyExtensionDescripti this._extensionsArr.push(extensionDescription); const activationEvents = this._activationEventsReader.readActivationEvents(extensionDescription); - if (Array.isArray(activationEvents)) { - for (let activationEvent of activationEvents) { - // TODO@joao: there's no easy way to contribute this - if (activationEvent === 'onUri') { - activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; - } - - if (!this._activationMap.has(activationEvent)) { - this._activationMap.set(activationEvent, []); - } - this._activationMap.get(activationEvent)!.push(extensionDescription); + for (const activationEvent of activationEvents) { + if (!this._activationMap.has(activationEvent)) { + this._activationMap.set(activationEvent, []); } + this._activationMap.get(activationEvent)!.push(extensionDescription); } } } @@ -261,15 +254,9 @@ export class ExtensionDescriptionRegistrySnapshot { } export interface IActivationEventsReader { - readActivationEvents(extensionDescription: IExtensionDescription): string[] | undefined; + readActivationEvents(extensionDescription: IExtensionDescription): string[]; } -export const basicActivationEventsReader: IActivationEventsReader = { - readActivationEvents: (extensionDescription: IExtensionDescription): string[] | undefined => { - return extensionDescription.activationEvents; - } -}; - export class LockableExtensionDescriptionRegistry implements IReadOnlyExtensionDescriptionRegistry { private readonly _actual: ExtensionDescriptionRegistry; diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index 0980a41d10c07..4946a0219a63c 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Barrier, IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -23,10 +23,11 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ExtHostCustomersRegistry, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { ExtensionHostKind, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensionHostKind'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; -import { ActivationKind, ExtensionActivationReason, ExtensionHostExtensions, ExtensionHostStartup, IExtensionHost, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationKind, ExtensionActivationReason, ExtensionHostStartup, IExtensionHost, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Proxied, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; @@ -34,37 +35,6 @@ import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } fr const LOG_EXTENSION_HOST_COMMUNICATION = false; const LOG_USE_COLORS = true; -export interface IExtensionHostManager { - readonly kind: ExtensionHostKind; - readonly startup: ExtensionHostStartup; - readonly onDidExit: Event<[number, string | null]>; - readonly onDidChangeResponsiveState: Event; - dispose(): void; - ready(): Promise; - representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean; - deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise; - containsExtension(extensionId: ExtensionIdentifier): boolean; - activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; - activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; - activationEventIsDone(activationEvent: string): boolean; - getInspectPort(tryEnableInspector: boolean): Promise; - resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; - /** - * Returns `null` if no resolver for `remoteAuthority` is found. - */ - getCanonicalURI(remoteAuthority: string, uri: URI): Promise; - start(extensionRegistryVersionId: number, allExtensions: readonly IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise; - extensionTestsExecute(): Promise; - setRemoteEnvironment(env: { [key: string]: string | null }): Promise; -} - -export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHost: IExtensionHost, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager { - if (extensionHost.startup === ExtensionHostStartup.Lazy && initialActivationEvents.length === 0) { - return instantiationService.createInstance(LazyCreateExtensionHostManager, extensionHost, internalExtensionService); - } - return instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService); -} - type ExtensionHostStartupClassification = { owner: 'alexdima'; comment: 'The startup state of the extension host'; @@ -85,7 +55,7 @@ type ExtensionHostStartupEvent = { errorStack?: string; }; -class ExtensionHostManager extends Disposable implements IExtensionHostManager { +export class ExtensionHostManager extends Disposable implements IExtensionHostManager { public readonly onDidExit: Event<[number, string | null]>; @@ -103,6 +73,10 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { private _proxy: Promise | null; private _hasStarted = false; + public get pid(): number | null { + return this._extensionHost.pid; + } + public get kind(): ExtensionHostKind { return this._extensionHost.runningLocation.kind; } @@ -111,6 +85,10 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { return this._extensionHost.startup; } + public get friendyName(): string { + return friendlyExtHostName(this.kind, this.pid); + } + constructor( extensionHost: IExtensionHost, initialActivationEvents: string[], @@ -366,6 +344,12 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { // i.e. the extension host could not be started return; } + + if (!this._extensionHost.extensions!.containsActivationEvent(activationEvent)) { + this._resolvedActivationEvents.add(activationEvent); + return; + } + await proxy.activateByEvent(activationEvent, activationKind); this._resolvedActivationEvents.add(activationEvent); } @@ -484,163 +468,11 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { } } -/** - * Waits until `start()` and only if it has extensions proceeds to really start. - */ -class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager { - - public readonly onDidExit: Event<[number, string | null]>; - private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); - public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; - - private readonly _extensionHost: IExtensionHost; - private _startCalled: Barrier; - private _actual: ExtensionHostManager | null; - private _lazyStartExtensions: ExtensionHostExtensions | null; - - public get kind(): ExtensionHostKind { - return this._extensionHost.runningLocation.kind; - } - - public get startup(): ExtensionHostStartup { - return this._extensionHost.startup; - } - - constructor( - extensionHost: IExtensionHost, - private readonly _internalExtensionService: IInternalExtensionService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ILogService private readonly _logService: ILogService, - ) { - super(); - this._extensionHost = extensionHost; - this.onDidExit = extensionHost.onExit; - this._startCalled = new Barrier(); - this._actual = null; - this._lazyStartExtensions = null; - } - - private _createActual(reason: string): ExtensionHostManager { - this._logService.info(`Creating lazy extension host: ${reason}`); - this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, [], this._internalExtensionService)); - this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e))); - return this._actual; - } - - private async _getOrCreateActualAndStart(reason: string): Promise { - if (this._actual) { - // already created/started - return this._actual; - } - const actual = this._createActual(reason); - await actual.start(this._lazyStartExtensions!.versionId, this._lazyStartExtensions!.allExtensions, this._lazyStartExtensions!.myExtensions); - return actual; - } - - public async ready(): Promise { - await this._startCalled.wait(); - if (this._actual) { - await this._actual.ready(); - } - } - public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean { - return this._extensionHost.runningLocation.equals(runningLocation); - } - public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.deltaExtensions(extensionsDelta); - } - this._lazyStartExtensions!.delta(extensionsDelta); - if (extensionsDelta.myToAdd.length > 0) { - const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`); - await actual.start(this._lazyStartExtensions!.versionId, this._lazyStartExtensions!.allExtensions, this._lazyStartExtensions!.myExtensions); - return; - } - } - public containsExtension(extensionId: ExtensionIdentifier): boolean { - return this._extensionHost.extensions?.containsExtension(extensionId) ?? false; - } - public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.activate(extension, reason); - } - return false; - } - public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { - if (activationKind === ActivationKind.Immediate) { - // this is an immediate request, so we cannot wait for start to be called - if (this._actual) { - return this._actual.activateByEvent(activationEvent, activationKind); - } - return; - } - await this._startCalled.wait(); - if (this._actual) { - return this._actual.activateByEvent(activationEvent, activationKind); - } - } - public activationEventIsDone(activationEvent: string): boolean { - if (!this._startCalled.isOpen()) { - return false; - } - if (this._actual) { - return this._actual.activationEventIsDone(activationEvent); - } - return true; - } - public async getInspectPort(tryEnableInspector: boolean): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.getInspectPort(tryEnableInspector); - } - return 0; - } - public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.resolveAuthority(remoteAuthority, resolveAttempt); - } - return { - type: 'error', - error: { - message: `Cannot resolve authority`, - code: RemoteAuthorityResolverErrorCode.Unknown, - detail: undefined - } - }; - } - public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.getCanonicalURI(remoteAuthority, uri); - } - throw new Error(`Cannot resolve canonical URI`); - } - public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise { - if (myExtensions.length > 0) { - // there are actual extensions, so let's launch the extension host - const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`); - const result = actual.start(extensionRegistryVersionId, allExtensions, myExtensions); - this._startCalled.open(); - return result; - } - // there are no actual extensions running, store extensions in `this._lazyStartExtensions` - this._lazyStartExtensions = new ExtensionHostExtensions(extensionRegistryVersionId, allExtensions, myExtensions); - this._startCalled.open(); - } - public async extensionTestsExecute(): Promise { - await this._startCalled.wait(); - const actual = await this._getOrCreateActualAndStart(`execute tests.`); - return actual.extensionTestsExecute(); - } - public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { - await this._startCalled.wait(); - if (this._actual) { - return this._actual.setRemoteEnvironment(env); - } +export function friendlyExtHostName(kind: ExtensionHostKind, pid: number | null) { + if (pid) { + return `${extensionHostKindToString(kind)} pid: ${pid}`; } + return `${extensionHostKindToString(kind)}`; } const colorTables = [ diff --git a/src/vs/workbench/services/extensions/common/extensionHostManagers.ts b/src/vs/workbench/services/extensions/common/extensionHostManagers.ts new file mode 100644 index 0000000000000..8f094a1044f6d --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostManagers.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensionHostKind'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; +import { ActivationKind, ExtensionActivationReason, ExtensionHostStartup } from 'vs/workbench/services/extensions/common/extensions'; +import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; + +export interface IExtensionHostManager { + readonly pid: number | null; + readonly kind: ExtensionHostKind; + readonly startup: ExtensionHostStartup; + readonly friendyName: string; + readonly onDidExit: Event<[number, string | null]>; + readonly onDidChangeResponsiveState: Event; + dispose(): void; + ready(): Promise; + representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean; + deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise; + containsExtension(extensionId: ExtensionIdentifier): boolean; + activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; + activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; + activationEventIsDone(activationEvent: string): boolean; + getInspectPort(tryEnableInspector: boolean): Promise; + resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise; + /** + * Returns `null` if no resolver for `remoteAuthority` is found. + */ + getCanonicalURI(remoteAuthority: string, uri: URI): Promise; + start(extensionRegistryVersionId: number, allExtensions: readonly IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise; + extensionTestsExecute(): Promise; + setRemoteEnvironment(env: { [key: string]: string | null }): Promise; +} diff --git a/src/vs/workbench/services/extensions/common/extensionRunningLocationTracker.ts b/src/vs/workbench/services/extensions/common/extensionRunningLocationTracker.ts index 5b4229bfe56cf..53227e19fe363 100644 --- a/src/vs/workbench/services/extensions/common/extensionRunningLocationTracker.ts +++ b/src/vs/workbench/services/extensions/common/extensionRunningLocationTracker.ts @@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IReadOnlyExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, determineExtensionHostKinds } from 'vs/workbench/services/extensions/common/extensionHostKind'; -import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 65d59f165b580..7e59905e62c11 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -112,6 +112,7 @@ export const enum ExtensionHostStartup { } export interface IExtensionHost { + readonly pid: number | null; readonly runningLocation: ExtensionRunningLocation; readonly remoteAuthority: string | null; readonly startup: ExtensionHostStartup; @@ -133,6 +134,7 @@ export class ExtensionHostExtensions { private _versionId: number; private _allExtensions: IExtensionDescription[]; private _myExtensions: ExtensionIdentifier[]; + private _myActivationEvents: Set | null; public get versionId(): number { return this._versionId; @@ -150,6 +152,7 @@ export class ExtensionHostExtensions { this._versionId = versionId; this._allExtensions = allExtensions.slice(0); this._myExtensions = myExtensions.slice(0); + this._myActivationEvents = null; } toSnapshot(): IExtensionDescriptionSnapshot { @@ -260,6 +263,9 @@ export class ExtensionHostExtensions { this._myExtensions.push(extensionId); } + // clear cached activation events + this._myActivationEvents = null; + return extensionsDelta; } @@ -271,6 +277,30 @@ export class ExtensionHostExtensions { } return false; } + + public containsActivationEvent(activationEvent: string): boolean { + if (!this._myActivationEvents) { + this._myActivationEvents = this._readMyActivationEvents(); + } + return this._myActivationEvents.has(activationEvent); + } + + private _readMyActivationEvents(): Set { + const result = new Set(); + + for (const extensionDescription of this._allExtensions) { + if (!this.containsExtension(extensionDescription.identifier)) { + continue; + } + + const activationEvents = ImplicitActivationEvents.readActivationEvents(extensionDescription); + for (const activationEvent of activationEvents) { + result.add(activationEvent); + } + } + + return result; + } } function extensionDescriptionArrayToMap(extensions: IExtensionDescription[]): ExtensionIdentifierMap { diff --git a/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts b/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts new file mode 100644 index 0000000000000..eb23d8d45c9be --- /dev/null +++ b/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Barrier } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensionHostKind'; +import { ExtensionHostManager, friendlyExtHostName } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; +import { ActivationKind, ExtensionActivationReason, ExtensionHostExtensions, ExtensionHostStartup, IExtensionHost, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; + +/** + * Waits until `start()` and only if it has extensions proceeds to really start. + */ +export class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager { + + public readonly onDidExit: Event<[number, string | null]>; + private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); + public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; + + private readonly _extensionHost: IExtensionHost; + private _startCalled: Barrier; + private _actual: ExtensionHostManager | null; + private _lazyStartExtensions: ExtensionHostExtensions | null; + + public get pid(): number | null { + if (this._actual) { + return this._actual.pid; + } + return null; + } + + public get kind(): ExtensionHostKind { + return this._extensionHost.runningLocation.kind; + } + + public get startup(): ExtensionHostStartup { + return this._extensionHost.startup; + } + + public get friendyName(): string { + return friendlyExtHostName(this.kind, this.pid); + } + + constructor( + extensionHost: IExtensionHost, + private readonly _internalExtensionService: IInternalExtensionService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILogService private readonly _logService: ILogService + ) { + super(); + this._extensionHost = extensionHost; + this.onDidExit = extensionHost.onExit; + this._startCalled = new Barrier(); + this._actual = null; + this._lazyStartExtensions = null; + } + + private _createActual(reason: string): ExtensionHostManager { + this._logService.info(`Creating lazy extension host (${this.friendyName}). Reason: ${reason}`); + this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, [], this._internalExtensionService)); + this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e))); + return this._actual; + } + + private async _getOrCreateActualAndStart(reason: string): Promise { + if (this._actual) { + // already created/started + return this._actual; + } + const actual = this._createActual(reason); + await actual.start(this._lazyStartExtensions!.versionId, this._lazyStartExtensions!.allExtensions, this._lazyStartExtensions!.myExtensions); + return actual; + } + + public async ready(): Promise { + await this._startCalled.wait(); + if (this._actual) { + await this._actual.ready(); + } + } + public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean { + return this._extensionHost.runningLocation.equals(runningLocation); + } + public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.deltaExtensions(extensionsDelta); + } + this._lazyStartExtensions!.delta(extensionsDelta); + if (extensionsDelta.myToAdd.length > 0) { + const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`); + await actual.start(this._lazyStartExtensions!.versionId, this._lazyStartExtensions!.allExtensions, this._lazyStartExtensions!.myExtensions); + return; + } + } + public containsExtension(extensionId: ExtensionIdentifier): boolean { + return this._extensionHost.extensions?.containsExtension(extensionId) ?? false; + } + public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.activate(extension, reason); + } + return false; + } + public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise { + if (activationKind === ActivationKind.Immediate) { + // this is an immediate request, so we cannot wait for start to be called + if (this._actual) { + return this._actual.activateByEvent(activationEvent, activationKind); + } + return; + } + await this._startCalled.wait(); + if (this._actual) { + return this._actual.activateByEvent(activationEvent, activationKind); + } + } + public activationEventIsDone(activationEvent: string): boolean { + if (!this._startCalled.isOpen()) { + return false; + } + if (this._actual) { + return this._actual.activationEventIsDone(activationEvent); + } + return true; + } + public async getInspectPort(tryEnableInspector: boolean): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.getInspectPort(tryEnableInspector); + } + return 0; + } + public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.resolveAuthority(remoteAuthority, resolveAttempt); + } + return { + type: 'error', + error: { + message: `Cannot resolve authority`, + code: RemoteAuthorityResolverErrorCode.Unknown, + detail: undefined + } + }; + } + public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.getCanonicalURI(remoteAuthority, uri); + } + throw new Error(`Cannot resolve canonical URI`); + } + public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise { + if (myExtensions.length > 0) { + // there are actual extensions, so let's launch the extension host + const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`); + const result = actual.start(extensionRegistryVersionId, allExtensions, myExtensions); + this._startCalled.open(); + return result; + } + // there are no actual extensions running, store extensions in `this._lazyStartExtensions` + this._lazyStartExtensions = new ExtensionHostExtensions(extensionRegistryVersionId, allExtensions, myExtensions); + this._startCalled.open(); + } + public async extensionTestsExecute(): Promise { + await this._startCalled.wait(); + const actual = await this._getOrCreateActualAndStart(`execute tests.`); + return actual.extensionTestsExecute(); + } + public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + await this._startCalled.wait(); + if (this._actual) { + return this._actual.setRemoteEnvironment(env); + } + } +} diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index a4036f94bd2d4..9a60251454b71 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -45,6 +45,7 @@ export interface IRemoteExtensionHostDataProvider { export class RemoteExtensionHost extends Disposable implements IExtensionHost { + public readonly pid = null; public readonly remoteAuthority: string; public readonly startup = ExtensionHostStartup.EagerAutoStart; public extensions: ExtensionHostExtensions | null = null; diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index c635003cea311..54b3217b6e986 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -7,7 +7,7 @@ import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { removeDangerousEnvVariables } from 'vs/base/common/processes'; @@ -18,7 +18,6 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; import * as nls from 'vs/nls'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -75,7 +74,7 @@ export class ExtensionHostProcess { this._id = id; } - public start(opts: IExtensionHostProcessOptions): Promise { + public start(opts: IExtensionHostProcessOptions): Promise<{ pid: number | undefined }> { return this._extensionHostStarter.start(this._id, opts); } @@ -90,6 +89,7 @@ export class ExtensionHostProcess { export class NativeLocalProcessExtensionHost implements IExtensionHost { + public pid: number | null = null; public readonly remoteAuthority = null; public extensions: ExtensionHostExtensions | null = null; @@ -98,7 +98,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { private readonly _onDidSetInspectPort = new Emitter(); - protected readonly _toDispose = new DisposableStore(); + private readonly _toDispose = new DisposableStore(); private readonly _isExtensionDevHost: boolean; private readonly _isExtensionDevDebug: boolean; @@ -124,15 +124,14 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { @INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @ILogService protected readonly _logService: ILogService, - @ILoggerService protected readonly _loggerService: ILoggerService, + @ILogService private readonly _logService: ILogService, + @ILoggerService private readonly _loggerService: ILoggerService, @ILabelService private readonly _labelService: ILabelService, @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, @IHostService private readonly _hostService: IHostService, @IProductService private readonly _productService: IProductService, @IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService, - @IExtensionHostStarter protected readonly _extensionHostStarter: IExtensionHostStarter, - @IConfigurationService protected readonly _configurationService: IConfigurationService, + @IExtensionHostStarter private readonly _extensionHostStarter: IExtensionHostStarter, ) { const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; @@ -182,16 +181,9 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { return this._messageProtocol; } - protected async _start(): Promise { - const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); - return this._startWithCommunication(communication); - } - - protected async _startWithCommunication(communication: IExtHostCommunication): Promise { - - const [extensionHostCreationResult, communicationPreparedData, portNumber, processEnv] = await Promise.all([ + private async _start(): Promise { + const [extensionHostCreationResult, portNumber, processEnv] = await Promise.all([ this._extensionHostStarter.createExtensionHost(), - communication.prepare(), this._tryFindDebugPort(), this._shellEnvironmentService.getShellEnv(), ]); @@ -322,7 +314,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { } // Initialize extension host process with hand shakes - const protocol = await communication.establishProtocol(communicationPreparedData, this._extensionHostProcess, opts); + const protocol = await this._establishProtocol(this._extensionHostProcess, opts); await this._performHandshake(protocol); clearTimeout(startupTimeoutHandle); return protocol; @@ -358,6 +350,54 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { return port || 0; } + private _establishProtocol(extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { + + writeExtHostConnection(new MessagePortExtHostConnection(), opts.env); + + // Get ready to acquire the message port from the shared process worker + const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); + + return new Promise((resolve, reject) => { + + const handle = setTimeout(() => { + reject('The local extension host took longer than 60s to connect.'); + }, 60 * 1000); + + portPromise.then((port) => { + this._toDispose.add(toDisposable(() => { + // Close the message port when the extension host is disposed + port.close(); + })); + clearTimeout(handle); + + const onMessage = new BufferedEmitter(); + port.onmessage = ((e) => onMessage.fire(VSBuffer.wrap(e.data))); + port.start(); + + resolve({ + onMessage: onMessage.event, + send: message => port.postMessage(message.buffer), + }); + }); + + // Now that the message port listener is installed, start the ext host process + const sw = StopWatch.create(false); + extensionHostProcess.start(opts).then(({ pid }) => { + if (pid) { + this.pid = pid; + } + this._logService.info(`Started local extension host with pid ${pid}.`); + const duration = sw.elapsed(); + if (platform.isCI) { + this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); + } + }, (err) => { + // Starting the ext host process resulted in an error + reject(err); + }); + }); + } + private _performHandshake(protocol: IMessagePassingProtocol): Promise { // 1) wait for the incoming `ready` event and send the initialization data. // 2) wait for the incoming `initialized` event. @@ -540,64 +580,3 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { } } } - -export interface IExtHostCommunication { - prepare(): Promise; - establishProtocol(prepared: T, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise; -} - -export class ExtHostMessagePortCommunication extends Disposable implements IExtHostCommunication { - - constructor( - @ILogService private readonly _logService: ILogService - ) { - super(); - } - - async prepare(): Promise { - } - - establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { - - writeExtHostConnection(new MessagePortExtHostConnection(), opts.env); - - // Get ready to acquire the message port from the shared process worker - const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); - - return new Promise((resolve, reject) => { - - const handle = setTimeout(() => { - reject('The local extension host took longer than 60s to connect.'); - }, 60 * 1000); - - portPromise.then((port) => { - this._register(toDisposable(() => { - // Close the message port when the extension host is disposed - port.close(); - })); - clearTimeout(handle); - - const onMessage = new BufferedEmitter(); - port.onmessage = ((e) => onMessage.fire(VSBuffer.wrap(e.data))); - port.start(); - - resolve({ - onMessage: onMessage.event, - send: message => port.postMessage(message.buffer), - }); - }); - - // Now that the message port listener is installed, start the ext host process - const sw = StopWatch.create(false); - extensionHostProcess.start(opts).then(() => { - const duration = sw.elapsed(); - if (platform.isCI) { - this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); - } - }, (err) => { - // Starting the ext host process resulted in an error - reject(err); - }); - }); - } -} diff --git a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts index c5adae5f0d249..f6a4677fb9c0d 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts @@ -29,7 +29,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IRemoteAuthorityResolverService, RemoteConnectionType, RemoteAuthorityResolverError, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteConnectionType, ResolverResult, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteExtensionsScannerService } from 'vs/platform/remote/common/remoteExtensionsScanner'; import { getRemoteName, parseAuthorityWithPort } from 'vs/platform/remote/common/remoteHosts'; import { updateProxyConfigurationsScope } from 'vs/platform/request/common/request'; @@ -43,7 +43,7 @@ import { AbstractExtensionService, ExtensionHostCrashTracker, IExtensionHostFact import { ExtensionDescriptionRegistrySnapshot } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, extensionHostKindToString, extensionRunningPreferenceToString } from 'vs/workbench/services/extensions/common/extensionHostKind'; -import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts index 7bb517785af99..846822893134e 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts @@ -36,7 +36,7 @@ import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWo import { BrowserExtensionHostKindPicker } from 'vs/workbench/services/extensions/browser/extensionService'; import { AbstractExtensionService, IExtensionHostFactory, ResolvedExtensions } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import { ExtensionHostKind, ExtensionRunningPreference } from 'vs/workbench/services/extensions/common/extensionHostKind'; -import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManagers'; import { ExtensionManifestPropertiesService, IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensionRunningLocation'; import { ExtensionRunningLocationTracker } from 'vs/workbench/services/extensions/common/extensionRunningLocationTracker'; diff --git a/src/vs/workbench/services/extensions/test/common/extensionDescriptionRegistry.test.ts b/src/vs/workbench/services/extensions/test/common/extensionDescriptionRegistry.test.ts index d4fb4a117539c..658072d31d8dc 100644 --- a/src/vs/workbench/services/extensions/test/common/extensionDescriptionRegistry.test.ts +++ b/src/vs/workbench/services/extensions/test/common/extensionDescriptionRegistry.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions'; -import { ExtensionDescriptionRegistry, basicActivationEventsReader } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { ExtensionDescriptionRegistry, IActivationEventsReader } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; suite('ExtensionDescriptionRegistry', () => { test('allow removing and adding the same extension at a different version', () => { @@ -14,6 +14,12 @@ suite('ExtensionDescriptionRegistry', () => { const extensionA1 = desc(idA, '1.0.0'); const extensionA2 = desc(idA, '2.0.0'); + const basicActivationEventsReader: IActivationEventsReader = { + readActivationEvents: (extensionDescription: IExtensionDescription): string[] => { + return extensionDescription.activationEvents ?? []; + } + }; + const registry = new ExtensionDescriptionRegistry(basicActivationEventsReader, [extensionA1]); registry.deltaExtensions([extensionA2], [idA]); diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index 576bad14363c3..6a16dd10210cb 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,19 +4,19 @@