Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)

* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898

* Fixes and cleanup

* Distro

* Fix hygiene yarn

* delete no yarn lock changes file

* Fix hygiene

* Fix layer check

* Fix CI

* Skip lib checks

* Remove tests deleted in vs code

* Fix tests

* Distro

* Fix tests and add removed extension point

* Skip failing notebook tests for now

* Disable broken tests and cleanup build folder

* Update yarn.lock and fix smoke tests

* Bump sqlite

* fix contributed actions and file spacing

* Fix user data path

* Update yarn.locks

Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
Charles Gagnon
2021-06-17 08:17:11 -07:00
committed by GitHub
parent fdcb97c7f7
commit 3cb2f552a6
2582 changed files with 124827 additions and 87099 deletions

View File

@@ -9,11 +9,11 @@ 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, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionHost } 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, ExtensionRunningLocation, ExtensionRunningLocationClassifier, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningLocation, ExtensionRunningLocationClassifier, ExtensionRunningPreference, parseScannedExtension } 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';
@@ -26,8 +26,8 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@@ -49,12 +49,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
@IUserDataInitializationService private readonly _userDataInitializationService: IUserDataInitializationService,
) {
super(
new ExtensionRunningLocationClassifier(
productService,
configurationService,
(extensionKinds, isInstalledLocally, isInstalledRemotely) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely)
(extension) => this._getExtensionKind(extension),
(extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
),
instantiationService,
notificationService,
@@ -66,35 +67,25 @@ export class ExtensionService extends AbstractExtensionService implements IExten
extensionManagementService,
contextService,
configurationService,
extensionManifestPropertiesService
);
this._runningLocation = new Map<string, ExtensionRunningLocation>();
// Initialize only after workbench is ready
this._lifecycleService.when(LifecyclePhase.Ready).then(() => this._initialize());
// 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);
this._initialize();
});
this._initFetchFileSystem();
}
dispose(): void {
override dispose(): void {
this._disposables.dispose();
super.dispose();
}
protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
super._onExtensionHostCrashed(extensionHost, code, signal);
if (extensionHost.kind === ExtensionHostKind.LocalWebWorker) {
if (code === ExtensionHostExitCode.StartTimeout60s) {
this._notificationService.prompt(
Severity.Error,
nls.localize('extensionService.startTimeout', "The Web Worker Extension Host did not start in 60s."),
[]
);
return;
}
}
}
protected async _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null> {
if (extension.location.scheme === Schemas.vscodeRemote) {
return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System);
@@ -137,23 +128,39 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation {
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
let canRunRemotely = false;
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledRemotely) {
// ui extensions run remotely if possible (but only as a last resort)
canRunRemotely = true;
if (preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
} else {
canRunRemotely = true;
}
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
}
}
if (extensionKind === 'web' && isInstalledLocally) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
}
}
}
return (canRunRemotely ? ExtensionRunningLocation.Remote : ExtensionRunningLocation.None);
if (canRunRemotely) {
result.push(ExtensionRunningLocation.Remote);
}
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
}
protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] {
@@ -211,7 +218,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
public _onExtensionHostExit(code: number): void {
// Dispose everything associated with the extension host
this._stopExtensionHosts();
this.stopExtensionHosts();
// We log the exit code to the console. Do NOT remove this
// code as the automated integration tests in browser rely

View File

@@ -85,14 +85,6 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`);
}
private _wrapInIframe(): boolean {
if (this._environmentService.options && typeof this._environmentService.options._wrapWebWorkerExtHostInIframe === 'boolean') {
return this._environmentService.options._wrapWebWorkerExtHostInIframe;
}
// wrap in <iframe> by default
return true;
}
private _webWorkerExtensionHostIframeSrc(): string | null {
if (this._environmentService.options && this._environmentService.options.webWorkerExtensionHostIframeSrc) {
return this._environmentService.options.webWorkerExtensionHostIframeSrc;
@@ -119,7 +111,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
if (!this._protocolPromise) {
if (platform.isWeb) {
const webWorkerExtensionHostIframeSrc = this._webWorkerExtensionHostIframeSrc();
if (webWorkerExtensionHostIframeSrc && this._wrapInIframe()) {
if (webWorkerExtensionHostIframeSrc) {
this._protocolPromise = this._startInsideIframe(webWorkerExtensionHostIframeSrc);
} else {
console.warn(`The web worker extension host is started without an iframe sandbox!`);
@@ -166,7 +158,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
};
startTimeout = setTimeout(() => {
rejectBarrier(ExtensionHostExitCode.StartTimeout60s, new Error('The Web Worker Extension Host did not start in 60s'));
console.warn(`The Web Worker Extension Host did not start in 60s, that might be a problem.`);
}, 60000);
this._register(dom.addDisposableListener(window, 'message', (event) => {
@@ -333,7 +325,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
return protocol;
}
public dispose(): void {
public override dispose(): void {
if (this._isTerminating) {
return;
}
@@ -366,7 +358,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
appUriScheme: this._productService.urlProtocol,
appLanguage: platform.language,
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
extensionTestsLocationURI: undefined, // never run extension tests in web worker extension host
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: this._environmentService.globalStorageHome,
workspaceStorageHome: this._environmentService.workspaceStorageHome,
webviewResourceRoot: this._environmentService.webviewResourceRoot,

View File

@@ -21,7 +21,7 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind, IExtensionContributions } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -29,9 +29,10 @@ import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtens
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@@ -41,7 +42,7 @@ export function parseScannedExtension(extension: ITranslatedScannedExtension): I
identifier: new ExtensionIdentifier(`${extension.packageJSON.publisher}.${extension.packageJSON.name}`),
isBuiltin: extension.type === ExtensionType.System,
isUserBuiltin: false,
isUnderDevelopment: false,
isUnderDevelopment: extension.isUnderDevelopment,
extensionLocation: extension.location,
...extension.packageJSON,
};
@@ -61,6 +62,12 @@ export const enum ExtensionRunningLocation {
Remote
}
export const enum ExtensionRunningPreference {
None,
Local,
Remote
}
export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: undefined;
@@ -71,7 +78,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
protected 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>());
protected 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>());
@@ -88,8 +95,11 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private readonly _proposedApiController: ProposedApiController;
private readonly _isExtensionDevHost: boolean;
protected readonly _isExtensionDevTestFromCli: boolean;
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
private _inHandleDeltaExtensions: boolean;
private readonly _onDidFinishHandleDeltaExtensions = this._register(new Emitter<void>());
protected _runningLocation: Map<string, ExtensionRunningLocation>;
// --- Members used per extension host process
@@ -110,6 +120,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
@IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@IConfigurationService protected readonly _configurationService: IConfigurationService,
@IExtensionManifestPropertiesService protected readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,
) {
super();
@@ -135,6 +146,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._deltaExtensionsQueue = [];
this._inHandleDeltaExtensions = false;
this._runningLocation = new Map<string, ExtensionRunningLocation>();
this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {
@@ -169,6 +181,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}));
}
protected _getExtensionKind(extensionDescription: IExtensionDescription): ExtensionKind[] {
if (extensionDescription.isUnderDevelopment && this._environmentService.extensionDevelopmentKind) {
return this._environmentService.extensionDevelopmentKind;
}
return this._extensionManifestPropertiesService.getExtensionKind(extensionDescription);
}
protected _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null {
for (const extensionHostManager of this._extensionHostManagers) {
if (extensionHostManager.kind === kind) {
@@ -196,6 +216,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._inHandleDeltaExtensions = false;
}
}
this._onDidFinishHandleDeltaExtensions.fire();
}
private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise<void> {
@@ -203,16 +225,16 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
for (let i = 0, len = _toAdd.length; i < len; i++) {
const extension = _toAdd[i];
if (!this._canAddExtension(extension)) {
continue;
}
const extensionDescription = await this._scanSingleExtension(extension);
if (!extensionDescription) {
// could not scan extension...
continue;
}
if (!this.canAddExtension(extensionDescription)) {
continue;
}
toAdd.push(extensionDescription);
}
@@ -277,9 +299,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
groupedToAdd[extensionHostKind] = filterByRunningLocation(toAdd, ext => ext.identifier, this._runningLocation, extensionRunningLocation);
};
for (const extension of toAdd) {
const extensionKind = getExtensionKind(extension, this._productService, this._configurationService);
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote);
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
groupAdd(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
@@ -302,25 +324,21 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
await Promise.all(promises);
}
public canAddExtension(extensionDescription: IExtensionDescription): boolean {
return this._canAddExtension(toExtension(extensionDescription));
}
private _canAddExtension(extension: IExtension): boolean {
const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id);
if (extensionDescription) {
public canAddExtension(extension: IExtensionDescription): boolean {
const existing = this._registry.getExtensionDescription(extension.identifier);
if (existing) {
// this extension is already running (most likely at a different version)
return false;
}
// Check if extension is renamed
if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) {
if (extension.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.uuid)) {
return false;
}
const extensionKind = getExtensionKind(extension.manifest, this._productService, this._configurationService);
const isRemote = extension.location.scheme === Schemas.vscodeRemote;
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote);
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
if (runningLocation === ExtensionRunningLocation.None) {
return false;
}
@@ -412,6 +430,67 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
await this._scanAndHandleExtensions();
this._releaseBarrier();
perf.mark('code/didLoadExtensions');
await this._handleExtensionTests();
}
private async _handleExtensionTests(): Promise<void> {
if (!this._environmentService.isExtensionDevelopment || !this._environmentService.extensionTestsLocationURI) {
return;
}
const extensionHostManager = this.findTestExtensionHost(this._environmentService.extensionTestsLocationURI);
if (!extensionHostManager) {
const msg = nls.localize('extensionTestError', "No extension host found that can launch the test runner at {0}.", this._environmentService.extensionTestsLocationURI.toString());
console.error(msg);
this._notificationService.error(msg);
return;
}
let exitCode: number;
try {
exitCode = await extensionHostManager.extensionTestsExecute();
} catch (err) {
console.error(err);
exitCode = 1 /* ERROR */;
}
await extensionHostManager.extensionTestsSendExit(exitCode);
this._onExtensionHostExit(exitCode);
}
private findTestExtensionHost(testLocation: URI): ExtensionHostManager | undefined | null {
let extensionHostKind: ExtensionHostKind | undefined;
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;
}
break;
}
}
if (extensionHostKind === undefined) {
// 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;
} 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;
}
}
if (extensionHostKind !== undefined) {
return this._getExtensionHostManager(extensionHostKind);
}
return undefined;
}
private _releaseBarrier(): void {
@@ -420,7 +499,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier));
}
protected _stopExtensionHosts(): void {
//#region Stopping / Starting / Restarting
public stopExtensionHosts(): void {
let previouslyActivatedExtensionIds: ExtensionIdentifier[] = [];
this._extensionHostActiveExtensions.forEach((value) => {
previouslyActivatedExtensionIds.push(value);
@@ -440,8 +521,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void {
this._stopExtensionHosts();
const extensionHosts = this._createExtensionHosts(isInitialStart);
extensionHosts.forEach((extensionHost) => {
const processManager = this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents);
@@ -465,21 +544,37 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
if (extensionHost.kind === ExtensionHostKind.LocalProcess) {
this._stopExtensionHosts();
this.stopExtensionHosts();
} else if (extensionHost.kind === ExtensionHostKind.Remote) {
for (let i = 0; i < this._extensionHostManagers.length; i++) {
if (this._extensionHostManagers[i] === extensionHost) {
this._extensionHostManagers[i].dispose();
this._extensionHostManagers.splice(i, 1);
break;
}
}
}
}
public async startExtensionHosts(): Promise<void> {
this.stopExtensionHosts();
if (this._inHandleDeltaExtensions) {
await Event.toPromise(this._onDidFinishHandleDeltaExtensions.event);
}
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
}
public async restartExtensionHost(): Promise<void> {
this.stopExtensionHosts();
await this.startExtensionHosts();
}
//#endregion
//#region IExtensionService
public restartExtensionHost(): void {
this._stopExtensionHosts();
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
}
protected startExtensionHost(): void {
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
}
public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise<void> {
if (this._installedExtensionsReady.isOpen()) {
// Extensions have been scanned and interpreted
@@ -535,14 +630,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
});
}
public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {
public readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {
return this._installedExtensionsReady.wait().then(() => {
const availableExtensions = this._registry.getAllExtensionDescriptions();
const result: ExtensionPointContribution<T>[] = [];
for (const desc of availableExtensions) {
if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name])); // {{SQL CARBON EDIT}} strict-null-checks
result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T));
}
}
@@ -593,23 +688,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return extensions.filter(extension => this._isEnabled(extension));
}
private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean {
if (this._environmentService.isExtensionDevelopment) {
const extDevLocs = this._environmentService.extensionDevelopmentLocationURI;
if (extDevLocs) {
const extLocation = extension.extensionLocation;
for (let p of extDevLocs) {
if (isEqualOrParent(extLocation, p)) {
return true;
}
}
}
}
return false;
}
protected _isEnabled(extension: IExtensionDescription): boolean {
if (this._isExtensionUnderDevelopment(extension)) {
if (extension.isUnderDevelopment) {
// Never disable extensions under development
return true;
}
@@ -691,13 +771,13 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
}
private static _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
private static _handleExtensionPoint<T extends IExtensionContributions[keyof IExtensionContributions]>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
const users: IExtensionPointUser<T>[] = [];
for (const desc of availableExtensions) {
if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {
users.push({
description: desc,
value: desc.contributes[extensionPoint.name], // {{SQL CARBON EDIT}} strict-null-checks
value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T,
collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)
});
}
@@ -725,7 +805,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
//#region Called by extension host
public _logOrShowMessage(severity: Severity, msg: string): void {
protected _logOrShowMessage(severity: Severity, msg: string): void {
if (this._isDev) {
this._showMessageToUser(severity, msg);
} else {
@@ -752,6 +832,21 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void {
type ExtensionActivationErrorClassification = {
extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
error: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
};
type ExtensionActivationErrorEvent = {
extensionId: string;
error: string;
};
this._telemetryService.publicLog2<ExtensionActivationErrorEvent, ExtensionActivationErrorClassification>('extensionActivationError', {
extensionId: extensionId.value,
error: error.message
});
}
public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void {
const extensionKey = ExtensionIdentifier.toKey(extensionId);
if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) {
@@ -771,33 +866,57 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
export class ExtensionRunningLocationClassifier {
constructor(
@IProductService private readonly _productService: IProductService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
public readonly pickRunningLocation: (extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean) => ExtensionRunningLocation,
public readonly getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
public readonly pickRunningLocation: (extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionRunningLocation,
) {
}
public determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation> {
const allExtensionKinds = new Map<string, ExtensionKind[]>();
localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService)));
remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService)));
localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), this.getExtensionKind(ext)));
remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), this.getExtensionKind(ext)));
const localExtensionsSet = new Set<string>();
localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const localUnderDevelopmentExtensionsSet = new Set<string>();
localExtensions.forEach((ext) => {
if (ext.isUnderDevelopment) {
localUnderDevelopmentExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier));
}
});
const remoteExtensionsSet = new Set<string>();
remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => {
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
return this.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely);
const remoteUnderDevelopmentExtensionsSet = new Set<string>();
remoteExtensions.forEach((ext) => {
if (ext.isUnderDevelopment) {
remoteUnderDevelopmentExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier));
}
});
const pickRunningLocation = (extensionIdentifier: ExtensionIdentifier): ExtensionRunningLocation => {
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extensionIdentifier));
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extensionIdentifier));
const isLocallyUnderDevelopment = localUnderDevelopmentExtensionsSet.has(ExtensionIdentifier.toKey(extensionIdentifier));
const isRemotelyUnderDevelopment = remoteUnderDevelopmentExtensionsSet.has(ExtensionIdentifier.toKey(extensionIdentifier));
let preference = ExtensionRunningPreference.None;
if (isLocallyUnderDevelopment && !isRemotelyUnderDevelopment) {
preference = ExtensionRunningPreference.Local;
} else if (isRemotelyUnderDevelopment && !isLocallyUnderDevelopment) {
preference = ExtensionRunningPreference.Remote;
}
const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extensionIdentifier)) || [];
return this.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference);
};
const runningLocation = new Map<string, ExtensionRunningLocation>();
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext.identifier)));
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext.identifier)));
return runningLocation;
}
}
@@ -817,7 +936,7 @@ class ProposedApiController {
this.enableProposedApiForAll =
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(!!_environmentService.extensionDevelopmentLocationURI && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this.enableProposedApiFor.length === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
this.productAllowProposedApi = new Set<string>();

View File

@@ -23,6 +23,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHost, ExtensionHostKind, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { timeout } from 'vs/base/common/async';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
@@ -70,7 +71,7 @@ export class ExtensionHostManager extends Disposable {
return { value: this._createExtensionHostCustomers(protocol) };
},
(err) => {
console.error('Error received from starting extension host');
console.error(`Error received from starting extension host (kind: ${this.kind})`);
console.error(err);
return null;
}
@@ -84,7 +85,7 @@ export class ExtensionHostManager extends Disposable {
this._resolveAuthorityAttempt = 0;
}
public dispose(): void {
public override dispose(): void {
if (this._extensionHost) {
this._extensionHost.dispose();
}
@@ -294,6 +295,28 @@ export class ExtensionHostManager extends Disposable {
return proxy.$startExtensionHost(enabledExtensionIds);
}
public async extensionTestsExecute(): Promise<number> {
const proxy = await this._getProxy();
if (!proxy) {
throw new Error('Could not obtain Extension Host Proxy');
}
return proxy.$extensionTestsExecute();
}
public async extensionTestsSendExit(exitCode: number): Promise<void> {
const proxy = await this._getProxy();
if (!proxy) {
return;
}
// This method does not wait for the actual RPC to be confirmed
// It waits for the socket to drain (i.e. the message has been sent)
// It also times out after 5s in case drain takes too long
proxy.$extensionTestsExit(exitCode);
if (this._rpcProtocol) {
await Promise.race([this._rpcProtocol.drain(), timeout(5000)]);
}
}
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
if (!proxy) {

View File

@@ -8,7 +8,6 @@ import { VSBuffer } from 'vs/base/common/buffer';
export const enum ExtensionHostExitCode {
// nodejs uses codes 1-13 and exit codes >128 are signal exits
VersionMismatch = 55,
StartTimeout60s = 56,
UnexpectedError = 81,
}

View File

@@ -0,0 +1,312 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManifest, ExtensionKind, ExtensionIdentifier, ExtensionUntrustedWorkpaceSupportType } from 'vs/platform/extensions/common/extensions';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IProductService } from 'vs/platform/product/common/productService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExtensionUntrustedWorkspaceSupport } from 'vs/base/common/product';
import { Disposable } from 'vs/base/common/lifecycle';
import { isWorkspaceTrustEnabled, WORKSPACE_TRUST_EXTENSION_SUPPORT } from 'vs/workbench/services/workspaces/common/workspaceTrust';
export const IExtensionManifestPropertiesService = createDecorator<IExtensionManifestPropertiesService>('extensionManifestPropertiesService');
export interface IExtensionManifestPropertiesService {
readonly _serviceBrand: undefined;
prefersExecuteOnUI(manifest: IExtensionManifest): boolean;
prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean;
prefersExecuteOnWeb(manifest: IExtensionManifest): boolean;
canExecuteOnUI(manifest: IExtensionManifest): boolean;
canExecuteOnWorkspace(manifest: IExtensionManifest): boolean;
canExecuteOnWeb(manifest: IExtensionManifest): boolean;
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[];
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkpaceSupportType;
canSupportVirtualWorkspace(manifest: IExtensionManifest): boolean;
}
export class ExtensionManifestPropertiesService extends Disposable implements IExtensionManifestPropertiesService {
readonly _serviceBrand: undefined;
private _uiExtensionPoints: Set<string> | null = null;
private _productExtensionKindsMap: Map<string, ExtensionKind[]> | null = null;
private _configuredExtensionKindsMap: Map<string, ExtensionKind | ExtensionKind[]> | null = null;
private _productVirtualWorkspaceSupportMap: Map<string, { default?: boolean, override?: boolean }> | null = null;
private _configuredVirtualWorkspaceSupportMap: Map<string, boolean> | null = null;
private readonly _configuredExtensionWorkspaceTrustRequestMap: Map<string, { supported: ExtensionUntrustedWorkpaceSupportType, version?: string }>;
private readonly _productExtensionWorkspaceTrustRequestMap: Map<string, ExtensionUntrustedWorkspaceSupport>;
constructor(
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
// Workspace trust request type (settings.json)
this._configuredExtensionWorkspaceTrustRequestMap = new Map<string, { supported: ExtensionUntrustedWorkpaceSupportType, version?: string }>();
const configuredExtensionWorkspaceTrustRequests = configurationService.inspect<{ [key: string]: { supported: ExtensionUntrustedWorkpaceSupportType, version?: string } }>(WORKSPACE_TRUST_EXTENSION_SUPPORT).userValue || {};
for (const id of Object.keys(configuredExtensionWorkspaceTrustRequests)) {
this._configuredExtensionWorkspaceTrustRequestMap.set(ExtensionIdentifier.toKey(id), configuredExtensionWorkspaceTrustRequests[id]);
}
// Workpace trust request type (products.json)
this._productExtensionWorkspaceTrustRequestMap = new Map<string, ExtensionUntrustedWorkspaceSupport>();
if (productService.extensionUntrustedWorkspaceSupport) {
for (const id of Object.keys(productService.extensionUntrustedWorkspaceSupport)) {
this._productExtensionWorkspaceTrustRequestMap.set(ExtensionIdentifier.toKey(id), productService.extensionUntrustedWorkspaceSupport[id]);
}
}
}
prefersExecuteOnUI(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'ui');
}
prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'workspace');
}
prefersExecuteOnWeb(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'web');
}
canExecuteOnUI(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'ui');
}
canExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'workspace');
}
canExecuteOnWeb(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'web');
}
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
// check in config
let result = this.getConfiguredExtensionKind(manifest);
if (typeof result !== 'undefined') {
return this.toArray(result);
}
// check product.json
result = this.getProductExtensionKind(manifest);
if (typeof result !== 'undefined') {
return result;
}
// check the manifest itself
result = manifest.extensionKind;
if (typeof result !== 'undefined') {
return this.toArray(result);
}
return this.deduceExtensionKind(manifest);
}
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkpaceSupportType {
// Workspace trust feature is disabled, or extension has no entry point
if (!isWorkspaceTrustEnabled(this.configurationService) || !manifest.main) {
return true;
}
// Get extension workspace trust requirements from settings.json
const configuredWorkspaceTrustRequest = this.getConfiguredExtensionWorkspaceTrustRequest(manifest);
// Get extension workspace trust requirements from product.json
const productWorkspaceTrustRequest = this.getProductExtensionWorkspaceTrustRequest(manifest);
// Use settings.json override value if it exists
if (configuredWorkspaceTrustRequest) {
return configuredWorkspaceTrustRequest;
}
// Use product.json override value if it exists
if (productWorkspaceTrustRequest?.override) {
return productWorkspaceTrustRequest.override;
}
// Use extension manifest value if it exists
if (manifest.capabilities?.untrustedWorkspaces?.supported !== undefined) {
return manifest.capabilities.untrustedWorkspaces.supported;
}
// Use product.json default value if it exists
if (productWorkspaceTrustRequest?.default) {
return productWorkspaceTrustRequest.default;
}
return false;
}
canSupportVirtualWorkspace(manifest: IExtensionManifest): boolean {
// check user configured
const userConfiguredVirtualWorkspaceSupport = this.getConfiguredVirtualWorkspaceSupport(manifest);
if (userConfiguredVirtualWorkspaceSupport !== undefined) {
return userConfiguredVirtualWorkspaceSupport;
}
const productConfiguredWorkspaceSchemes = this.getProductVirtualWorkspaceSupport(manifest);
// check override from product
if (productConfiguredWorkspaceSchemes?.override !== undefined) {
return productConfiguredWorkspaceSchemes.override;
}
// check the manifest
if (manifest.capabilities?.virtualWorkspaces !== undefined) {
return manifest.capabilities?.virtualWorkspaces;
}
// check default from product
if (productConfiguredWorkspaceSchemes?.default !== undefined) {
return productConfiguredWorkspaceSchemes.default;
}
// Default - supports virtual workspace
return true;
}
deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
// Not an UI extension if it has main
if (manifest.main) {
if (manifest.browser) {
return ['workspace', 'web'];
}
return ['workspace'];
}
if (manifest.browser) {
return ['web'];
}
// Not an UI nor web extension if it has dependencies or an extension pack
if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) {
return ['workspace'];
}
if (manifest.contributes) {
// Not an UI nor web extension if it has no ui contributions
for (const contribution of Object.keys(manifest.contributes)) {
if (!this.isUIExtensionPoint(contribution)) {
return ['workspace'];
}
}
}
return ['ui', 'workspace', 'web'];
}
private isUIExtensionPoint(extensionPoint: string): boolean {
if (this._uiExtensionPoints === null) {
const uiExtensionPoints = new Set<string>();
ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').forEach(e => {
uiExtensionPoints.add(e.name);
});
this._uiExtensionPoints = uiExtensionPoints;
}
return this._uiExtensionPoints.has(extensionPoint);
}
private getProductExtensionKind(manifest: IExtensionManifest): ExtensionKind[] | undefined {
if (this._productExtensionKindsMap === null) {
const productExtensionKindsMap = new Map<string, ExtensionKind[]>();
if (this.productService.extensionKind) {
for (const id of Object.keys(this.productService.extensionKind)) {
productExtensionKindsMap.set(ExtensionIdentifier.toKey(id), this.productService.extensionKind[id]);
}
}
this._productExtensionKindsMap = productExtensionKindsMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._productExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId));
}
private getConfiguredExtensionKind(manifest: IExtensionManifest): ExtensionKind | ExtensionKind[] | undefined {
if (this._configuredExtensionKindsMap === null) {
const configuredExtensionKindsMap = new Map<string, ExtensionKind | ExtensionKind[]>();
const configuredExtensionKinds = this.configurationService.getValue<{ [key: string]: ExtensionKind | ExtensionKind[] }>('remote.extensionKind') || {};
for (const id of Object.keys(configuredExtensionKinds)) {
configuredExtensionKindsMap.set(ExtensionIdentifier.toKey(id), configuredExtensionKinds[id]);
}
this._configuredExtensionKindsMap = configuredExtensionKindsMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._configuredExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId));
}
private getProductVirtualWorkspaceSupport(manifest: IExtensionManifest): { default?: boolean, override?: boolean } | undefined {
if (this._productVirtualWorkspaceSupportMap === null) {
const productWorkspaceSchemesMap = new Map<string, { default?: boolean, override?: boolean }>();
if (this.productService.extensionVirtualWorkspacesSupport) {
for (const id of Object.keys(this.productService.extensionVirtualWorkspacesSupport)) {
productWorkspaceSchemesMap.set(ExtensionIdentifier.toKey(id), this.productService.extensionVirtualWorkspacesSupport[id]);
}
}
this._productVirtualWorkspaceSupportMap = productWorkspaceSchemesMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._productVirtualWorkspaceSupportMap.get(ExtensionIdentifier.toKey(extensionId));
}
private getConfiguredVirtualWorkspaceSupport(manifest: IExtensionManifest): boolean | undefined {
if (this._configuredVirtualWorkspaceSupportMap === null) {
const configuredWorkspaceSchemesMap = new Map<string, boolean>();
const configuredWorkspaceSchemes = this.configurationService.getValue<{ [key: string]: boolean }>('extensions.supportVirtualWorkspaces') || {};
for (const id of Object.keys(configuredWorkspaceSchemes)) {
if (configuredWorkspaceSchemes[id] !== undefined) {
configuredWorkspaceSchemesMap.set(ExtensionIdentifier.toKey(id), configuredWorkspaceSchemes[id]);
}
}
this._configuredVirtualWorkspaceSupportMap = configuredWorkspaceSchemesMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._configuredVirtualWorkspaceSupportMap.get(ExtensionIdentifier.toKey(extensionId));
}
private getConfiguredExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkpaceSupportType | undefined {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const extensionWorkspaceTrustRequest = this._configuredExtensionWorkspaceTrustRequestMap.get(ExtensionIdentifier.toKey(extensionId));
if (extensionWorkspaceTrustRequest && (extensionWorkspaceTrustRequest.version === undefined || extensionWorkspaceTrustRequest.version === manifest.version)) {
return extensionWorkspaceTrustRequest.supported;
}
return undefined;
}
private getProductExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupport | undefined {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return this._productExtensionWorkspaceTrustRequestMap.get(ExtensionIdentifier.toKey(extensionId));
}
private toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[] {
if (Array.isArray(extensionKind)) {
return extensionKind;
}
return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind];
}
}
registerSingleton(IExtensionManifestPropertiesService, ExtensionManifestPropertiesService);

View File

@@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, IExtensionContributions } from 'vs/platform/extensions/common/extensions';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
@@ -41,8 +41,7 @@ export interface IExtensionsStatus {
runtimeErrors: Error[];
}
export type ExtensionActivationError = string | MissingDependencyError;
export class MissingDependencyError {
export class MissingExtensionDependency {
constructor(readonly dependency: string) { }
}
@@ -224,7 +223,7 @@ export interface IExtensionService {
/**
* Read all contributions to an extension point.
*/
readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]>;
readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]>;
/**
* Get information about extensions status.
@@ -237,10 +236,20 @@ export interface IExtensionService {
*/
getInspectPort(tryEnableInspector: boolean): Promise<number>;
/**
* Stops the extension hosts.
*/
stopExtensionHosts(): void;
/**
* Restarts the extension host.
*/
restartExtensionHost(): void;
restartExtensionHost(): Promise<void>;
/**
* Starts the extension hosts.
*/
startExtensionHosts(): Promise<void>;
/**
* Modify the environment of the remote extension host
@@ -248,12 +257,11 @@ export interface IExtensionService {
*/
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
_logOrShowMessage(severity: Severity, msg: string): void;
_activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
_onWillActivateExtension(extensionId: ExtensionIdentifier): void;
_onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
_onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void;
_onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void;
_onExtensionHostExit(code: number): void;
}
export interface ProfileSession {
@@ -307,14 +315,16 @@ export class NullExtensionService implements IExtensionService {
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); }
restartExtensionHost(): void { }
stopExtensionHosts(): void { }
async restartExtensionHost(): Promise<void> { }
async startExtensionHosts(): Promise<void> { }
async setRemoteEnvironment(_env: { [key: string]: string | null }): Promise<void> { }
canAddExtension(): boolean { return false; }
canRemoveExtension(): boolean { return false; }
_logOrShowMessage(_severity: Severity, _msg: string): void { }
_activateById(_extensionId: ExtensionIdentifier, _reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(); }
_onWillActivateExtension(_extensionId: ExtensionIdentifier): void { }
_onDidActivateExtension(_extensionId: ExtensionIdentifier, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationReason: ExtensionActivationReason): void { }
_onDidActivateExtensionError(_extensionId: ExtensionIdentifier, _error: Error): void { }
_onExtensionRuntimeError(_extensionId: ExtensionIdentifier, _err: Error): void { }
_onExtensionHostExit(code: number): void { }
}

View File

@@ -315,6 +315,11 @@ export const schema: IJSONSchema = {
body: 'onNotebook:${10:viewType}',
description: nls.localize('vscode.extension.activationEvents.onNotebook', 'An activation event emitted whenever the specified notebook document is opened.'),
},
{
label: 'onAuthenticationRequest',
body: 'onAuthenticationRequest:${11:authenticationProviderId}',
description: nls.localize('vscode.extension.activationEvents.onAuthenticationRequest', 'An activation event emitted whenever sessions are requested from the specified authentication provider.')
},
{
label: '*',
description: nls.localize('vscode.extension.activationEvents.star', 'An activation event emitted on VS Code startup. To ensure a great end user experience, please use this activation event in your extension only when no other activation events combination works in your use-case.'),
@@ -410,6 +415,48 @@ export const schema: IJSONSchema = {
}
]
},
capabilities: {
description: nls.localize('vscode.extension.capabilities', "Declare the set of supported capabilities by the extension."),
type: 'object',
properties: {
virtualWorkspaces: {
description: nls.localize('vscode.extension.capabilities.virtualWorkspaces', "Declares whether the extension should be enabled in virtual workspaces. A virtual workspace is a workspace which is not backed by any on-disk resources. When false, this extension will be automatically disabled in virtual workspaces. Default is true."),
type: 'boolean',
default: true
},
untrustedWorkspaces: {
description: nls.localize('vscode.extension.capabilities.untrustedWorkspaces', 'Declares how the extension should be handled in untrusted workspaces.'),
type: 'object',
required: ['supported'],
defaultSnippets: [
{ body: { supported: '${1:limited}', description: '${2}' } },
],
properties: {
supported: {
markdownDescription: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported', "Declares the level of support for untrusted workspaces by the extension."),
type: ['string', 'boolean'],
enum: ['limited', true, false],
enumDescriptions: [
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.limited', "The extension will be enabled in untrusted workspaces with some functionality disabled."),
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.true', "The extension will be enabled in untrusted workspaces with all functionality enabled."),
nls.localize('vscode.extension.capabilities.untrustedWorkspaces.supported.false', "The extension will not be enabled in untrusted workspaces."),
]
},
restrictedConfigurations: {
description: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.restrictedConfigurations', "A list of configuration keys contributed by the extension that should not use workspace values in untrusted workspaces."),
type: 'array',
items: {
type: 'string'
}
},
description: {
type: 'string',
markdownDescription: nls.localize('vscode.extension.capabilities.untrustedWorkspaces.description', "A description of how workspace trust affects the extensions behavior and why it is needed. This only applies when `supported` is not `true`."),
}
}
}
}
},
scripts: {
type: 'object',
properties: {

View File

@@ -10,56 +10,63 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IProductService } from 'vs/platform/product/common/productService';
export function prefersExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return (extensionKind.length > 0 && extensionKind[0] === 'ui');
}
export function prefersExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return (extensionKind.length > 0 && extensionKind[0] === 'workspace');
}
export function prefersExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return (extensionKind.length > 0 && extensionKind[0] === 'web');
}
export function canExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'ui');
}
export function canExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'workspace');
}
export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'web');
}
export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] {
// check in config
let result = getConfiguredExtensionKind(manifest, configurationService);
if (typeof result !== 'undefined') {
return toArray(result);
export class ExtensionKindController2 {
constructor(
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
}
prefersExecuteOnUI(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'ui');
}
// check product.json
result = getProductExtensionKind(manifest, productService);
if (typeof result !== 'undefined') {
return result;
prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'workspace');
}
// check the manifest itself
result = manifest.extensionKind;
if (typeof result !== 'undefined') {
return toArray(result);
prefersExecuteOnWeb(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return (extensionKind.length > 0 && extensionKind[0] === 'web');
}
return deduceExtensionKind(manifest);
canExecuteOnUI(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'ui');
}
canExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'workspace');
}
canExecuteOnWeb(manifest: IExtensionManifest): boolean {
const extensionKind = this.getExtensionKind(manifest);
return extensionKind.some(kind => kind === 'web');
}
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
// check in config
let result = getConfiguredExtensionKind(manifest, this.configurationService);
if (typeof result !== 'undefined') {
return toArray(result);
}
// check product.json
result = getProductExtensionKind(manifest, this.productService);
if (typeof result !== 'undefined') {
return result;
}
// check the manifest itself
result = manifest.extensionKind;
if (typeof result !== 'undefined') {
return toArray(result);
}
return deduceExtensionKind(manifest);
}
}
export function deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
@@ -141,3 +148,4 @@ function toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[
}
return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind];
}

View File

@@ -81,7 +81,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
this._hasLostConnection = false;
this._terminating = false;
this._register(this._lifecycleService.onShutdown(reason => this.dispose()));
this._register(this._lifecycleService.onDidShutdown(() => this.dispose()));
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
@@ -270,7 +270,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
return Promise.resolve(false);
}
dispose(): void {
override dispose(): void {
super.dispose();
this._terminating = true;

View File

@@ -110,7 +110,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
this._protocol.onMessage((msg) => this._receiveOneMessage(msg));
}
public dispose(): void {
public override dispose(): void {
this._isDisposed = true;
// Release all outstanding promises with a canceled error

View File

@@ -6,7 +6,7 @@
import { LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractExtensionService, ExtensionRunningLocation, ExtensionRunningLocationClassifier, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningLocation, ExtensionRunningLocationClassifier, ExtensionRunningPreference, parseScannedExtension } 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';
@@ -15,7 +15,7 @@ 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, RemoteTrustOption, 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';
@@ -42,6 +42,12 @@ import { Schemas } from 'vs/base/common/network';
import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { updateProxyConfigurationsScope } from 'vs/platform/request/common/request';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { Codicon } from 'vs/base/common/codicons';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
const MACHINE_PROMPT = false;
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@@ -52,7 +58,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@INotificationService notificationService: INotificationService,
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
@IWorkbenchEnvironmentService _environmentService: IWorkbenchEnvironmentService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,
@IFileService fileService: IFileService,
@@ -69,12 +75,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
@ILogService private readonly _logService: ILogService,
@IDialogService private readonly _dialogService: IDialogService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
) {
super(
new ExtensionRunningLocationClassifier(
productService,
configurationService,
(extensionKinds, isInstalledLocally, isInstalledRemotely) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely)
(extension) => this._getExtensionKind(extension),
(extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
),
instantiationService,
notificationService,
@@ -86,9 +94,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
extensionManagementService,
contextService,
configurationService,
extensionManifestPropertiesService
);
this._enableLocalWebWorker = this._configurationService.getValue<boolean>(webWorkerExtHostConfig);
this._enableLocalWebWorker = this._isLocalWebWorkerEnabled();
this._remoteInitData = new Map<string, IRemoteExtensionHostInitData>();
this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
@@ -106,6 +115,16 @@ export class ExtensionService extends AbstractExtensionService implements IExten
});
}
private _isLocalWebWorkerEnabled() {
if (this._configurationService.getValue<boolean>(webWorkerExtHostConfig)) {
return true;
}
if (this._environmentService.isExtensionDevelopment && this._environmentService.extensionDevelopmentKind?.some(k => k === 'web')) {
return true;
}
return false;
}
protected _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null> {
if (extension.location.scheme === Schemas.vscodeRemote) {
return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System);
@@ -155,26 +174,47 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
private _pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation {
private _pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
return ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker);
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledLocally) {
// ui extensions run locally if possible
return ExtensionRunningLocation.LocalProcess;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
}
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
}
}
if (extensionKind === 'workspace' && !this._environmentService.remoteAuthority) {
if (extensionKind === 'workspace' && !hasRemoteExtHost) {
// workspace extensions also run locally if there is no remote
return ExtensionRunningLocation.LocalProcess;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
}
}
if (extensionKind === 'web' && isInstalledLocally && this._enableLocalWebWorker) {
if (extensionKind === 'web' && isInstalledLocally && hasWebWorkerExtHost) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
}
}
}
return ExtensionRunningLocation.None;
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
}
protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] {
@@ -197,7 +237,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
return result;
}
protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
protected override _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
const activatedExtensions = Array.from(this._extensionHostActiveExtensions.values());
super._onExtensionHostCrashed(extensionHost, code, signal);
@@ -229,7 +269,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
},
{
label: nls.localize('restart', "Restart Extension Host"),
run: () => this.startExtensionHost()
run: () => this.startExtensionHosts()
}]
);
@@ -328,6 +368,38 @@ export class ExtensionService extends AbstractExtensionService implements IExten
return;
}
let promptForMachineTrust = MACHINE_PROMPT;
if (resolverResult.options?.trust === RemoteTrustOption.DisableTrust) {
promptForMachineTrust = false;
this._workspaceTrustManagementService.setWorkspaceTrust(true);
} else if (resolverResult.options?.trust === RemoteTrustOption.MachineTrusted) {
promptForMachineTrust = false;
}
if (promptForMachineTrust) {
const dialogResult = await this._dialogService.show(
Severity.Info,
nls.localize('machineTrustQuestion', "Do you trust the machine you're connecting to?"),
[nls.localize('yes', "Yes, connect."), nls.localize('no', "No, do not connect.")],
{
cancelId: 1,
custom: {
icon: Codicon.remoteExplorer
},
// checkbox: { label: nls.localize('remember', "Remember my choice"), checked: true }
}
);
if (dialogResult.choice !== 0) {
// Did not confirm trust
this._notificationService.notify({ severity: Severity.Warning, message: nls.localize('trustFailure', "Refused to connect to untrusted machine.") });
// Proceed with the local extension host
await this._startLocalExtensionHost(localExtensions);
return;
}
}
// set the resolved authority
this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);
this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);
@@ -403,7 +475,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
}
public async getInspectPort(tryEnableInspector: boolean): Promise<number> {
public override async getInspectPort(tryEnableInspector: boolean): Promise<number> {
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
return localProcessExtensionHost.getInspectPort(tryEnableInspector);
@@ -413,7 +485,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
public _onExtensionHostExit(code: number): void {
// Dispose everything associated with the extension host
this._stopExtensionHosts();
this.stopExtensionHosts();
if (this._isExtensionDevTestFromCli) {
// When CLI testing make sure to exit with proper exit code

View File

@@ -47,6 +47,7 @@ import { isUUID } from 'vs/base/common/uuid';
import { join } from 'vs/base/common/path';
import { Readable, Writable } from 'stream';
import { StringDecoder } from 'string_decoder';
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
export interface ILocalProcessExtensionHostInitData {
readonly autoStart: boolean;
@@ -104,7 +105,8 @@ export class LocalProcessExtensionHost implements IExtensionHost {
@ILabelService private readonly _labelService: ILabelService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
@IHostService private readonly _hostService: IHostService,
@IProductService private readonly _productService: IProductService
@IProductService private readonly _productService: IProductService,
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService
) {
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
@@ -125,7 +127,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
this._toDispose.add(this._onExit);
this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e)));
this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate()));
this._toDispose.add(this._lifecycleService.onDidShutdown(reason => this.terminate()));
this._toDispose.add(this._extensionHostDebugService.onClose(event => {
if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) {
this._nativeHostService.closeWindow();
@@ -157,9 +159,10 @@ export class LocalProcessExtensionHost implements IExtensionHost {
if (!this._messageProtocol) {
this._messageProtocol = Promise.all([
this._tryListenOnPipe(),
this._tryFindDebugPort()
]).then(([pipeName, portNumber]) => {
const env = objects.mixin(objects.deepClone(process.env), {
this._tryFindDebugPort(),
this._shellEnvironmentService.getShellEnv()
]).then(([pipeName, portNumber, processEnv]) => {
const env = objects.mixin(processEnv, {
VSCODE_AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: true,
@@ -206,6 +209,10 @@ export class LocalProcessExtensionHost implements IExtensionHost {
opts.execArgv.unshift('--prof');
}
if (this._environmentService.args['max-memory']) {
opts.execArgv.unshift(`--max-old-space-size=${this._environmentService.args['max-memory']}`);
}
// On linux crash reporter needs to be started on child node processes explicitly
if (platform.isLinux) {
const crashReporterStartOptions: CrashReporterStartOptions = {

View File

@@ -105,7 +105,7 @@ function _createExtHostProtocol(): Promise<PersistentProtocol> {
let protocol: PersistentProtocol | null = null;
let timer = setTimeout(() => {
reject(new Error('VSCODE_EXTHOST_IPC_SOCKET timeout'));
onTerminate('VSCODE_EXTHOST_IPC_SOCKET timeout');
}, 60000);
const reconnectionGraceTime = ProtocolConstants.ReconnectionGraceTime;

View File

@@ -6,27 +6,15 @@
import * as http from 'http';
import * as https from 'https';
import * as tls from 'tls';
import * as nodeurl from 'url';
import * as os from 'os';
import * as fs from 'fs';
import * as cp from 'child_process';
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ProxyAgent } from 'vscode-proxy-agent';
import { MainThreadTelemetryShape, IInitData } from 'vs/workbench/api/common/extHost.protocol';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
interface ConnectionResult {
proxy: string;
connection: string;
code: string;
count: number;
}
import { LogLevel, createHttpPatch, ProxyResolveEvent, createProxyResolver, createTlsPatch, ProxySupportSetting } from 'vscode-proxy-agent';
export function connectProxyResolver(
extHostWorkspace: IExtHostWorkspaceProvider,
@@ -36,269 +24,59 @@ export function connectProxyResolver(
mainThreadTelemetry: MainThreadTelemetryShape,
initData: IInitData,
) {
const resolveProxy = setupProxyResolution(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry, initData);
const useHostProxy = initData.environment.useHostProxy;
const doUseHostProxy = typeof useHostProxy === 'boolean' ? useHostProxy : !initData.remote.isRemote;
const resolveProxy = createProxyResolver({
resolveProxy: url => extHostWorkspace.resolveProxy(url),
getHttpProxySetting: () => configProvider.getConfiguration('http').get('proxy'),
log: (level, message, ...args) => {
switch (level) {
case LogLevel.Trace: extHostLogService.trace(message, ...args); break;
case LogLevel.Debug: extHostLogService.debug(message, ...args); break;
case LogLevel.Info: extHostLogService.info(message, ...args); break;
case LogLevel.Warning: extHostLogService.warn(message, ...args); break;
case LogLevel.Error: extHostLogService.error(message, ...args); break;
case LogLevel.Critical: extHostLogService.critical(message, ...args); break;
case LogLevel.Off: break;
default: never(level, message, args); break;
}
function never(level: never, message: string, ...args: any[]) {
extHostLogService.error('Unknown log level', level);
extHostLogService.error(message, ...args);
}
},
getLogLevel: () => extHostLogService.getLevel(),
proxyResolveTelemetry: event => {
type ResolveProxyClassification = {
count: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
duration: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
errorCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheSize: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheRolls: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
envCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
settingsCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
localhostCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
envNoProxyCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
results: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
};
mainThreadTelemetry.$publicLog2<ProxyResolveEvent, ResolveProxyClassification>('resolveProxy', event);
},
useHostProxy: doUseHostProxy,
env: process.env,
});
const lookup = createPatchedModules(configProvider, resolveProxy);
return configureModuleLoading(extensionService, lookup);
}
const maxCacheEntries = 5000; // Cache can grow twice that much due to 'oldCache'.
function setupProxyResolution(
extHostWorkspace: IExtHostWorkspaceProvider,
configProvider: ExtHostConfigProvider,
extHostLogService: ILogService,
mainThreadTelemetry: MainThreadTelemetryShape,
initData: IInitData,
) {
const env = process.env;
let settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http')
.get<string>('proxy'));
configProvider.onDidChangeConfiguration(e => {
settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http')
.get<string>('proxy'));
});
let envProxy = proxyFromConfigURL(env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY); // Not standardized.
let envNoProxy = noProxyFromEnv(env.no_proxy || env.NO_PROXY); // Not standardized.
let cacheRolls = 0;
let oldCache = new Map<string, string>();
let cache = new Map<string, string>();
function getCacheKey(url: nodeurl.UrlWithStringQuery) {
// Expecting proxies to usually be the same per scheme://host:port. Assuming that for performance.
return nodeurl.format({ ...url, ...{ pathname: undefined, search: undefined, hash: undefined } });
}
function getCachedProxy(key: string) {
let proxy = cache.get(key);
if (proxy) {
return proxy;
}
proxy = oldCache.get(key);
if (proxy) {
oldCache.delete(key);
cacheProxy(key, proxy);
}
return proxy;
}
function cacheProxy(key: string, proxy: string) {
cache.set(key, proxy);
if (cache.size >= maxCacheEntries) {
oldCache = cache;
cache = new Map();
cacheRolls++;
extHostLogService.trace('ProxyResolver#cacheProxy cacheRolls', cacheRolls);
}
}
let timeout: NodeJS.Timer | undefined;
let count = 0;
let duration = 0;
let errorCount = 0;
let cacheCount = 0;
let envCount = 0;
let settingsCount = 0;
let localhostCount = 0;
let envNoProxyCount = 0;
let results: ConnectionResult[] = [];
function logEvent() {
timeout = undefined;
type ResolveProxyClassification = {
count: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
duration: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
errorCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheSize: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
cacheRolls: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
envCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
settingsCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
localhostCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
envNoProxyCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
results: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
};
type ResolveProxyEvent = {
count: number;
duration: number;
errorCount: number;
cacheCount: number;
cacheSize: number;
cacheRolls: number;
envCount: number;
settingsCount: number;
localhostCount: number;
envNoProxyCount: number;
results: ConnectionResult[];
};
mainThreadTelemetry.$publicLog2<ResolveProxyEvent, ResolveProxyClassification>('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, envNoProxyCount, results });
count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = envNoProxyCount = 0;
results = [];
}
function resolveProxy(flags: { useProxySettings: boolean, useSystemCertificates: boolean }, req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) {
if (!timeout) {
timeout = setTimeout(logEvent, 10 * 60 * 1000);
}
const useHostProxy = initData.environment.useHostProxy;
const doUseHostProxy = typeof useHostProxy === 'boolean' ? useHostProxy : !initData.remote.isRemote;
useSystemCertificates(extHostLogService, flags.useSystemCertificates, opts, () => {
useProxySettings(doUseHostProxy, flags.useProxySettings, req, opts, url, callback);
});
}
function useProxySettings(useHostProxy: boolean, useProxySettings: boolean, req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) {
if (!useProxySettings) {
callback('DIRECT');
return;
}
const parsedUrl = nodeurl.parse(url); // Coming from Node's URL, sticking with that.
const hostname = parsedUrl.hostname;
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '::ffff:127.0.0.1') {
localhostCount++;
callback('DIRECT');
extHostLogService.trace('ProxyResolver#resolveProxy localhost', url, 'DIRECT');
return;
}
if (typeof hostname === 'string' && envNoProxy(hostname, String(parsedUrl.port || (<any>opts.agent).defaultPort))) {
envNoProxyCount++;
callback('DIRECT');
extHostLogService.trace('ProxyResolver#resolveProxy envNoProxy', url, 'DIRECT');
return;
}
if (settingsProxy) {
settingsCount++;
callback(settingsProxy);
extHostLogService.trace('ProxyResolver#resolveProxy settings', url, settingsProxy);
return;
}
if (envProxy) {
envCount++;
callback(envProxy);
extHostLogService.trace('ProxyResolver#resolveProxy env', url, envProxy);
return;
}
const key = getCacheKey(parsedUrl);
const proxy = getCachedProxy(key);
if (proxy) {
cacheCount++;
collectResult(results, proxy, parsedUrl.protocol === 'https:' ? 'HTTPS' : 'HTTP', req);
callback(proxy);
extHostLogService.trace('ProxyResolver#resolveProxy cached', url, proxy);
return;
}
if (!useHostProxy) {
callback('DIRECT');
extHostLogService.trace('ProxyResolver#resolveProxy unconfigured', url, 'DIRECT');
return;
}
const start = Date.now();
extHostWorkspace.resolveProxy(url) // Use full URL to ensure it is an actually used one.
.then(proxy => {
if (proxy) {
cacheProxy(key, proxy);
collectResult(results, proxy, parsedUrl.protocol === 'https:' ? 'HTTPS' : 'HTTP', req);
}
callback(proxy);
extHostLogService.debug('ProxyResolver#resolveProxy', url, proxy);
}).then(() => {
count++;
duration = Date.now() - start + duration;
}, err => {
errorCount++;
callback();
extHostLogService.error('ProxyResolver#resolveProxy', toErrorMessage(err));
});
}
return resolveProxy;
}
function collectResult(results: ConnectionResult[], resolveProxy: string, connection: string, req: http.ClientRequest) {
const proxy = resolveProxy ? String(resolveProxy).trim().split(/\s+/, 1)[0] : 'EMPTY';
req.on('response', res => {
const code = `HTTP_${res.statusCode}`;
const result = findOrCreateResult(results, proxy, connection, code);
result.count++;
});
req.on('error', err => {
const code = err && typeof (<any>err).code === 'string' && (<any>err).code || 'UNKNOWN_ERROR';
const result = findOrCreateResult(results, proxy, connection, code);
result.count++;
});
}
function findOrCreateResult(results: ConnectionResult[], proxy: string, connection: string, code: string): ConnectionResult {
for (const result of results) {
if (result.proxy === proxy && result.connection === connection && result.code === code) {
return result;
}
}
const result = { proxy, connection, code, count: 0 };
results.push(result);
return result;
}
function proxyFromConfigURL(configURL: string | undefined) {
const url = (configURL || '').trim();
const i = url.indexOf('://');
if (i === -1) {
return undefined;
}
const scheme = url.substr(0, i).toLowerCase();
const proxy = url.substr(i + 3);
if (scheme === 'http') {
return 'PROXY ' + proxy;
} else if (scheme === 'https') {
return 'HTTPS ' + proxy;
} else if (scheme === 'socks') {
return 'SOCKS ' + proxy;
}
return undefined;
}
function noProxyFromEnv(envValue?: string) {
const value = (envValue || '')
.trim()
.toLowerCase();
if (value === '*') {
return () => true;
}
const filters = value
.split(',')
.map(s => s.trim().split(':', 2))
.map(([name, port]) => ({ name, port }))
.filter(filter => !!filter.name)
.map(({ name, port }) => {
const domain = name[0] === '.' ? name : `.${name}`;
return { domain, port };
});
if (!filters.length) {
return () => false;
}
return (hostname: string, port: string) => filters.some(({ domain, port: filterPort }) => {
return `.${hostname.toLowerCase()}`.endsWith(domain) && (!filterPort || port === filterPort);
});
}
function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProxy: ReturnType<typeof setupProxyResolution>) {
function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProxy: ReturnType<typeof createProxyResolver>) {
const proxySetting = {
config: configProvider.getConfiguration('http')
.get<string>('proxySupport') || 'off'
.get<ProxySupportSetting>('proxySupport') || 'off'
};
configProvider.onDidChangeConfiguration(e => {
proxySetting.config = configProvider.getConfiguration('http')
.get<string>('proxySupport') || 'off';
.get<ProxySupportSetting>('proxySupport') || 'off';
});
const certSetting = {
config: !!configProvider.getConfiguration('http')
@@ -311,111 +89,30 @@ function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProx
return {
http: {
off: Object.assign({}, http, patches(http, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, http, patches(http, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, http, patches(http, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, http, patches(http, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(http, patches(http, resolveProxy, proxySetting, certSetting, false)) // run last
off: Object.assign({}, http, createHttpPatch(http, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, http, createHttpPatch(http, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, http, createHttpPatch(http, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, http, createHttpPatch(http, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(http, createHttpPatch(http, resolveProxy, proxySetting, certSetting, false)) // run last
} as Record<string, typeof http>,
https: {
off: Object.assign({}, https, patches(https, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, https, patches(https, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, https, patches(https, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, https, patches(https, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(https, patches(https, resolveProxy, proxySetting, certSetting, false)) // run last
off: Object.assign({}, https, createHttpPatch(https, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, https, createHttpPatch(https, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, https, createHttpPatch(https, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, https, createHttpPatch(https, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(https, createHttpPatch(https, resolveProxy, proxySetting, certSetting, false)) // run last
} as Record<string, typeof https>,
tls: Object.assign(tls, tlsPatches(tls))
tls: Object.assign(tls, createTlsPatch(tls))
};
}
function patches(originals: typeof http | typeof https, resolveProxy: ReturnType<typeof setupProxyResolution>, proxySetting: { config: string }, certSetting: { config: boolean }, onRequest: boolean) {
return {
get: patch(originals.get),
request: patch(originals.request)
};
function patch(original: typeof http.get) {
function patched(url?: string | URL | null, options?: http.RequestOptions | null, callback?: (res: http.IncomingMessage) => void): http.ClientRequest {
if (typeof url !== 'string' && !(url && (<any>url).searchParams)) {
callback = <any>options;
options = url;
url = null;
}
if (typeof options === 'function') {
callback = options;
options = null;
}
options = options || {};
if (options.socketPath) {
return original.apply(null, arguments as any);
}
const originalAgent = options.agent;
if (originalAgent === true) {
throw new Error('Unexpected agent option: true');
}
const optionsPatched = originalAgent instanceof ProxyAgent;
const config = onRequest && ((<any>options)._vscodeProxySupport || /* LS */ (<any>options)._vscodeSystemProxy) || proxySetting.config;
const useProxySettings = !optionsPatched && (config === 'override' || config === 'on' && originalAgent === undefined);
const useSystemCertificates = !optionsPatched && certSetting.config && originals === https && !(options as https.RequestOptions).ca;
if (useProxySettings || useSystemCertificates) {
if (url) {
const parsed = typeof url === 'string' ? new nodeurl.URL(url) : url;
const urlOptions = {
protocol: parsed.protocol,
hostname: parsed.hostname.lastIndexOf('[', 0) === 0 ? parsed.hostname.slice(1, -1) : parsed.hostname,
port: parsed.port,
path: `${parsed.pathname}${parsed.search}`
};
if (parsed.username || parsed.password) {
options.auth = `${parsed.username}:${parsed.password}`;
}
options = { ...urlOptions, ...options };
} else {
options = { ...options };
}
options.agent = new ProxyAgent({
resolveProxy: resolveProxy.bind(undefined, { useProxySettings, useSystemCertificates }),
defaultPort: originals === https ? 443 : 80,
originalAgent
});
return original(options, callback);
}
return original.apply(null, arguments as any);
}
return patched;
}
}
function tlsPatches(originals: typeof tls) {
return {
createSecureContext: patch(originals.createSecureContext)
};
function patch(original: typeof tls.createSecureContext): typeof tls.createSecureContext {
return function (details?: tls.SecureContextOptions): ReturnType<typeof tls.createSecureContext> {
const context = original.apply(null, arguments as any);
const certs = (details as any)._vscodeAdditionalCaCerts;
if (certs) {
for (const cert of certs) {
context.context.addCACert(cert);
}
}
return context;
};
}
}
const modulesCache = new Map<IExtensionDescription | undefined, { http?: typeof http, https?: typeof https }>();
function configureModuleLoading(extensionService: ExtHostExtensionService, lookup: ReturnType<typeof createPatchedModules>): Promise<void> {
return extensionService.getExtensionPathIndex()
.then(extensionPaths => {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: any, isMain: any) {
node_module._load = function load(request: string, parent: { filename: string; }, isMain: boolean) {
if (request === 'tls') {
return lookup.tls;
}
@@ -441,126 +138,3 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku
};
});
}
function useSystemCertificates(extHostLogService: ILogService, useSystemCertificates: boolean, opts: http.RequestOptions, callback: () => void) {
if (useSystemCertificates) {
getCaCertificates(extHostLogService)
.then(caCertificates => {
if (caCertificates) {
if (caCertificates.append) {
(opts as any)._vscodeAdditionalCaCerts = caCertificates.certs;
} else {
(opts as https.RequestOptions).ca = caCertificates.certs;
}
}
callback();
})
.catch(err => {
extHostLogService.error('ProxyResolver#useSystemCertificates', toErrorMessage(err));
});
} else {
callback();
}
}
let _caCertificates: ReturnType<typeof readCaCertificates> | Promise<undefined>;
async function getCaCertificates(extHostLogService: ILogService) {
if (!_caCertificates) {
_caCertificates = readCaCertificates()
.then(res => {
extHostLogService.debug('ProxyResolver#getCaCertificates count', res && res.certs.length);
return res && res.certs.length ? res : undefined;
})
.catch(err => {
extHostLogService.error('ProxyResolver#getCaCertificates error', toErrorMessage(err));
return undefined;
});
}
return _caCertificates;
}
async function readCaCertificates() {
if (process.platform === 'win32') {
return readWindowsCaCertificates();
}
if (process.platform === 'darwin') {
return readMacCaCertificates();
}
if (process.platform === 'linux') {
return readLinuxCaCertificates();
}
return undefined;
}
async function readWindowsCaCertificates() {
// @ts-ignore Windows only
const winCA = await import('vscode-windows-ca-certs');
let ders: any[] = [];
const store = new winCA.Crypt32();
try {
let der: any;
while (der = store.next()) {
ders.push(der);
}
} finally {
store.done();
}
const certs = new Set(ders.map(derToPem));
return {
certs: Array.from(certs),
append: true
};
}
async function readMacCaCertificates() {
const stdout = await new Promise<string>((resolve, reject) => {
const child = cp.spawn('/usr/bin/security', ['find-certificate', '-a', '-p']);
const stdout: string[] = [];
child.stdout.setEncoding('utf8');
child.stdout.on('data', str => stdout.push(str));
child.on('error', reject);
child.on('exit', code => code ? reject(code) : resolve(stdout.join('')));
});
const certs = new Set(stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g)
.filter(pem => !!pem.length));
return {
certs: Array.from(certs),
append: true
};
}
const linuxCaCertificatePaths = [
'/etc/ssl/certs/ca-certificates.crt',
'/etc/ssl/certs/ca-bundle.crt',
];
async function readLinuxCaCertificates() {
for (const certPath of linuxCaCertificatePaths) {
try {
const content = await fs.promises.readFile(certPath, { encoding: 'utf8' });
const certs = new Set(content.split(/(?=-----BEGIN CERTIFICATE-----)/g)
.filter(pem => !!pem.length));
return {
certs: Array.from(certs),
append: false
};
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
}
return undefined;
}
function derToPem(blob: Buffer) {
const lines = ['-----BEGIN CERTIFICATE-----'];
const der = blob.toString('base64');
for (let i = 0; i < der.length; i += 64) {
lines.push(der.substr(i, 64));
}
lines.push('-----END CERTIFICATE-----', '');
return lines.join(os.EOL);
}

View File

@@ -4,85 +4,86 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService';
import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService';
suite('BrowserExtensionService', () => {
test('pickRunningLocation', () => {
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, true), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, true), ExtensionRunningLocation.None);
assert(true); // {{SQL CARBON EDIT}} Workaround for error loading test modules when there's no imports in the file
/* {{SQL CARBON EDIT}} Disable broken tests for unused service
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(['ui'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true), ExtensionRunningLocation.Remote);
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(['workspace'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true), ExtensionRunningLocation.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(['web'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
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(['ui', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true), ExtensionRunningLocation.Remote);
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(['web', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
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(['ui', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
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.Remote);
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', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true), ExtensionRunningLocation.Remote);
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.Remote);
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(['web', 'ui', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker);
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.Remote);
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.Remote);
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(['workspace', 'ui', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true), ExtensionRunningLocation.Remote);
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);
*/
});
});

View File

@@ -0,0 +1,155 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { IExtensionManifest, ExtensionKind, ExtensionUntrustedWorkpaceSupportType } from 'vs/platform/extensions/common/extensions';
import { ExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { isWeb } from 'vs/base/common/platform';
suite('ExtensionManifestPropertiesService - ExtensionKind', () => {
function check(manifest: Partial<IExtensionManifest>, expected: ExtensionKind[]): void {
const extensionManifestPropertiesService = new ExtensionManifestPropertiesService(TestProductService, new TestConfigurationService());
assert.deepStrictEqual(extensionManifestPropertiesService.deduceExtensionKind(<IExtensionManifest>manifest), expected);
}
test('declarative with extension dependencies => workspace', () => {
check({ extensionDependencies: ['ext1'] }, ['workspace']);
});
test('declarative extension pack => workspace', () => {
check({ extensionPack: ['ext1', 'ext2'] }, ['workspace']);
});
test('declarative with unknown contribution point => workspace', () => {
check({ contributes: <any>{ 'unknownPoint': { something: true } } }, ['workspace']);
});
test('simple declarative => ui, workspace, web', () => {
check({}, ['ui', 'workspace', 'web']);
});
test('only browser => web', () => {
check({ browser: 'main.browser.js' }, ['web']);
});
test('only main => workspace', () => {
check({ main: 'main.js' }, ['workspace']);
});
test('main and browser => workspace, web', () => {
check({ main: 'main.js', browser: 'main.browser.js' }, ['workspace', 'web']);
});
});
// Workspace Trust is disabled in web at the moment
if (!isWeb) {
suite('ExtensionManifestPropertiesService - ExtensionUntrustedWorkpaceSupportType', () => {
let testObject: ExtensionManifestPropertiesService;
let instantiationService: TestInstantiationService;
let testConfigurationService: TestConfigurationService;
setup(async () => {
instantiationService = new TestInstantiationService();
testConfigurationService = new TestConfigurationService();
instantiationService.stub(IConfigurationService, testConfigurationService);
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { enabled: true } } });
});
teardown(() => testObject.dispose());
function assertUntrustedWorkspaceSupport(extensionMaifest: IExtensionManifest, expected: ExtensionUntrustedWorkpaceSupportType): void {
testObject = instantiationService.createInstance(ExtensionManifestPropertiesService);
const untrustedWorkspaceSupport = testObject.getExtensionUntrustedWorkspaceSupportType(extensionMaifest);
assert.strictEqual(untrustedWorkspaceSupport, expected);
}
function getExtensionManifest(properties: any = {}): IExtensionManifest {
return Object.create({ name: 'a', publisher: 'pub', version: '1.0.0', ...properties }) as IExtensionManifest;
}
test('test extension workspace trust request when main entry point is missing', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
const extensionMaifest = getExtensionManifest();
assertUntrustedWorkspaceSupport(extensionMaifest, true);
});
test('test extension workspace trust request when workspace trust is disabled', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { enabled: false } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
});
test('test extension workspace trust request when override exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionUntrustedSupport: { 'pub.a': { supported: true } } } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
});
test('test extension workspace trust request when override for the version exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionUntrustedSupport: { 'pub.a': { supported: true, version: '1.0.0' } } } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
});
test('test extension workspace trust request when override for a different version exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
await testConfigurationService.setUserConfiguration('security', {
workspace: {
trust: {
enabled: true,
extensionUntrustedSupport: { 'pub.a': { supported: true, version: '2.0.0' } }
}
}
});
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } });
assertUntrustedWorkspaceSupport(extensionMaifest, 'limited');
});
test('test extension workspace trust request when default exists in product.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{ extensionUntrustedWorkspaceSupport: { 'pub.a': { default: true } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
});
test('test extension workspace trust request when override exists in product.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{ extensionUntrustedWorkspaceSupport: { 'pub.a': { override: 'limited' } } });
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } });
assertUntrustedWorkspaceSupport(extensionMaifest, 'limited');
});
test('test extension workspace trust request when value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } });
assertUntrustedWorkspaceSupport(extensionMaifest, 'limited');
});
test('test extension workspace trust request when no value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, false);
});
});
}

View File

@@ -1,43 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { deduceExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IExtensionManifest, ExtensionKind } from 'vs/platform/extensions/common/extensions';
suite('ExtensionKind', () => {
function check(manifest: Partial<IExtensionManifest>, expected: ExtensionKind[]): void {
assert.deepStrictEqual(deduceExtensionKind(<IExtensionManifest>manifest), expected);
}
test('declarative with extension dependencies => workspace', () => {
check({ extensionDependencies: ['ext1'] }, ['workspace']);
});
test('declarative extension pack => workspace', () => {
check({ extensionPack: ['ext1', 'ext2'] }, ['workspace']);
});
test('declarative with unknown contribution point => workspace', () => {
check({ contributes: <any>{ 'unknownPoint': { something: true } } }, ['workspace']);
});
test('simple declarative => ui, workspace, web', () => {
check({}, ['ui', 'workspace', 'web']);
});
test('only browser => web', () => {
check({ browser: 'main.browser.js' }, ['web']);
});
test('only main => workspace', () => {
check({ main: 'main.js' }, ['workspace']);
});
test('main and browser => workspace, web', () => {
check({ main: 'main.js', browser: 'main.browser.js' }, ['workspace', 'web']);
});
});

View File

@@ -1,88 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService';
import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService';
suite('BrowserExtensionService', () => {
test('pickRunningLocation', () => {
assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, true), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, true), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false), ExtensionRunningLocation.None);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true), ExtensionRunningLocation.Remote);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker);
assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true), ExtensionRunningLocation.Remote);
});
});

View File

@@ -5,24 +5,69 @@
(function () {
let MonacoEnvironment = (<any>self).MonacoEnvironment;
let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../';
const MonacoEnvironment = (<any>self).MonacoEnvironment;
const monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../';
const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value });
const trustedTypesPolicy = (
typeof self.trustedTypes?.createPolicy === 'function'
? self.trustedTypes?.createPolicy('amdLoader', {
createScriptURL: value => value,
createScript: (_, ...args: string[]) => {
// workaround a chrome issue not allowing to create new functions
// see https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
const fnArgs = args.slice(0, -1).join(',');
const fnBody = args.pop()!.toString();
const body = `(function anonymous(${fnArgs}) {\n${fnBody}\n})`;
return body;
}
})
: undefined
);
if (typeof (<any>self).define !== 'function' || !(<any>self).define.amd) {
let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js';
if (trustedTypesPolicy) {
loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc);
}
importScripts(loaderSrc as string);
function loadAMDLoader() {
return new Promise<void>((resolve, reject) => {
if (typeof (<any>self).define === 'function' && (<any>self).define.amd) {
return resolve();
}
const loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js';
const isCrossOrigin = (/^((http:)|(https:)|(file:))/.test(loaderSrc) && loaderSrc.substring(0, self.origin.length) !== self.origin);
if (!isCrossOrigin) {
// use `fetch` if possible because `importScripts`
// is synchronous and can lead to deadlocks on Safari
fetch(loaderSrc).then((response) => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response.text();
}).then((text) => {
text = `${text}\n//# sourceURL=${loaderSrc}`;
const func = (
trustedTypesPolicy
? self.eval(trustedTypesPolicy.createScript('', text) as unknown as string)
: new Function(text)
);
func.call(self);
resolve();
}).then(undefined, reject);
return;
}
if (trustedTypesPolicy) {
importScripts(trustedTypesPolicy.createScriptURL(loaderSrc) as unknown as string);
} else {
importScripts(loaderSrc as string);
}
resolve();
});
}
require.config({
baseUrl: monacoBaseUrl,
catchError: true,
trustedTypesPolicy
});
require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err));
loadAMDLoader().then(() => {
require.config({
baseUrl: monacoBaseUrl,
catchError: true,
trustedTypesPolicy
});
require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err));
}).then(undefined, (err) => console.error(err));
})();