Merge pull request #144840 from microsoft/alex/multiple-local-extension-hosts

Run multiple local extension hosts
This commit is contained in:
Alexandru Dima 2022-03-11 23:12:30 +01:00 committed by GitHub
commit 5f5e1ca791
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 819 additions and 416 deletions

View file

@ -197,9 +197,9 @@ class ExtensionHostProxy implements IExtensionHostProxy {
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
return this._actual.$resolveAuthority(remoteAuthority, resolveAttempt);
}
async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri);
return URI.revive(uriComponents);
return (uriComponents ? URI.revive(uriComponents) : uriComponents);
}
startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
return this._actual.$startExtensionHost(enabledExtensionIds);

View file

@ -1339,7 +1339,10 @@ export interface ExtHostSearchShape {
export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents | null>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$extensionTestsExecute(): Promise<number>;
$extensionTestsExit(code: number): Promise<void>;

View file

@ -753,12 +753,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
}
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents> {
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {
this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);
const { authorityPrefix, resolver } = await this._activateAndGetResolver(remoteAuthority);
const { resolver } = await this._activateAndGetResolver(remoteAuthority);
if (!resolver) {
throw new Error(`Cannot get canonical URI because no remote extension is installed to resolve ${authorityPrefix}`);
// Return `null` if no resolver for `remoteAuthority` is found.
return null;
}
const uri = URI.revive(uriComponents);

View file

@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile, ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { append, $, Dimension, clearNode, addDisposableListener } from 'vs/base/browser/dom';
@ -36,6 +36,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput';
import { Action2, MenuId } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
interface IExtensionProfileInformation {
/**
@ -80,6 +81,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
@IStorageService storageService: IStorageService,
@ILabelService private readonly _labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IClipboardService private readonly _clipboardService: IClipboardService,
) {
super(AbstractRuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
@ -370,7 +372,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
}
let extraLabel: string | null = null;
if (element.status.runningLocation === ExtensionRunningLocation.LocalWebWorker) {
if (element.status.runningLocation && element.status.runningLocation.equals(new LocalWebWorkerRunningLocation())) {
extraLabel = `$(globe) web worker`;
} else if (element.description.extensionLocation.scheme === Schemas.vscodeRemote) {
const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority);
@ -379,6 +381,8 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
} else {
extraLabel = `$(remote) ${element.description.extensionLocation.authority}`;
}
} else if (element.status.runningLocation && element.status.runningLocation.affinity > 0) {
extraLabel = `$(server-process) local process ${element.status.runningLocation.affinity + 1}`;
}
if (extraLabel) {
@ -427,11 +431,21 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
const actions: IAction[] = [];
actions.push(new Action(
'runtimeExtensionsEditor.action.copyId',
nls.localize('copy id', "Copy id ({0})", e.element!.description.identifier.value),
undefined,
true,
() => {
this._clipboardService.writeText(e.element!.description.identifier.value);
}
));
const reportExtensionIssueAction = this._createReportExtensionIssueAction(e.element);
if (reportExtensionIssueAction) {
actions.push(reportExtensionIssueAction);
actions.push(new Separator());
}
actions.push(new Separator());
if (e.element!.marketplaceInfo) {
actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo!, EnablementState.DisabledWorkspace)));

View file

@ -4,38 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor';
export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
) {
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService);
}
protected _getProfileInfo(): IExtensionHostProfile | null {
return null;
}

View file

@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { IProductService } from 'vs/platform/product/common/productService';
import { Action } from 'vs/base/common/actions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
@ -29,8 +29,8 @@ export class DebugExtensionHostAction extends Action {
override async run(): Promise<any> {
const inspectPort = await this._extensionService.getInspectPort(false);
if (!inspectPort) {
const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false);
if (inspectPorts.length === 0) {
const res = await this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@ -45,11 +45,16 @@ export class DebugExtensionHostAction extends Action {
return;
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`);
}
return this._debugService.startDebugging(undefined, {
type: 'node',
name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"),
request: 'attach',
port: inspectPort
port: inspectPorts[0]
});
}
}

View file

@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHostProfile, ProfileSession, IExtensionService, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar';
@ -116,8 +116,9 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
return null;
}
const inspectPort = await this._extensionService.getInspectPort(true);
if (!inspectPort) {
const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, true);
if (inspectPorts.length === 0) {
return this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@ -131,9 +132,14 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
});
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for profiling. Picking the first one...`);
}
this._setState(ProfileSessionState.Starting);
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPort).start().then((value) => {
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPorts[0]).start().then((value) => {
this._profileSession = value;
this._setState(ProfileSessionState.Running);
}, (err) => {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
@ -46,8 +46,11 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise<void> {
if (event.extensionHostKind !== ExtensionHostKind.LocalProcess) {
return;
}
const port = await this._extensionService.getInspectPort(true);
const port = await this._extensionService.getInspectPort(event.extensionHostId, true);
if (!port) {
return;

View file

@ -26,6 +26,7 @@ import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IV8Profile, Utils } from 'vs/platform/profiling/common/profiling';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
@ -72,9 +73,10 @@ export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
@IStorageService storageService: IStorageService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IClipboardService clipboardService: IClipboardService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService);
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService, clipboardService);
this._profileInfo = this._extensionHostProfileService.lastProfile;
this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService);
this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService);

View file

@ -9,14 +9,14 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionService, IExtensionHost, toExtensionDescription, ExtensionRunningLocation, extensionRunningLocationToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionHost, toExtensionDescription, ExtensionRunningLocation, ExtensionHostKind, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IProductService } from 'vs/platform/product/common/productService';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { RemoteExtensionHost, IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWebWorkerExtensionHostDataProvider, WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier, IExtensionDescription, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
@ -72,8 +72,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
logService
);
this._runningLocation = new Map<string, ExtensionRunningLocation>();
// Initialize installed extensions first and do it only after workbench is ready
this._lifecycleService.when(LifecyclePhase.Ready).then(async () => {
await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService);
@ -107,11 +105,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._disposables.add(this._fileService.registerProvider(Schemas.https, provider));
}
private _createLocalExtensionHostDataProvider() {
private _createLocalExtensionHostDataProvider(desiredRunningLocation: ExtensionRunningLocation): IWebWorkerExtensionHostDataProvider {
return {
getInitData: async () => {
const allExtensions = await this.getExtensions();
const localWebWorkerExtensions = filterByRunningLocation(allExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
const localWebWorkerExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation);
return {
autoStart: true,
extensions: localWebWorkerExtensions
@ -130,20 +128,20 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
protected _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
protected _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result = ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionRunningLocationToString(result)}`);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
return result;
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result: ExtensionHostKind[] = [];
let canRunRemotely = false;
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledRemotely) {
// ui extensions run remotely if possible (but only as a last resort)
if (preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
canRunRemotely = true;
}
@ -151,39 +149,42 @@ export class ExtensionService extends AbstractExtensionService implements IExten
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
}
if (extensionKind === 'web' && (isInstalledLocally || isInstalledRemotely)) {
// web worker extensions run in the local web worker if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
return ExtensionHostKind.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
result.push(ExtensionHostKind.LocalWebWorker);
}
}
}
if (canRunRemotely) {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
return (result.length > 0 ? result[0] : null);
}
protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, false, this._createLocalExtensionHostDataProvider());
result.push(webWorkerExtHost);
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
result.push(remoteExtHost);
protected _createExtensionHost(runningLocation: ExtensionRunningLocation, _isInitialStart: boolean): IExtensionHost | null {
switch (runningLocation.kind) {
case ExtensionHostKind.LocalProcess: {
return null;
}
case ExtensionHostKind.LocalWebWorker: {
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, false, this._createLocalExtensionHostDataProvider(runningLocation));
}
case ExtensionHostKind.Remote: {
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
}
return null;
}
}
return result;
}
protected async _scanAndHandleExtensions(): Promise<void> {
@ -199,12 +200,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const remoteAgentConnection = this._remoteAgentService.getConnection();
// `determineRunningLocation` will look at the complete picture (e.g. an extension installed on both sides),
// takes care of duplicates and picks a running location for each extension
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
this._initializeRunningLocation(localExtensions, remoteExtensions);
// Some remote extensions could run locally in the web worker, so store them
const remoteExtensionsThatNeedToRunLocally = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
localExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
const remoteExtensionsThatNeedToRunLocally = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.LocalWebWorker);
localExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);
remoteExtensions = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);
// Add locally the remote extensions that need to run locally in the web worker
for (const ext of remoteExtensionsThatNeedToRunLocally) {
@ -246,10 +247,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}
function includes(extensions: IExtensionDescription[], identifier: ExtensionIdentifier): boolean {
for (const extension of extensions) {
if (ExtensionIdentifier.equals(extension.identifier, identifier)) {

View file

@ -16,7 +16,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import * as platform from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
import { URI } from 'vs/base/common/uri';
import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IProductService } from 'vs/platform/product/common/productService';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { joinPath } from 'vs/base/common/resources';
@ -30,6 +30,7 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { FileAccess } from 'vs/base/common/network';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { parentOriginHash } from 'vs/workbench/browser/webview';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export interface IWebWorkerExtensionHostInitData {
readonly autoStart: boolean;
@ -42,9 +43,9 @@ export interface IWebWorkerExtensionHostDataProvider {
export class WebWorkerExtensionHost extends Disposable implements IExtensionHost {
public readonly kind = ExtensionHostKind.LocalWebWorker;
public readonly remoteAuthority = null;
public readonly lazyStart: boolean;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private readonly _onDidExit = this._register(new Emitter<[number, string | null]>());
public readonly onExit: Event<[number, string | null]> = this._onDidExit.event;
@ -57,6 +58,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
private readonly _extensionHostLogFile: URI;
constructor(
public readonly runningLocation: LocalWebWorkerRunningLocation,
lazyStart: boolean,
private readonly _initDataProvider: IWebWorkerExtensionHostDataProvider,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ -265,6 +267,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
private async _createExtHostInitData(): Promise<IExtensionHostInitData> {
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(initData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -288,7 +291,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
},
resolvedExtensions: [],
hostExtensions: [],
extensions: initData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: this._extensionHostLogsLocation,

View file

@ -15,7 +15,7 @@ import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } fr
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, RemoteRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
@ -132,29 +132,28 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
public _serviceBrand: undefined;
protected readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;
protected readonly _onDidChangeExtensionsStatus: Emitter<ExtensionIdentifier[]> = this._register(new Emitter<ExtensionIdentifier[]>());
private readonly _onDidChangeExtensionsStatus: Emitter<ExtensionIdentifier[]> = this._register(new Emitter<ExtensionIdentifier[]>());
public readonly onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]> = this._onDidChangeExtensionsStatus.event;
protected readonly _onDidChangeExtensions: Emitter<void> = this._register(new Emitter<void>({ leakWarningThreshold: 400 }));
private readonly _onDidChangeExtensions: Emitter<void> = this._register(new Emitter<void>({ leakWarningThreshold: 400 }));
public readonly onDidChangeExtensions: Event<void> = this._onDidChangeExtensions.event;
protected readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
private readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
public readonly onWillActivateByEvent: Event<IWillActivateEvent> = this._onWillActivateByEvent.event;
protected readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
private readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
public readonly onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = this._onDidChangeResponsiveChange.event;
protected readonly _runningLocationClassifier: ExtensionRunningLocationClassifier;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _registryLock: Lock;
private readonly _installedExtensionsReady: Barrier;
protected readonly _isDev: boolean;
private readonly _isDev: boolean;
private readonly _extensionsMessages: Map<string, IMessage[]>;
protected readonly _allRequestedActivateEvents = new Set<string>();
private readonly _allRequestedActivateEvents = new Set<string>();
private readonly _proposedApiController: ProposedApiController;
private readonly _isExtensionDevHost: boolean;
protected readonly _isExtensionDevTestFromCli: boolean;
@ -162,10 +161,12 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
private _inHandleDeltaExtensions: boolean;
protected _runningLocation: Map<string, ExtensionRunningLocation>;
private _runningLocation: Map<string, ExtensionRunningLocation | null>;
private _lastExtensionHostId: number = 0;
private _maxLocalProcessAffinity: number = 0;
// --- Members used per extension host process
protected _extensionHostManagers: IExtensionHostManager[];
private _extensionHostManagers: IExtensionHostManager[];
protected _extensionHostActiveExtensions: Map<string, ExtensionIdentifier>;
private _extensionHostActivationTimes: Map<string, ActivationTimes>;
private _extensionHostExtensionRuntimeErrors: Map<string, Error[]>;
@ -187,11 +188,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
) {
super();
this._runningLocationClassifier = new ExtensionRunningLocationClassifier(
(extension) => this._getExtensionKind(extension),
(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._pickRunningLocation(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
);
// help the file service to activate providers by activating extensions by file system event
this._register(this._fileService.onWillActivateFileSystemProvider(e => {
if (e.scheme !== Schemas.vscodeRemote) {
@ -264,17 +260,247 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return this._extensionManifestPropertiesService.getExtensionKind(extensionDescription);
}
protected abstract _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation;
protected abstract _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null;
protected _getExtensionHostManager(kind: ExtensionHostKind): IExtensionHostManager | null {
protected _getExtensionHostManagers(kind: ExtensionHostKind): IExtensionHostManager[] {
return this._extensionHostManagers.filter(extHostManager => extHostManager.kind === kind);
}
protected _getExtensionHostManagerByRunningLocation(runningLocation: ExtensionRunningLocation): IExtensionHostManager | null {
for (const extensionHostManager of this._extensionHostManagers) {
if (extensionHostManager.kind === kind) {
if (extensionHostManager.representsRunningLocation(runningLocation)) {
return extensionHostManager;
}
}
return null;
}
//#region running location
private _computeAffinity(inputExtensions: IExtensionDescription[], extensionHostKind: ExtensionHostKind, isInitialAllocation: boolean): { affinities: Map<string, number>; maxAffinity: number } {
// Only analyze extensions that can execute
const extensions = new Map<string, IExtensionDescription>();
for (const extension of inputExtensions) {
if (extension.main || extension.browser) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
}
// Also add existing extensions of the same kind that can execute
for (const extension of this._registry.getAllExtensionDescriptions()) {
if (extension.main || extension.browser) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation && runningLocation.kind === extensionHostKind) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
}
}
// Initially, each extension belongs to its own group
const groups = new Map<string, number>();
let groupNumber = 0;
for (const [_, extension] of extensions) {
groups.set(ExtensionIdentifier.toKey(extension.identifier), ++groupNumber);
}
const changeGroup = (from: number, to: number) => {
for (const [key, group] of groups) {
if (group === from) {
groups.set(key, to);
}
}
};
// We will group things together when there are dependencies
for (const [_, extension] of extensions) {
if (!extension.extensionDependencies) {
continue;
}
const myGroup = groups.get(ExtensionIdentifier.toKey(extension.identifier))!;
for (const depId of extension.extensionDependencies) {
const depGroup = groups.get(ExtensionIdentifier.toKey(depId));
if (!depGroup) {
// probably can't execute, so it has no impact
continue;
}
if (depGroup === myGroup) {
// already in the same group
continue;
}
changeGroup(depGroup, myGroup);
}
}
// Initialize with existing affinities
const resultingAffinities = new Map<number, number>();
let lastAffinity = 0;
for (const [_, extension] of extensions) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation) {
const group = groups.get(ExtensionIdentifier.toKey(extension.identifier))!;
resultingAffinities.set(group, runningLocation.affinity);
lastAffinity = Math.max(lastAffinity, runningLocation.affinity);
}
}
// Go through each configured affinity and try to accomodate it
const configuredAffinities = this._configurationService.getValue<{ [extensionId: string]: number } | undefined>('extensions.experimental.affinity') || {};
const configuredExtensionIds = Object.keys(configuredAffinities);
const configuredAffinityToResultingAffinity = new Map<number, number>();
for (const extensionId of configuredExtensionIds) {
const configuredAffinity = configuredAffinities[extensionId];
if (typeof configuredAffinity !== 'number' || configuredAffinity <= 0 || Math.floor(configuredAffinity) !== configuredAffinity) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because the value is not a positive integer.`);
continue;
}
const group = groups.get(ExtensionIdentifier.toKey(extensionId));
if (!group) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because the extension is unknown or cannot execute.`);
continue;
}
const affinity1 = resultingAffinities.get(group);
if (affinity1) {
// Affinity for this group is already established
configuredAffinityToResultingAffinity.set(configuredAffinity, affinity1);
continue;
}
const affinity2 = configuredAffinityToResultingAffinity.get(configuredAffinity);
if (affinity2) {
// Affinity for this configuration is already established
resultingAffinities.set(group, affinity2);
continue;
}
if (!isInitialAllocation) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because extension host(s) are already running. Reload window.`);
continue;
}
const affinity3 = ++lastAffinity;
configuredAffinityToResultingAffinity.set(configuredAffinity, affinity3);
resultingAffinities.set(group, affinity3);
}
const result = new Map<string, number>();
for (const extension of inputExtensions) {
const group = groups.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
const affinity = resultingAffinities.get(group) || 0;
result.set(ExtensionIdentifier.toKey(extension.identifier), affinity);
}
if (lastAffinity > 0 && isInitialAllocation) {
for (let affinity = 1; affinity <= lastAffinity; affinity++) {
const extensionIds: ExtensionIdentifier[] = [];
for (const extension of inputExtensions) {
if (result.get(ExtensionIdentifier.toKey(extension.identifier)) === affinity) {
extensionIds.push(extension.identifier);
}
}
this._logService.info(`Placing extension(s) ${extensionIds.map(e => e.value).join(', ')} on a separate extension host.`);
}
}
return { affinities: result, maxAffinity: lastAffinity };
}
private _computeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): { runningLocation: Map<string, ExtensionRunningLocation | null>; maxLocalProcessAffinity: number } {
const extensionHostKinds = ExtensionHostKindClassifier.determineExtensionHostKinds(
localExtensions,
remoteExtensions,
(extension) => this._getExtensionKind(extension),
(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._pickExtensionHostKind(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
);
const extensions = new Map<string, IExtensionDescription>();
for (const extension of localExtensions) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
for (const extension of remoteExtensions) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
const result = new Map<string, ExtensionRunningLocation | null>();
const localProcessExtensions: IExtensionDescription[] = [];
for (const [extensionIdKey, extensionHostKind] of extensionHostKinds) {
let runningLocation: ExtensionRunningLocation | null = null;
if (extensionHostKind === ExtensionHostKind.LocalProcess) {
const extensionDescription = extensions.get(ExtensionIdentifier.toKey(extensionIdKey));
if (extensionDescription) {
localProcessExtensions.push(extensionDescription);
}
} else if (extensionHostKind === ExtensionHostKind.LocalWebWorker) {
runningLocation = new LocalWebWorkerRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
}
result.set(extensionIdKey, runningLocation);
}
const { affinities, maxAffinity } = this._computeAffinity(localProcessExtensions, ExtensionHostKind.LocalProcess, isInitialAllocation);
for (const extension of localProcessExtensions) {
const affinity = affinities.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
result.set(ExtensionIdentifier.toKey(extension.identifier), new LocalProcessRunningLocation(affinity));
}
return { runningLocation: result, maxLocalProcessAffinity: maxAffinity };
}
protected _determineRunningLocation(localExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation | null> {
return this._computeRunningLocation(localExtensions, [], false).runningLocation;
}
protected _initializeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): void {
const { runningLocation, maxLocalProcessAffinity } = this._computeRunningLocation(localExtensions, remoteExtensions, true);
this._runningLocation = runningLocation;
this._maxLocalProcessAffinity = maxLocalProcessAffinity;
this._startExtensionHostsIfNecessary(true, []);
}
/**
* Update `this._runningLocation` with running locations for newly enabled/installed extensions.
*/
private _updateRunningLocationForAddedExtensions(toAdd: IExtensionDescription[]): void {
// Determine new running location
const localProcessExtensions: IExtensionDescription[] = [];
for (const extension of toAdd) {
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const extensionHostKind = this._pickExtensionHostKind(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
let runningLocation: ExtensionRunningLocation | null = null;
if (extensionHostKind === ExtensionHostKind.LocalProcess) {
localProcessExtensions.push(extension);
} else if (extensionHostKind === ExtensionHostKind.LocalWebWorker) {
runningLocation = new LocalWebWorkerRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
}
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
const { affinities } = this._computeAffinity(localProcessExtensions, ExtensionHostKind.LocalProcess, false);
for (const extension of localProcessExtensions) {
const affinity = affinities.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), new LocalProcessRunningLocation(affinity));
}
}
protected _filterByRunningLocation(extensions: IExtensionDescription[], desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return filterByRunningLocation(extensions, this._runningLocation, desiredRunningLocation);
}
protected _filterByExtensionHostKind(extensions: IExtensionDescription[], desiredExtensionHostKind: ExtensionHostKind): IExtensionDescription[] {
return filterByExtensionHostKind(extensions, this._runningLocation, desiredExtensionHostKind);
}
protected _filterByExtensionHostManager(extensions: IExtensionDescription[], extensionHostManager: IExtensionHostManager): IExtensionDescription[] {
return filterByExtensionHostManager(extensions, this._runningLocation, extensionHostManager);
}
//#endregion
//#region deltaExtensions
private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {
@ -374,47 +600,32 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
private async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const groupedToRemove: ExtensionIdentifier[][] = [];
const groupRemove = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToRemove[extensionHostKind] = filterByRunningLocation(toRemove, extId => extId, this._runningLocation, extensionRunningLocation);
};
groupRemove(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupRemove(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupRemove(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
// Remove old running location
const removedRunningLocation = new Map<string, ExtensionRunningLocation | null>();
for (const extensionId of toRemove) {
this._runningLocation.delete(ExtensionIdentifier.toKey(extensionId));
const extensionKey = ExtensionIdentifier.toKey(extensionId);
removedRunningLocation.set(extensionKey, this._runningLocation.get(extensionKey) || null);
this._runningLocation.delete(extensionKey);
}
const groupedToAdd: IExtensionDescription[][] = [];
const groupAdd = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToAdd[extensionHostKind] = filterByRunningLocation(toAdd, ext => ext.identifier, this._runningLocation, extensionRunningLocation);
};
for (const extension of toAdd) {
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._pickRunningLocation(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
groupAdd(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupAdd(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupAdd(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
const promises: Promise<void>[] = [];
for (const extensionHostKind of [ExtensionHostKind.LocalProcess, ExtensionHostKind.LocalWebWorker, ExtensionHostKind.Remote]) {
const toAdd = groupedToAdd[extensionHostKind];
const toRemove = groupedToRemove[extensionHostKind];
if (toAdd.length > 0 || toRemove.length > 0) {
const extensionHostManager = this._getExtensionHostManager(extensionHostKind);
if (extensionHostManager) {
promises.push(extensionHostManager.deltaExtensions(toAdd, toRemove));
}
}
}
// Determine new running location
this._updateRunningLocationForAddedExtensions(toAdd);
const promises = this._extensionHostManagers.map(
extHostManager => this._updateExtensionsOnExtHost(extHostManager, toAdd, toRemove, removedRunningLocation)
);
await Promise.all(promises);
}
private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, _toAdd: IExtensionDescription[], _toRemove: ExtensionIdentifier[], removedRunningLocation: Map<string, ExtensionRunningLocation | null>): Promise<void> {
const toAdd = filterByExtensionHostManager(_toAdd, this._runningLocation, extensionHostManager);
const toRemove = _filterByExtensionHostManager(_toRemove, extId => extId, removedRunningLocation, extensionHostManager);
if (toRemove.length > 0 || toAdd.length > 0) {
await extensionHostManager.deltaExtensions(toAdd, toRemove);
}
}
public canAddExtension(extension: IExtensionDescription): boolean {
const existing = this._registry.getExtensionDescription(extension.identifier);
if (existing) {
@ -429,8 +640,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._pickRunningLocation(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
if (runningLocation === ExtensionRunningLocation.None) {
const extensionHostKind = this._pickExtensionHostKind(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
if (extensionHostKind === null) {
return false;
}
@ -518,7 +729,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
protected async _initialize(): Promise<void> {
perf.mark('code/willLoadExtensions');
this._startExtensionHosts(true, []);
this._startExtensionHostsIfNecessary(true, []);
const lock = await this._registryLock.acquire('_initialize');
try {
@ -558,38 +769,31 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._onExtensionHostExit(exitCode);
}
private findTestExtensionHost(testLocation: URI): IExtensionHostManager | undefined | null {
let extensionHostKind: ExtensionHostKind | undefined;
private findTestExtensionHost(testLocation: URI): IExtensionHostManager | null {
let runningLocation: ExtensionRunningLocation | null = null;
for (const extension of this._registry.getAllExtensionDescriptions()) {
if (isEqualOrParent(testLocation, extension.extensionLocation)) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation === ExtensionRunningLocation.LocalProcess) {
extensionHostKind = ExtensionHostKind.LocalProcess;
} else if (runningLocation === ExtensionRunningLocation.LocalWebWorker) {
extensionHostKind = ExtensionHostKind.LocalWebWorker;
} else if (runningLocation === ExtensionRunningLocation.Remote) {
extensionHostKind = ExtensionHostKind.Remote;
}
runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier)) || null;
break;
}
}
if (extensionHostKind === undefined) {
if (runningLocation === null) {
// not sure if we should support that, but it was possible to have an test outside an extension
if (testLocation.scheme === Schemas.vscodeRemote) {
extensionHostKind = ExtensionHostKind.Remote;
runningLocation = new RemoteRunningLocation();
} else {
// When a debugger attaches to the extension host, it will surface all console.log messages from the extension host,
// but not necessarily from the window. So it would be best if any errors get printed to the console of the extension host.
// That is why here we use the local process extension host even for non-file URIs
extensionHostKind = ExtensionHostKind.LocalProcess;
runningLocation = new LocalProcessRunningLocation(0);
}
}
if (extensionHostKind !== undefined) {
return this._getExtensionHostManager(extensionHostKind);
if (runningLocation !== null) {
return this._getExtensionHostManagerByRunningLocation(runningLocation);
}
return undefined;
return null;
}
private _releaseBarrier(): void {
@ -619,14 +823,42 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
}
private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void {
const extensionHosts = this._createExtensionHosts(isInitialStart);
extensionHosts.forEach((extensionHost) => {
const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHost, isInitialStart, initialActivationEvents, this._acquireInternalAPI());
processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal));
processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); });
this._extensionHostManagers.push(processManager);
private _startExtensionHostsIfNecessary(isInitialStart: boolean, initialActivationEvents: string[]): void {
const locations: ExtensionRunningLocation[] = [];
for (let affinity = 0; affinity <= this._maxLocalProcessAffinity; affinity++) {
locations.push(new LocalProcessRunningLocation(affinity));
}
locations.push(new LocalWebWorkerRunningLocation());
locations.push(new RemoteRunningLocation());
for (const location of locations) {
if (this._getExtensionHostManagerByRunningLocation(location)) {
// already running
continue;
}
const extHostManager = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);
if (extHostManager) {
this._extensionHostManagers.push(extHostManager);
}
}
}
private _createExtensionHostManager(runningLocation: ExtensionRunningLocation, isInitialStart: boolean, initialActivationEvents: string[]): IExtensionHostManager | null {
const extensionHost = this._createExtensionHost(runningLocation, isInitialStart);
if (!extensionHost) {
return null;
}
const extensionHostId = String(++this._lastExtensionHostId);
const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHostId, extensionHost, isInitialStart, initialActivationEvents, this._acquireInternalAPI());
processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal));
processManager.onDidChangeResponsiveState((responsiveState) => {
this._onDidChangeResponsiveChange.fire({
extensionHostId: extensionHostId,
extensionHostKind: processManager.kind,
isResponsive: responsiveState === ResponsiveState.Responsive
});
});
return processManager;
}
private _onExtensionHostCrashOrExit(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {
@ -660,12 +892,10 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
const lock = await this._registryLock.acquire('startExtensionHosts');
try {
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
await localProcessExtensionHost.ready();
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
await Promise.all(localProcessExtensionHosts.map(extHost => extHost.ready()));
} finally {
lock.dispose();
}
@ -771,15 +1001,28 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
messages: this._extensionsMessages.get(extensionKey) || [],
activationTimes: this._extensionHostActivationTimes.get(extensionKey),
runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [],
runningLocation: this._runningLocation.get(extensionKey) || ExtensionRunningLocation.None,
runningLocation: this._runningLocation.get(extensionKey) || null,
};
}
}
return result;
}
public getInspectPort(_tryEnableInspector: boolean): Promise<number> {
return Promise.resolve(0);
public async getInspectPort(extensionHostId: string, tryEnableInspector: boolean): Promise<number> {
for (const extHostManager of this._extensionHostManagers) {
if (extHostManager.extensionHostId === extensionHostId) {
return extHostManager.getInspectPort(tryEnableInspector);
}
}
return 0;
}
public async getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<number[]> {
const result = await Promise.all(
this._getExtensionHostManagers(extensionHostKind).map(extHost => extHost.getInspectPort(tryEnableInspector))
);
// remove 0s:
return result.filter(element => Boolean(element));
}
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
@ -1040,7 +1283,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
//#endregion
protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[];
protected abstract _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null;
protected abstract _scanAndHandleExtensions(): Promise<void>;
protected abstract _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null>;
public abstract _onExtensionHostExit(code: number): void;
@ -1094,25 +1337,28 @@ class ExtensionInfo {
}
}
class ExtensionRunningLocationClassifier {
constructor(
private readonly getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
private readonly pickRunningLocation: (extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionRunningLocation,
) {
}
class ExtensionHostKindClassifier {
private _toExtensionWithKind(extensions: IExtensionDescription[]): Map<string, ExtensionWithKind> {
private static _toExtensionWithKind(
extensions: IExtensionDescription[],
getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[]
): Map<string, ExtensionWithKind> {
const result = new Map<string, ExtensionWithKind>();
extensions.forEach((desc) => {
const ext = new ExtensionWithKind(desc, this.getExtensionKind(desc));
const ext = new ExtensionWithKind(desc, getExtensionKind(desc));
result.set(ext.key, ext);
});
return result;
}
public determineRunningLocation(_localExtensions: IExtensionDescription[], _remoteExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation> {
const localExtensions = this._toExtensionWithKind(_localExtensions);
const remoteExtensions = this._toExtensionWithKind(_remoteExtensions);
public static determineExtensionHostKinds(
_localExtensions: IExtensionDescription[],
_remoteExtensions: IExtensionDescription[],
getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
pickExtensionHostKind: (extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionHostKind | null
): Map<string, ExtensionHostKind | null> {
const localExtensions = this._toExtensionWithKind(_localExtensions, getExtensionKind);
const remoteExtensions = this._toExtensionWithKind(_remoteExtensions, getExtensionKind);
const allExtensions = new Map<string, ExtensionInfo>();
const collectExtension = (ext: ExtensionWithKind) => {
@ -1127,7 +1373,7 @@ class ExtensionRunningLocationClassifier {
localExtensions.forEach((ext) => collectExtension(ext));
remoteExtensions.forEach((ext) => collectExtension(ext));
const runningLocation = new Map<string, ExtensionRunningLocation>();
const extensionHostKinds = new Map<string, ExtensionHostKind | null>();
allExtensions.forEach((ext) => {
const isInstalledLocally = Boolean(ext.local);
const isInstalledRemotely = Boolean(ext.remote);
@ -1142,10 +1388,10 @@ class ExtensionRunningLocationClassifier {
preference = ExtensionRunningPreference.Remote;
}
runningLocation.set(ext.key, this.pickRunningLocation(ext.identifier, ext.kind, isInstalledLocally, isInstalledRemotely, preference));
extensionHostKinds.set(ext.key, pickExtensionHostKind(ext.identifier, ext.kind, isInstalledLocally, isInstalledRemotely, preference));
});
return runningLocation;
return extensionHostKinds;
}
}
@ -1244,6 +1490,29 @@ class ProposedApiController {
}
}
function filterByRunningLocation<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): T[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(extId(ext))) === desiredRunningLocation);
export function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return _filterByRunningLocation(extensions, ext => ext.identifier, runningLocation, desiredRunningLocation);
}
function _filterByRunningLocation<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, desiredRunningLocation: ExtensionRunningLocation): T[] {
return _filterExtensions(extensions, extId, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
}
function filterByExtensionHostKind(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, desiredExtensionHostKind: ExtensionHostKind): IExtensionDescription[] {
return _filterExtensions(extensions, ext => ext.identifier, runningLocation, extRunningLocation => extRunningLocation.kind === desiredExtensionHostKind);
}
function filterByExtensionHostManager(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, extensionHostManager: IExtensionHostManager): IExtensionDescription[] {
return _filterByExtensionHostManager(extensions, ext => ext.identifier, runningLocation, extensionHostManager);
}
function _filterByExtensionHostManager<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, extensionHostManager: IExtensionHostManager): T[] {
return _filterExtensions(extensions, extId, runningLocation, extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation));
}
function _filterExtensions<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, predicate: (extRunningLocation: ExtensionRunningLocation) => boolean): T[] {
return extensions.filter((ext) => {
const extRunningLocation = runningLocation.get(ExtensionIdentifier.toKey(extId(ext)));
return extRunningLocation && predicate(extRunningLocation);
});
}

View file

@ -12,49 +12,54 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import { ExtHostCustomersRegistry, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { Proxied, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as nls from 'vs/nls';
import { registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { StopWatch } from 'vs/base/common/stopwatch';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { Barrier, timeout } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionHostProxy } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
const LOG_USE_COLORS = true;
export interface IExtensionHostManager {
readonly extensionHostId: string;
readonly kind: ExtensionHostKind;
readonly onDidExit: Event<[number, string | null]>;
readonly onDidChangeResponsiveState: Event<ResponsiveState>;
dispose(): void;
ready(): Promise<void>;
representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean;
deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
activationEventIsDone(activationEvent: string): boolean;
getInspectPort(tryEnableInspector: boolean): Promise<number>;
resolveAuthority(remoteAuthority: string): Promise<ResolverResult>;
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI>;
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null>;
start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
extensionTestsExecute(): Promise<number>;
extensionTestsSendExit(exitCode: number): Promise<void>;
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}
export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager {
export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHostId: string, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager {
if (extensionHost.lazyStart && isInitialStart && initialActivationEvents.length === 0) {
return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHost, internalExtensionService);
return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHostId, extensionHost, internalExtensionService);
}
return instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);
return instantiationService.createInstance(ExtensionHostManager, extensionHostId, extensionHost, initialActivationEvents, internalExtensionService);
}
export type ExtensionHostStartupClassification = {
@ -77,7 +82,6 @@ export type ExtensionHostStartupEvent = {
class ExtensionHostManager extends Disposable implements IExtensionHostManager {
public readonly kind: ExtensionHostKind;
public readonly onDidExit: Event<[number, string | null]>;
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
@ -92,10 +96,14 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
private readonly _customers: IDisposable[];
private readonly _extensionHost: IExtensionHost;
private _proxy: Promise<IExtensionHostProxy | null> | null;
private _resolveAuthorityAttempt: number;
private _hasStarted = false;
public get kind(): ExtensionHostKind {
return this._extensionHost.runningLocation.kind;
}
constructor(
public readonly extensionHostId: string,
extensionHost: IExtensionHost,
initialActivationEvents: string[],
private readonly _internalExtensionService: IInternalExtensionService,
@ -111,7 +119,6 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
this._customers = [];
this._extensionHost = extensionHost;
this.kind = this._extensionHost.kind;
this.onDidExit = this._extensionHost.onExit;
const startingTelemetryEvent: ExtensionHostStartupEvent = {
@ -166,7 +173,6 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
measure: () => this.measure()
}));
});
this._resolveAuthorityAttempt = 0;
}
public override dispose(): void {
@ -190,7 +196,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
private async measure(): Promise<ExtHostLatencyResult | null> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return null;
}
@ -205,12 +211,8 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
};
}
private async _getProxy(): Promise<IExtensionHostProxy | null> {
return this._proxy;
}
public async ready(): Promise<void> {
await this._getProxy();
await this._proxy;
}
private async _measureLatency(proxy: IExtensionHostProxy): Promise<number> {
@ -310,7 +312,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return false;
}
@ -359,57 +361,52 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
return 0;
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not need to be resolved, simply parse the port number
const lastColon = remoteAuthority.lastIndexOf(':');
return Promise.resolve({
authority: {
authority: remoteAuthority,
host: remoteAuthority.substring(0, lastColon),
port: parseInt(remoteAuthority.substring(lastColon + 1), 10),
connectionToken: undefined
}
});
}
const proxy = await this._getProxy();
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
const proxy = await this._proxy;
if (!proxy) {
throw new Error(`Cannot resolve authority`);
return {
type: 'error',
error: {
message: `Cannot resolve authority`,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: undefined
}
};
}
this._resolveAuthorityAttempt++;
const result = await proxy.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt);
if (result.type === 'ok') {
return result.value;
} else {
throw new RemoteAuthorityResolverError(result.error.message, result.error.code, result.error.detail);
try {
return proxy.resolveAuthority(remoteAuthority, resolveAttempt);
} catch (err) {
return {
type: 'error',
error: {
message: err.message,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: err
}
};
}
}
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not use a resolver
return uri;
}
const proxy = await this._getProxy();
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
const proxy = await this._proxy;
if (!proxy) {
throw new Error(`Cannot resolve canonical URI`);
}
const result = await proxy.getCanonicalURI(remoteAuthority, uri);
return URI.revive(result);
return proxy.getCanonicalURI(remoteAuthority, uri);
}
public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
this._extensionHost.extensions.keepOnly(enabledExtensionIds);
return proxy.startExtensionHost(enabledExtensionIds);
}
public async extensionTestsExecute(): Promise<number> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
throw new Error('Could not obtain Extension Host Proxy');
}
@ -417,7 +414,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
public async extensionTestsSendExit(exitCode: number): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
@ -430,16 +427,21 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
}
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
return this._extensionHost.runningLocation.equals(runningLocation);
}
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
this._extensionHost.extensions.deltaExtensions(toAdd, toRemove);
return proxy.deltaExtensions(toAdd, toRemove);
}
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
@ -452,7 +454,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
* Waits until `start()` and only if it has extensions proceeds to really start.
*/
class LazyStartExtensionHostManager extends Disposable implements IExtensionHostManager {
public readonly kind: ExtensionHostKind;
public readonly onDidExit: Event<[number, string | null]>;
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
@ -461,7 +463,12 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
private _startCalled: Barrier;
private _actual: ExtensionHostManager | null;
public get kind(): ExtensionHostKind {
return this._extensionHost.runningLocation.kind;
}
constructor(
public readonly extensionHostId: string,
extensionHost: IExtensionHost,
private readonly _internalExtensionService: IInternalExtensionService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ -469,7 +476,6 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
) {
super();
this._extensionHost = extensionHost;
this.kind = extensionHost.kind;
this.onDidExit = extensionHost.onExit;
this._startCalled = new Barrier();
this._actual = null;
@ -477,7 +483,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
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._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this.extensionHostId, this._extensionHost, [], this._internalExtensionService));
this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));
return this._actual;
}
@ -498,6 +504,9 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
await this._actual.ready();
}
}
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
return this._extensionHost.runningLocation.equals(runningLocation);
}
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
await this._startCalled.wait();
const extensionHostAlreadyStarted = Boolean(this._actual);
@ -543,14 +552,21 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
}
return 0;
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
await this._startCalled.wait();
if (this._actual) {
return this._actual.resolveAuthority(remoteAuthority);
return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);
}
throw new Error(`Cannot resolve authority`);
return {
type: 'error',
error: {
message: `Cannot resolve authority`,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: undefined
}
};
}
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
await this._startCalled.wait();
if (this._actual) {
return this._actual.getCanonicalURI(remoteAuthority, uri);

View file

@ -27,7 +27,10 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut
export interface IExtensionHostProxy {
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null>;
startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
extensionTestsExecute(): Promise<number>;
extensionTestsExit(code: number): Promise<void>;

View file

@ -13,6 +13,7 @@ import { getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionMana
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals';
import { IV8Profile } from 'vs/platform/profiling/common/profiling';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
identifier: new ExtensionIdentifier('nullExtensionDescription'),
@ -36,31 +37,48 @@ export interface IMessage {
extensionPointId: string;
}
export const enum ExtensionRunningLocation {
None,
LocalProcess,
LocalWebWorker,
Remote
}
export function extensionRunningLocationToString(location: ExtensionRunningLocation) {
switch (location) {
case ExtensionRunningLocation.None:
return 'None';
case ExtensionRunningLocation.LocalProcess:
export class LocalProcessRunningLocation {
public readonly kind = ExtensionHostKind.LocalProcess;
constructor(
public readonly affinity: number
) { }
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind && this.affinity === other.affinity);
}
public asString(): string {
if (this.affinity === 0) {
return 'LocalProcess';
case ExtensionRunningLocation.LocalWebWorker:
return 'LocalWebWorker';
case ExtensionRunningLocation.Remote:
return 'Remote';
}
return `LocalProcess${this.affinity}`;
}
}
export class LocalWebWorkerRunningLocation {
public readonly kind = ExtensionHostKind.LocalWebWorker;
public readonly affinity = 0;
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind);
}
public asString(): string {
return 'LocalWebWorker';
}
}
export class RemoteRunningLocation {
public readonly kind = ExtensionHostKind.Remote;
public readonly affinity = 0;
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind);
}
public asString(): string {
return 'Remote';
}
}
export type ExtensionRunningLocation = LocalProcessRunningLocation | LocalWebWorkerRunningLocation | RemoteRunningLocation;
export interface IExtensionsStatus {
messages: IMessage[];
activationTimes: ActivationTimes | undefined;
runtimeErrors: Error[];
runningLocation: ExtensionRunningLocation;
runningLocation: ExtensionRunningLocation | null;
}
export class MissingExtensionDependency {
@ -108,12 +126,15 @@ export interface IExtensionHostProfile {
}
export const enum ExtensionHostKind {
LocalProcess,
LocalWebWorker,
Remote
LocalProcess = 1,
LocalWebWorker = 2,
Remote = 3
}
export function extensionHostKindToString(kind: ExtensionHostKind): string {
export function extensionHostKindToString(kind: ExtensionHostKind | null): string {
if (kind === null) {
return 'None';
}
switch (kind) {
case ExtensionHostKind.LocalProcess: return 'LocalProcess';
case ExtensionHostKind.LocalWebWorker: return 'LocalWebWorker';
@ -122,9 +143,14 @@ export function extensionHostKindToString(kind: ExtensionHostKind): string {
}
export interface IExtensionHost {
readonly kind: ExtensionHostKind;
readonly runningLocation: ExtensionRunningLocation;
readonly remoteAuthority: string | null;
readonly lazyStart: boolean;
/**
* A collection of extensions that will execute or are executing on this extension host.
* **NOTE**: this will reflect extensions correctly only after `start()` resolves.
*/
readonly extensions: ExtensionDescriptionRegistry;
readonly onExit: Event<[number, string | null]>;
start(): Promise<IMessagePassingProtocol> | null;
@ -186,6 +212,8 @@ export interface IWillActivateEvent {
}
export interface IResponsiveStateChangeEvent {
extensionHostId: string;
extensionHostKind: ExtensionHostKind;
isResponsive: boolean;
}
@ -288,10 +316,15 @@ export interface IExtensionService {
getExtensionsStatus(): { [id: string]: IExtensionsStatus };
/**
* Return the inspect port or `0`, the latter means inspection
* is not possible.
* Return the inspect port or `0` for a certain extension host.
* `0` means inspection is not possible.
*/
getInspectPort(tryEnableInspector: boolean): Promise<number>;
getInspectPort(extensionHostId: string, tryEnableInspector: boolean): Promise<number>;
/**
* Return the inspect ports (if inspection is possible) for extension hosts of kind `extensionHostKind`.
*/
getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<number[]>;
/**
* Stops the extension hosts.
@ -372,7 +405,8 @@ export class NullExtensionService implements IExtensionService {
getExtension() { return Promise.resolve(undefined); }
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
getExtensionsStatus(): { [id: string]: IExtensionsStatus } { return Object.create(null); }
getInspectPort(_tryEnableInspector: boolean): Promise<number> { return Promise.resolve(0); }
getInspectPort(_extensionHostId: string, _tryEnableInspector: boolean): Promise<number> { return Promise.resolve(0); }
getInspectPorts(_extensionHostKind: ExtensionHostKind, _tryEnableInspector: boolean): Promise<number[]> { return Promise.resolve([]); }
stopExtensionHosts(): void { }
async restartExtensionHost(): Promise<void> { }
async startExtensionHosts(): Promise<void> { }

View file

@ -25,9 +25,10 @@ import { ISignService } from 'vs/platform/sign/common/sign';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { createMessageOfType, isMessageOfType, MessageType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { ExtensionHostKind, ExtensionHostLogFileName, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostLogFileName, IExtensionHost, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output';
@ -49,9 +50,9 @@ export interface IRemoteExtensionHostDataProvider {
export class RemoteExtensionHost extends Disposable implements IExtensionHost {
public readonly kind = ExtensionHostKind.Remote;
public readonly remoteAuthority: string;
public readonly lazyStart = false;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>());
public readonly onExit: Event<[number, string | null]> = this._onExit.event;
@ -62,6 +63,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
private readonly _isExtensionDevHost: boolean;
constructor(
public readonly runningLocation: RemoteRunningLocation,
private readonly _initDataProvider: IRemoteExtensionHostDataProvider,
private readonly _socketFactory: ISocketFactory,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ -222,6 +224,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier)
);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(remoteInitData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -251,7 +254,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
},
resolvedExtensions: resolvedExtensions,
hostExtensions: hostExtensions,
extensions: remoteInitData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: remoteInitData.extensionHostLogsPath,

View file

@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { ILocalProcessExtensionHostDataProvider, LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString, filterByRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import * as nls from 'vs/nls';
import { runWhenIdle } from 'vs/base/common/async';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@ -16,13 +16,13 @@ import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsSc
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig, ExtensionRunningLocation, WebWorkerExtHostConfigValue, extensionRunningLocationToString, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig, ExtensionRunningLocation, WebWorkerExtHostConfigValue, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
@ -35,7 +35,7 @@ import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remo
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWebWorkerExtensionHostDataProvider, WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ILogService } from 'vs/platform/log/common/log';
import { CATEGORIES } from 'vs/workbench/common/actions';
@ -48,6 +48,8 @@ import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/w
import { CancellationToken } from 'vs/base/common/cancellation';
import { StopWatch } from 'vs/base/common/stopwatch';
import { isCI } from 'vs/base/common/platform';
import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { URI } from 'vs/base/common/uri';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@ -56,6 +58,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
private readonly _remoteInitData: Map<string, IRemoteExtensionHostInitData>;
private readonly _extensionScanner: CachedExtensionScanner;
private readonly _crashTracker = new ExtensionHostCrashTracker();
private _resolveAuthorityAttempt: number = 0;
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@ -151,13 +154,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten
]));
}
private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation) {
private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation): ILocalProcessExtensionHostDataProvider & IWebWorkerExtensionHostDataProvider {
return {
getInitData: async () => {
if (isInitialStart) {
// Here we load even extensions that would be disabled by workspace trust
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), /* ignore workspace trust */true);
const runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, []);
const runningLocation = this._determineRunningLocation(localExtensions);
const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation);
return {
autoStart: false,
@ -166,7 +169,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
} else {
// restart case
const allExtensions = await this.getExtensions();
const localProcessExtensions = filterByRunningLocation(allExtensions, this._runningLocation, desiredRunningLocation);
const localProcessExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation);
return {
autoStart: true,
extensions: localProcessExtensions
@ -186,69 +189,70 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
protected _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
const result = ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionRunningLocationToString(result)}`);
protected _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result = ExtensionService.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
return result;
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
public static pickExtensionHostKind(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionHostKind | null {
const result: ExtensionHostKind[] = [];
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledLocally) {
// ui extensions run locally if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
return ExtensionHostKind.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
result.push(ExtensionHostKind.LocalProcess);
}
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
}
if (extensionKind === 'workspace' && !hasRemoteExtHost) {
// workspace extensions also run locally if there is no remote
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
return ExtensionHostKind.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
result.push(ExtensionHostKind.LocalProcess);
}
}
if (extensionKind === 'web' && isInstalledLocally && hasWebWorkerExtHost) {
// web worker extensions run in the local web worker if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
return ExtensionHostKind.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
result.push(ExtensionHostKind.LocalWebWorker);
}
}
}
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
return (result.length > 0 ? result[0] : null);
}
protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
const localProcessExtHost = this._instantiationService.createInstance(LocalProcessExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalProcess));
result.push(localProcessExtHost);
if (this._enableLocalWebWorker) {
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._lazyLocalWebWorker, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalWebWorker));
result.push(webWorkerExtHost);
protected _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
switch (runningLocation.kind) {
case ExtensionHostKind.LocalProcess: {
return this._instantiationService.createInstance(LocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation));
}
case ExtensionHostKind.LocalWebWorker: {
if (this._enableLocalWebWorker) {
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, this._lazyLocalWebWorker, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation));
}
return null;
}
case ExtensionHostKind.Remote: {
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
}
return null;
}
}
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
result.push(remoteExtHost);
}
return result;
}
protected override _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {
@ -340,18 +344,88 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// --- impl
private async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not need to be resolved, simply parse the port number
const lastColon = remoteAuthority.lastIndexOf(':');
return {
authority: {
authority: remoteAuthority,
host: remoteAuthority.substring(0, lastColon),
port: parseInt(remoteAuthority.substring(lastColon + 1), 10),
connectionToken: undefined
}
};
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHosts.length === 0) {
// no local process extension hosts
throw new Error(`Cannot resolve authority`);
}
this._resolveAuthorityAttempt++;
const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt)));
let bestErrorResult: IResolveAuthorityErrorResult | null = null;
for (const result of results) {
if (result.type === 'ok') {
return result.value;
}
if (!bestErrorResult) {
bestErrorResult = result;
continue;
}
const bestErrorIsUnknown = (bestErrorResult.error.code === RemoteAuthorityResolverErrorCode.Unknown);
const errorIsUnknown = (result.error.code === RemoteAuthorityResolverErrorCode.Unknown);
if (bestErrorIsUnknown && !errorIsUnknown) {
bestErrorResult = result;
}
}
// we can only reach this if there is an error
throw new RemoteAuthorityResolverError(bestErrorResult!.error.message, bestErrorResult!.error.code, bestErrorResult!.error.detail);
}
private async _getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not use a resolver
return uri;
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHosts.length === 0) {
// no local process extension hosts
throw new Error(`Cannot resolve canonical URI`);
}
const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.getCanonicalURI(remoteAuthority, uri)));
for (const result of results) {
if (result) {
return result;
}
}
// we can only reach this if there was no resolver extension that can return the cannonical uri
throw new Error(`Cannot get canonical URI because no extension is installed to resolve ${getRemoteAuthorityPrefix(remoteAuthority)}`);
}
private async _resolveAuthorityAgain(): Promise<void> {
const remoteAuthority = this._environmentService.remoteAuthority;
if (!remoteAuthority) {
return;
}
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
const sw = StopWatch.create(false);
this._logService.info(`Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`);
try {
const result = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
const result = await this._resolveAuthority(remoteAuthority);
this._logService.info(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${result.authority.host}:${result.authority.port}' after ${sw.elapsed()} ms`);
this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);
} catch (err) {
@ -364,7 +438,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._extensionScanner.startScanningExtensions(this.createLogger());
const remoteAuthority = this._environmentService.remoteAuthority;
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
let remoteEnv: IRemoteAgentEnvironment | null = null;
let remoteExtensions: IExtensionDescription[] = [];
@ -376,12 +449,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// The current remote authority resolver cannot give the canonical URI for this URI
return uri;
}
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
if (isCI) {
this._logService.info(`Invoking getCanonicalURI for authority ${getRemoteAuthorityPrefix(remoteAuthority)}...`);
}
try {
return localProcessExtensionHost.getCanonicalURI(remoteAuthority, uri);
return this._getCanonicalURI(remoteAuthority, uri);
} finally {
if (isCI) {
this._logService.info(`getCanonicalURI returned for authority ${getRemoteAuthorityPrefix(remoteAuthority)}.`);
@ -407,7 +479,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const sw = StopWatch.create(false);
this._logService.info(`Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`);
try {
resolverResult = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
resolverResult = await this._resolveAuthority(remoteAuthority);
this._logService.info(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${resolverResult.authority.host}:${resolverResult.authority.port}' after ${sw.elapsed()} ms`);
} catch (err) {
this._logService.error(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned an error after ${sw.elapsed()} ms`, err);
@ -471,12 +543,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions, false);
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), false);
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
this._initializeRunningLocation(localExtensions, remoteExtensions);
// remove non-UI extensions from the local extensions
const localProcessExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalProcess);
const localWebWorkerExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
const localProcessExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalProcess);
const localWebWorkerExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);
remoteExtensions = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);
const result = this._registry.deltaExtensions(remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions), []);
if (result.removedDueToLooping.length > 0) {
@ -498,23 +570,22 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions());
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
const filteredLocalProcessExtensions = localProcessExtensions.filter(extension => this._registry.containsExtension(extension.identifier));
for (const extHost of localProcessExtensionHosts) {
this._startExtensionHost(extHost, filteredLocalProcessExtensions);
}
const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker);
if (localWebWorkerExtensionHost) {
localWebWorkerExtensionHost.start(localWebWorkerExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
const localWebWorkerExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalWebWorker);
const filteredLocalWebWorkerExtensions = localWebWorkerExtensions.filter(extension => this._registry.containsExtension(extension.identifier));
for (const extHost of localWebWorkerExtensionHosts) {
this._startExtensionHost(extHost, filteredLocalWebWorkerExtensions);
}
}
public override async getInspectPort(tryEnableInspector: boolean): Promise<number> {
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
return localProcessExtensionHost.getInspectPort(tryEnableInspector);
}
return 0;
private _startExtensionHost(extensionHostManager: IExtensionHostManager, _extensions: IExtensionDescription[]): void {
const extensions = this._filterByExtensionHostManager(_extensions, extensionHostManager);
extensionHostManager.start(extensions.map(extension => extension.identifier));
}
public _onExtensionHostExit(code: number): void {
@ -630,10 +701,6 @@ function getRemoteAuthorityPrefix(remoteAuthority: string): string {
return remoteAuthority.substring(0, plusIndex);
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}
registerSingleton(IExtensionService, ExtensionService);
class RestartExtensionHostAction extends Action2 {

View file

@ -34,7 +34,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { parseExtensionDevOptions } from '../common/extensionDevOptions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { joinPath } from 'vs/base/common/resources';
import { Registry } from 'vs/platform/registry/common/platform';
@ -44,6 +44,7 @@ import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform
import { SerializedError } from 'vs/base/common/errors';
import { removeDangerousEnvVariables } from 'vs/base/node/processes';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export interface ILocalProcessExtensionHostInitData {
readonly autoStart: boolean;
@ -105,9 +106,9 @@ class ExtensionHostProcess {
export class LocalProcessExtensionHost implements IExtensionHost {
public readonly kind = ExtensionHostKind.LocalProcess;
public readonly remoteAuthority = null;
public readonly lazyStart = false;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>();
public readonly onExit: Event<[number, string]> = this._onExit.event;
@ -135,6 +136,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
private readonly _extensionHostLogFile: URI;
constructor(
public readonly runningLocation: LocalProcessRunningLocation,
private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@INotificationService private readonly _notificationService: INotificationService,
@ -502,6 +504,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
private async _createExtHostInitData(): Promise<IExtensionHostInitData> {
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(initData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -532,7 +535,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
},
resolvedExtensions: [],
hostExtensions: [],
extensions: initData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: this._environmentService.extHostLogsPath,

View file

@ -6,84 +6,84 @@
import * as assert from 'assert';
import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService';
import { ExtensionRunningPreference } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
suite('BrowserExtensionService', () => {
test('pickRunningLocation', () => {
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, true, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, true, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
});
});