Merge from vscode e3c4990c67c40213af168300d1cfeb71d680f877 (#16569)

This commit is contained in:
Cory Rivera
2021-08-25 16:28:29 -07:00
committed by GitHub
parent ab1112bfb3
commit cb7b7da0a4
1752 changed files with 59525 additions and 33878 deletions

View File

@@ -185,8 +185,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._remoteAgentService.getEnvironment(),
this._remoteAgentService.scanExtensions()
]);
localExtensions = this._checkEnabledAndProposedAPI(localExtensions);
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions);
localExtensions = this._checkEnabledAndProposedAPI(localExtensions, false);
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions, false);
const remoteAgentConnection = this._remoteAgentService.getConnection();
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);

View File

@@ -8,7 +8,7 @@ import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/li
import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -263,7 +263,13 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
// Extension is not installed
else {
const galleryExtension = await this.galleryService.getCompatibleExtension(extensionIdentifier);
let galleryExtension: IGalleryExtension | undefined;
try {
galleryExtension = await this.galleryService.getCompatibleExtension(extensionIdentifier) ?? undefined;
} catch (err) {
return;
}
if (!galleryExtension) {
return;
@@ -285,7 +291,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
await this.progressService.withProgress({
location: ProgressLocation.Notification,
title: localize('Installing', "Installing Extension '{0}'...", galleryExtension.displayName || galleryExtension.name)
}, () => this.extensionManagementService.installFromGallery(galleryExtension));
}, () => this.extensionManagementService.installFromGallery(galleryExtension!));
this.notificationService.prompt(
Severity.Info,

View File

@@ -361,8 +361,6 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: this._environmentService.globalStorageHome,
workspaceStorageHome: this._environmentService.workspaceStorageHome,
webviewResourceRoot: this._environmentService.webviewResourceRoot,
webviewCspSource: this._environmentService.webviewCspSource,
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: workspace.configuration || undefined,

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Barrier } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import * as perf from 'vs/base/common/performance';
import { isEqualOrParent } from 'vs/base/common/resources';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -51,7 +51,7 @@ export function parseScannedExtension(extension: ITranslatedScannedExtension): I
class DeltaExtensionsQueueItem {
constructor(
public readonly toAdd: IExtension[],
public readonly toRemove: string[]
public readonly toRemove: string[] | IExtension[]
) { }
}
@@ -68,6 +68,69 @@ export const enum ExtensionRunningPreference {
Remote
}
class LockCustomer {
public readonly promise: Promise<IDisposable>;
private _resolve!: (value: IDisposable) => void;
constructor(
public readonly name: string
) {
this.promise = new Promise<IDisposable>((resolve, reject) => {
this._resolve = resolve;
});
}
resolve(value: IDisposable): void {
this._resolve(value);
}
}
class Lock {
private readonly _pendingCustomers: LockCustomer[] = [];
private _isLocked = false;
public async acquire(customerName: string): Promise<IDisposable> {
const customer = new LockCustomer(customerName);
this._pendingCustomers.push(customer);
this._advance();
return customer.promise;
}
private _advance(): void {
if (this._isLocked) {
// cannot advance yet
return;
}
if (this._pendingCustomers.length === 0) {
// no more waiting customers
return;
}
const customer = this._pendingCustomers.shift()!;
this._isLocked = true;
let customerHoldsLock = true;
let logLongRunningCustomerTimeout = setTimeout(() => {
if (customerHoldsLock) {
console.warn(`The customer named ${customer.name} has been holding on to the lock for 30s. This might be a problem.`);
}
}, 30 * 1000 /* 30 seconds */);
const releaseLock = () => {
if (!customerHoldsLock) {
return;
}
clearTimeout(logLongRunningCustomerTimeout);
customerHoldsLock = false;
this._isLocked = false;
this._advance();
};
customer.resolve(toDisposable(releaseLock));
}
}
export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: undefined;
@@ -88,6 +151,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
public readonly onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = this._onDidChangeResponsiveChange.event;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _registryLock: Lock;
private readonly _installedExtensionsReady: Barrier;
protected readonly _isDev: boolean;
private readonly _extensionsMessages: Map<string, IMessage[]>;
@@ -98,7 +163,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
private _inHandleDeltaExtensions: boolean;
private readonly _onDidFinishHandleDeltaExtensions = this._register(new Emitter<void>());
protected _runningLocation: Map<string, ExtensionRunningLocation>;
@@ -130,6 +194,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}));
this._registry = new ExtensionDescriptionRegistry([]);
this._registryLock = new Lock();
this._installedExtensionsReady = new Barrier();
this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
this._extensionsMessages = new Map<string, IMessage[]>();
@@ -151,14 +217,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {
let toAdd: IExtension[] = [];
let toRemove: string[] = [];
let toRemove: IExtension[] = [];
for (const extension of extensions) {
if (this._safeInvokeIsEnabled(extension)) {
// an extension has been enabled
toAdd.push(extension);
} else {
// an extension has been disabled
toRemove.push(extension.identifier.id);
toRemove.push(extension);
}
}
this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));
@@ -207,20 +273,23 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return;
}
while (this._deltaExtensionsQueue.length > 0) {
const item = this._deltaExtensionsQueue.shift()!;
try {
this._inHandleDeltaExtensions = true;
let lock: IDisposable | null = null;
try {
this._inHandleDeltaExtensions = true;
lock = await this._registryLock.acquire('handleDeltaExtensions');
while (this._deltaExtensionsQueue.length > 0) {
const item = this._deltaExtensionsQueue.shift()!;
await this._deltaExtensions(item.toAdd, item.toRemove);
} finally {
this._inHandleDeltaExtensions = false;
}
} finally {
this._inHandleDeltaExtensions = false;
if (lock) {
lock.dispose();
}
}
this._onDidFinishHandleDeltaExtensions.fire();
}
private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise<void> {
private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[] | IExtension[]): Promise<void> {
let toAdd: IExtensionDescription[] = [];
for (let i = 0, len = _toAdd.length; i < len; i++) {
const extension = _toAdd[i];
@@ -240,13 +309,20 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
let toRemove: IExtensionDescription[] = [];
for (let i = 0, len = _toRemove.length; i < len; i++) {
const extensionId = _toRemove[i];
const extensionOrId = _toRemove[i];
const extensionId = (typeof extensionOrId === 'string' ? extensionOrId : extensionOrId.identifier.id);
const extension = (typeof extensionOrId === 'string' ? null : extensionOrId);
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
// ignore disabling/uninstalling an extension which is not running
continue;
}
if (extension && extensionDescription.extensionLocation.scheme !== extension.location.scheme) {
// this event is for a different extension than mine (maybe for the local extension, while I have the remote extension)
continue;
}
if (!this.canRemoveExtension(extensionDescription)) {
// uses non-dynamic extension point or is activated
continue;
@@ -559,11 +635,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
public async startExtensionHosts(): Promise<void> {
this.stopExtensionHosts();
if (this._inHandleDeltaExtensions) {
await Event.toPromise(this._onDidFinishHandleDeltaExtensions.event);
}
const lock = await this._registryLock.acquire('startExtensionHosts');
try {
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
await localProcessExtensionHost.ready();
}
} finally {
lock.dispose();
}
}
public async restartExtensionHost(): Promise<void> {
@@ -680,15 +762,23 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
}
protected _checkEnabledAndProposedAPI(extensions: IExtensionDescription[]): IExtensionDescription[] {
/**
* @argument extensions The extensions to be checked.
* @argument ignoreWorkspaceTrust Do not take workspace trust into account.
*/
protected _checkEnabledAndProposedAPI(extensions: IExtensionDescription[], ignoreWorkspaceTrust: boolean): IExtensionDescription[] {
// enable or disable proposed API per extension
this._checkEnableProposedApi(extensions);
// keep only enabled extensions
return extensions.filter(extension => this._isEnabled(extension));
return extensions.filter(extension => this._isEnabled(extension, ignoreWorkspaceTrust));
}
protected _isEnabled(extension: IExtensionDescription): boolean {
/**
* @argument extension The extension to be checked.
* @argument ignoreWorkspaceTrust Do not take workspace trust into account.
*/
protected _isEnabled(extension: IExtensionDescription, ignoreWorkspaceTrust: boolean): boolean {
if (extension.isUnderDevelopment) {
// Never disable extensions under development
return true;
@@ -699,7 +789,20 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return false;
}
return this._safeInvokeIsEnabled(toExtension(extension));
const ext = toExtension(extension);
const isEnabled = this._safeInvokeIsEnabled(ext);
if (isEnabled) {
return true;
}
if (ignoreWorkspaceTrust && this._safeInvokeIsDisabledByWorkspaceTrust(ext)) {
// This extension is disabled, but the reason for it being disabled
// is workspace trust, so we will consider it enabled
return true;
}
return false;
}
protected _safeInvokeIsEnabled(extension: IExtension): boolean {
@@ -710,6 +813,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
}
protected _safeInvokeIsDisabledByWorkspaceTrust(extension: IExtension): boolean {
try {
return this._extensionEnablementService.isDisabledByWorkspaceTrust(extension);
} catch (err) {
return false;
}
}
protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void {
const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null);
for (let extensionDescription of affectedExtensions) {

View File

@@ -36,6 +36,7 @@ export class ExtensionHostMain {
private _isTerminating: boolean;
private readonly _hostUtils: IHostUtils;
private readonly _rpcProtocol: RPCProtocol;
private readonly _extensionService: IExtHostExtensionService;
private readonly _logService: ILogService;
private readonly _disposables = new DisposableStore();
@@ -48,15 +49,15 @@ export class ExtensionHostMain {
) {
this._isTerminating = false;
this._hostUtils = hostUtils;
const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer);
this._rpcProtocol = new RPCProtocol(protocol, null, uriTransformer);
// ensure URIs are transformed and revived
initData = ExtensionHostMain._transform(initData, rpcProtocol);
initData = ExtensionHostMain._transform(initData, this._rpcProtocol);
// bootstrap services
const services = new ServiceCollection(...getSingletonServiceDescriptors());
services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData });
services.set(IExtHostRpcService, new ExtHostRpcService(rpcProtocol));
services.set(IExtHostRpcService, new ExtHostRpcService(this._rpcProtocol));
services.set(IURITransformerService, new URITransformerService(uriTransformer));
services.set(IHostUtils, hostUtils);
@@ -99,8 +100,8 @@ export class ExtensionHostMain {
};
});
const mainThreadExtensions = rpcProtocol.getProxy(MainContext.MainThreadExtensionService);
const mainThreadErrors = rpcProtocol.getProxy(MainContext.MainThreadErrors);
const mainThreadExtensions = this._rpcProtocol.getProxy(MainContext.MainThreadExtensionService);
const mainThreadErrors = this._rpcProtocol.getProxy(MainContext.MainThreadErrors);
errors.setUnexpectedErrorHandler(err => {
const data = errors.transformErrorForSerialization(err);
const extension = extensionErrors.get(err);
@@ -124,9 +125,12 @@ export class ExtensionHostMain {
this._disposables.dispose();
errors.setUnexpectedErrorHandler((err) => {
// TODO: write to log once we have one
this._logService.error(err);
});
// Invalidate all proxies
this._rpcProtocol.dispose();
const extensionsDeactivated = this._extensionService.deactivateAll();
// Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds

View File

@@ -24,6 +24,7 @@ import { IExtensionHost, ExtensionHostKind, ActivationKind } from 'vs/workbench/
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { timeout } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
@@ -132,6 +133,10 @@ export class ExtensionHostManager extends Disposable {
return p.value;
}
public async ready(): Promise<void> {
await this._getProxy();
}
private async _measureLatency(proxy: ExtHostExtensionServiceShape): Promise<number> {
const COUNT = 10;
@@ -287,6 +292,15 @@ export class ExtensionHostManager extends Disposable {
}
}
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
const proxy = await this._getProxy();
if (!proxy) {
throw new Error(`Cannot resolve canonical URI`);
}
const result = await proxy.$getCanonicalURI(remoteAuthority, uri);
return URI.revive(result);
}
public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
if (!proxy) {

View File

@@ -1,10 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. 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 { IExtensionManifest, ExtensionKind, ExtensionIdentifier, ExtensionUntrustedWorkpaceSupportType, ExtensionVirtualWorkpaceSupportType } 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';
@@ -13,7 +13,9 @@ 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';
import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from 'vs/workbench/services/workspaces/common/workspaceTrust';
import { isBoolean } from 'vs/base/common/types';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
export const IExtensionManifestPropertiesService = createDecorator<IExtensionManifestPropertiesService>('extensionManifestPropertiesService');
@@ -30,7 +32,7 @@ export interface IExtensionManifestPropertiesService {
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[];
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkpaceSupportType;
canSupportVirtualWorkspace(manifest: IExtensionManifest): boolean;
getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkpaceSupportType;
}
export class ExtensionManifestPropertiesService extends Disposable implements IExtensionManifestPropertiesService {
@@ -50,6 +52,7 @@ export class ExtensionManifestPropertiesService extends Disposable implements IE
constructor(
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService
) {
super();
@@ -123,7 +126,7 @@ export class ExtensionManifestPropertiesService extends Disposable implements IE
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkpaceSupportType {
// Workspace trust feature is disabled, or extension has no entry point
if (!isWorkspaceTrustEnabled(this.configurationService) || !manifest.main) {
if (!this.workspaceTrustManagementService.workspaceTrustEnabled || !manifest.main) {
return true;
}
@@ -156,7 +159,7 @@ export class ExtensionManifestPropertiesService extends Disposable implements IE
return false;
}
canSupportVirtualWorkspace(manifest: IExtensionManifest): boolean {
getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkpaceSupportType {
// check user configured
const userConfiguredVirtualWorkspaceSupport = this.getConfiguredVirtualWorkspaceSupport(manifest);
if (userConfiguredVirtualWorkspaceSupport !== undefined) {
@@ -171,8 +174,14 @@ export class ExtensionManifestPropertiesService extends Disposable implements IE
}
// check the manifest
if (manifest.capabilities?.virtualWorkspaces !== undefined) {
return manifest.capabilities?.virtualWorkspaces;
const virtualWorkspaces = manifest.capabilities?.virtualWorkspaces;
if (isBoolean(virtualWorkspaces)) {
return virtualWorkspaces;
} else if (virtualWorkspaces) {
const supported = virtualWorkspaces.supported;
if (isBoolean(supported) || supported === 'limited') {
return supported;
}
}
// check default from product

View File

@@ -320,6 +320,16 @@ export const schema: IJSONSchema = {
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: 'onRenderer',
description: nls.localize('vscode.extension.activationEvents.onRenderer', 'An activation event emitted whenever a notebook output renderer is used.'),
body: 'onRenderer:${11:rendererId}'
},
{
label: 'onTerminalProfile',
body: 'onTerminalProfile:${1:terminalType}',
description: nls.localize('vscode.extension.activationEvents.onTerminalProfile', 'An activation event emitted when a specific terminal profile is launched.'),
},
{
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.'),
@@ -421,8 +431,28 @@ export const schema: IJSONSchema = {
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
type: ['boolean', 'object'],
defaultSnippets: [
{ label: 'limited', body: { supported: '${1:limited}', description: '${2}' } },
{ label: 'false', body: { supported: false, description: '${2}' } },
],
default: true.valueOf,
properties: {
supported: {
markdownDescription: nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported', "Declares the level of support for virtual workspaces by the extension."),
type: ['string', 'boolean'],
enum: ['limited', true, false],
enumDescriptions: [
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.limited', "The extension will be enabled in virtual workspaces with some functionality disabled."),
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.true', "The extension will be enabled in virtual workspaces with all functionality enabled."),
nls.localize('vscode.extension.capabilities.virtualWorkspaces.supported.false', "The extension will not be enabled in virtual workspaces."),
]
},
description: {
type: 'string',
markdownDescription: nls.localize('vscode.extension.capabilities.virtualWorkspaces.description', "A description of how virtual workspaces affects the extensions behavior and why it is needed. This only applies when `supported` is not `true`."),
}
}
},
untrustedWorkspaces: {
description: nls.localize('vscode.extension.capabilities.untrustedWorkspaces', 'Declares how the extension should be handled in untrusted workspaces.'),

View File

@@ -236,9 +236,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: remoteInitData.globalStorageHome,
workspaceStorageHome: remoteInitData.workspaceStorageHome,
webviewResourceRoot: this._environmentService.webviewResourceRoot,
webviewCspSource: this._environmentService.webviewCspSource,
workspaceStorageHome: remoteInitData.workspaceStorageHome
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
configuration: workspace.configuration,

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as errors from 'vs/base/common/errors';
@@ -69,9 +68,10 @@ export class CachedExtensionScanner {
const version = this._productService.version;
const commit = this._productService.commit;
const date = this._productService.date;
const devMode = !!process.env['VSCODE_DEV'];
const locale = platform.language;
const input = new ExtensionScannerInput(version, commit, locale, devMode, path, isBuiltin, false, translations);
const input = new ExtensionScannerInput(version, date, commit, locale, devMode, path, isBuiltin, false, translations);
return ExtensionScanner.scanSingleExtension(input, log);
}
@@ -130,7 +130,7 @@ export class CachedExtensionScanner {
}
try {
await pfs.rimraf(cacheFile, pfs.RimRafMode.MOVE);
await pfs.Promises.rm(cacheFile, pfs.RimRafMode.MOVE);
} catch (err) {
errors.onUnexpectedError(err);
console.error(err);
@@ -151,7 +151,7 @@ export class CachedExtensionScanner {
const cacheFile = path.join(cacheFolder, cacheKey);
try {
const cacheRawContents = await fs.promises.readFile(cacheFile, 'utf8');
const cacheRawContents = await pfs.Promises.readFile(cacheFile, 'utf8');
return JSON.parse(cacheRawContents);
} catch (err) {
// That's ok...
@@ -165,13 +165,13 @@ export class CachedExtensionScanner {
const cacheFile = path.join(cacheFolder, cacheKey);
try {
await fs.promises.mkdir(cacheFolder, { recursive: true });
await pfs.Promises.mkdir(cacheFolder, { recursive: true });
} catch (err) {
// That's ok...
}
try {
await pfs.writeFile(cacheFile, JSON.stringify(cacheContents));
await pfs.Promises.writeFile(cacheFile, JSON.stringify(cacheContents));
} catch (err) {
// That's ok...
}
@@ -184,7 +184,7 @@ export class CachedExtensionScanner {
}
try {
const folderStat = await fs.promises.stat(input.absoluteFolderPath);
const folderStat = await pfs.Promises.stat(input.absoluteFolderPath);
input.mtime = folderStat.mtime.getTime();
} catch (err) {
// That's ok...
@@ -224,7 +224,7 @@ export class CachedExtensionScanner {
private static async _readTranslationConfig(): Promise<Translations> {
if (platform.translationsConfigFile) {
try {
const content = await fs.promises.readFile(platform.translationsConfigFile, 'utf8');
const content = await pfs.Promises.readFile(platform.translationsConfigFile, 'utf8');
return JSON.parse(content) as Translations;
} catch (err) {
// no problemo
@@ -245,6 +245,7 @@ export class CachedExtensionScanner {
const version = productService.version;
const commit = productService.commit;
const date = productService.date;
const devMode = !!process.env['VSCODE_DEV'];
const locale = platform.language;
@@ -253,7 +254,7 @@ export class CachedExtensionScanner {
notificationService,
environmentService,
BUILTIN_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations),
new ExtensionScannerInput(version, date, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations),
log
);
@@ -263,10 +264,10 @@ export class CachedExtensionScanner {
const builtInExtensions = Promise.resolve<IBuiltInExtension[]>(productService.builtInExtensions || []);
const controlFilePath = joinPath(environmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json').fsPath;
const controlFile = fs.promises.readFile(controlFilePath, 'utf8')
const controlFile = pfs.Promises.readFile(controlFilePath, 'utf8')
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations);
const input = new ExtensionScannerInput(version, date, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations);
const extraBuiltinExtensions = Promise.all([builtInExtensions, controlFile])
.then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control))
.then(resolver => ExtensionScanner.scanExtensions(input, log, resolver));
@@ -279,7 +280,7 @@ export class CachedExtensionScanner {
notificationService,
environmentService,
USER_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
new ExtensionScannerInput(version, date, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
log
));
@@ -288,7 +289,7 @@ export class CachedExtensionScanner {
if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI) {
const extDescsP = environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file).map(extLoc => {
return ExtensionScanner.scanOneOrMultipleExtensions(
new ExtensionScannerInput(version, commit, locale, devMode, originalFSPath(extLoc), false, true, translations), log
new ExtensionScannerInput(version, date, commit, locale, devMode, originalFSPath(extLoc), false, true, translations), log
);
});
developedExtensions = Promise.all(extDescsP).then((extDescArrays: IExtensionDescription[][]) => {

View File

@@ -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, RemoteTrustOption, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, 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,12 +42,8 @@ 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;
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@@ -75,7 +71,6 @@ 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,
) {
@@ -144,7 +139,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
return {
getInitData: async () => {
if (isInitialStart) {
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
// Here we load even extensions that would be disabled by workspace trust
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), /* ignore workspace trust */true);
const runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, []);
const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation);
return {
@@ -343,11 +339,24 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const remoteAuthority = this._environmentService.remoteAuthority;
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
let remoteEnv: IRemoteAgentEnvironment | null = null;
let remoteExtensions: IExtensionDescription[] = [];
if (remoteAuthority) {
this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => {
if (uri.scheme !== Schemas.vscodeRemote || uri.authority !== remoteAuthority) {
// The current remote authority resolver cannot give the canonical URI for this URI
return uri;
}
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
return localProcessExtensionHost.getCanonicalURI(remoteAuthority, uri);
});
// Now that the canonical URI provider has been registered, we need to wait for the trust state to be
// calculated. The trust state will be used while resolving the authority, however the resolver can
// override the trust state through the resolver result.
await this._workspaceTrustManagementService.workspaceResolved;
let resolverResult: ResolverResult;
try {
@@ -364,42 +373,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
// Proceed with the local extension host
await this._startLocalExtensionHost(localExtensions);
await this._startLocalExtensionHost();
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);
@@ -420,23 +397,31 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._remoteAgentService.getEnvironment(),
this._remoteAgentService.scanExtensions()
]);
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions);
if (!remoteEnv) {
this._notificationService.notify({ severity: Severity.Error, message: nls.localize('getEnvironmentFailure', "Could not fetch remote environment") });
// Proceed with the local extension host
await this._startLocalExtensionHost(localExtensions);
await this._startLocalExtensionHost();
return;
}
updateProxyConfigurationsScope(remoteEnv.useHostProxy ? ConfigurationScope.APPLICATION : ConfigurationScope.MACHINE);
} else {
this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => uri);
}
await this._startLocalExtensionHost(localExtensions, remoteAuthority, remoteEnv, remoteExtensions);
await this._startLocalExtensionHost(remoteAuthority, remoteEnv, remoteExtensions);
}
private async _startLocalExtensionHost(localExtensions: IExtensionDescription[], remoteAuthority: string | undefined = undefined, remoteEnv: IRemoteAgentEnvironment | null = null, remoteExtensions: IExtensionDescription[] = []): Promise<void> {
private async _startLocalExtensionHost(remoteAuthority: string | undefined = undefined, remoteEnv: IRemoteAgentEnvironment | null = null, remoteExtensions: IExtensionDescription[] = []): Promise<void> {
// Ensure that the workspace trust state has been fully initialized so
// that the extension host can start with the correct set of extensions.
await this._workspaceTrustManagementService.workspaceTrustInitialized;
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions, false);
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), false);
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
// remove non-UI extensions from the local extensions
@@ -516,7 +501,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const allExtensions = await this._scanAllLocalExtensions();
const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0];
if (extension) {
if (!this._isEnabled(extension)) {
if (!this._isEnabled(extension, false)) {
const message = nls.localize('enableResolver', "Extension '{0}' is required to open the remote window.\nOK to enable?", recommendation.friendlyName);
this._notificationService.prompt(Severity.Info, message,
[{

View File

@@ -180,9 +180,9 @@ export class LocalProcessExtensionHost implements IExtensionHost {
}
if (this._isExtensionDevHost) {
// Unset `VSCODE_NODE_CACHED_DATA_DIR` when developing extensions because it might
// Unset `VSCODE_CODE_CACHE_PATH` when developing extensions because it might
// be that dependencies, that otherwise would be cached, get modified.
delete env['VSCODE_NODE_CACHED_DATA_DIR'];
delete env['VSCODE_CODE_CACHE_PATH'];
}
const opts = {
@@ -477,8 +477,6 @@ export class LocalProcessExtensionHost implements IExtensionHost {
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: this._environmentService.globalStorageHome,
workspaceStorageHome: this._environmentService.workspaceStorageHome,
webviewResourceRoot: this._environmentService.webviewResourceRoot,
webviewCspSource: this._environmentService.webviewCspSource,
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: withNullAsUndefined(workspace.configuration),
@@ -628,6 +626,8 @@ export class LocalProcessExtensionHost implements IExtensionHost {
// (graceful termination)
protocol.send(createMessageOfType(MessageType.Terminate));
protocol.getSocket().dispose();
protocol.dispose();
// Give the extension host 10s, after which we will

View File

@@ -18,7 +18,7 @@ import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessag
import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain';
import { VSBuffer } from 'vs/base/common/buffer';
import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc';
import { exists } from 'vs/base/node/pfs';
import { Promises } from 'vs/base/node/pfs';
import { realpath } from 'vs/base/node/extpath';
import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -173,6 +173,9 @@ function _createExtHostProtocol(): Promise<PersistentProtocol> {
});
socket.once('error', reject);
socket.on('close', () => {
onTerminate('renderer closed the socket');
});
});
}
}
@@ -323,7 +326,7 @@ export async function startExtensionHostProcess(): Promise<void> {
const hostUtils = new class NodeHost implements IHostUtils {
declare readonly _serviceBrand: undefined;
exit(code: number) { nativeExit(code); }
exists(path: string) { return exists(path); }
exists(path: string) { return Promises.exists(path); }
realpath(path: string) { return realpath(path); }
};

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as semver from 'vs/base/common/semver/semver';
@@ -30,14 +29,16 @@ export interface NlsConfiguration {
abstract class ExtensionManifestHandler {
protected readonly _ourVersion: string;
protected readonly _ourProductDate: string | undefined;
protected readonly _log: ILog;
protected readonly _absoluteFolderPath: string;
protected readonly _isBuiltin: boolean;
protected readonly _isUnderDevelopment: boolean;
protected readonly _absoluteManifestPath: string;
constructor(ourVersion: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean) {
constructor(ourVersion: string, ourProductDate: string | undefined, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean) {
this._ourVersion = ourVersion;
this._ourProductDate = ourProductDate;
this._log = log;
this._absoluteFolderPath = absoluteFolderPath;
this._isBuiltin = isBuiltin;
@@ -58,7 +59,7 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
}
public parse(): Promise<IExtensionDescription> {
return fs.promises.readFile(this._absoluteManifestPath).then((manifestContents) => {
return pfs.Promises.readFile(this._absoluteManifestPath).then((manifestContents) => {
const errors: json.ParseError[] = [];
const manifest = ExtensionManifestParser._fastParseJSON(manifestContents.toString(), errors);
if (json.getNodeType(manifest) !== 'object') {
@@ -94,8 +95,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
private readonly _nlsConfig: NlsConfiguration;
constructor(ourVersion: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration) {
super(ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
constructor(ourVersion: string, ourProductDate: string | undefined, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration) {
super(ourVersion, ourProductDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
this._nlsConfig = nlsConfig;
}
@@ -131,7 +132,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
let translationPath = this._nlsConfig.translations[translationId];
let localizedMessages: Promise<LocalizedMessages | undefined>;
if (translationPath) {
localizedMessages = fs.promises.readFile(translationPath, 'utf8').then<LocalizedMessages, LocalizedMessages>((content) => {
localizedMessages = pfs.Promises.readFile(translationPath, 'utf8').then<LocalizedMessages, LocalizedMessages>((content) => {
let errors: json.ParseError[] = [];
let translationBundle: TranslationBundle = json.parse(content, errors);
if (errors.length > 0) {
@@ -156,7 +157,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (!messageBundle.localized) {
return { values: undefined, default: messageBundle.original };
}
return fs.promises.readFile(messageBundle.localized, 'utf8').then(messageBundleContent => {
return pfs.Promises.readFile(messageBundle.localized, 'utf8').then(messageBundleContent => {
let errors: json.ParseError[] = [];
let messages: MessageBag = json.parse(messageBundleContent, errors);
if (errors.length > 0) {
@@ -205,7 +206,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
private static resolveOriginalMessageBundle(originalMessageBundle: string | null, errors: json.ParseError[]) {
return new Promise<{ [key: string]: string; } | null>((c, e) => {
if (originalMessageBundle) {
fs.promises.readFile(originalMessageBundle).then(originalBundleContent => {
pfs.Promises.readFile(originalMessageBundle).then(originalBundleContent => {
c(json.parse(originalBundleContent.toString(), errors));
}, (err) => {
c(null);
@@ -321,7 +322,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
extensionDescription.isUnderDevelopment = this._isUnderDevelopment;
let notices: string[] = [];
if (!ExtensionManifestValidator.isValidExtensionDescription(this._ourVersion, this._absoluteFolderPath, extensionDescription, notices)) {
if (!ExtensionManifestValidator.isValidExtensionDescription(this._ourVersion, this._ourProductDate, this._absoluteFolderPath, extensionDescription, notices)) {
notices.forEach((error) => {
this._log.error(this._absoluteFolderPath, error);
});
@@ -347,7 +348,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
return extensionDescription;
}
private static isValidExtensionDescription(version: string, extensionFolderPath: string, extensionDescription: IExtensionDescription, notices: string[]): boolean {
private static isValidExtensionDescription(version: string, productDate: string | undefined, extensionFolderPath: string, extensionDescription: IExtensionDescription, notices: string[]): boolean {
if (!ExtensionManifestValidator.baseIsValidExtensionDescription(extensionFolderPath, extensionDescription, notices)) {
return false;
@@ -358,7 +359,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
return false;
}
return isValidExtensionVersion(version, extensionDescription, notices);
return isValidExtensionVersion(version, productDate, extensionDescription, notices);
}
private static baseIsValidExtensionDescription(extensionFolderPath: string, extensionDescription: IExtensionDescription, notices: string[]): boolean {
@@ -456,6 +457,7 @@ export class ExtensionScannerInput {
constructor(
public readonly ourVersion: string,
public readonly ourProductDate: string | undefined,
public readonly commit: string | undefined,
public readonly locale: string | undefined,
public readonly devMode: boolean,
@@ -479,6 +481,7 @@ export class ExtensionScannerInput {
public static equals(a: ExtensionScannerInput, b: ExtensionScannerInput): boolean {
return (
a.ourVersion === b.ourVersion
&& a.ourProductDate === b.ourProductDate
&& a.commit === b.commit
&& a.locale === b.locale
&& a.devMode === b.devMode
@@ -505,7 +508,7 @@ class DefaultExtensionResolver implements IExtensionResolver {
constructor(private root: string) { }
resolveExtensions(): Promise<IExtensionReference[]> {
return pfs.readDirsInDir(this.root)
return pfs.Promises.readDirsInDir(this.root)
.then(folders => folders.map(name => ({ name, path: path.join(this.root, name) })));
}
}
@@ -515,23 +518,23 @@ export class ExtensionScanner {
/**
* Read the extension defined in `absoluteFolderPath`
*/
private static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise<IExtensionDescription | null> {
private static scanExtension(version: string, productDate: string | undefined, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise<IExtensionDescription | null> {
absoluteFolderPath = path.normalize(absoluteFolderPath);
let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
let parser = new ExtensionManifestParser(version, productDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
return parser.parse().then<IExtensionDescription | null>((extensionDescription) => {
if (extensionDescription === null) {
return null;
}
let nlsReplacer = new ExtensionManifestNLSReplacer(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig);
let nlsReplacer = new ExtensionManifestNLSReplacer(version, productDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig);
return nlsReplacer.replaceNLS(extensionDescription);
}).then((extensionDescription) => {
if (extensionDescription === null) {
return null;
}
let validator = new ExtensionManifestValidator(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
let validator = new ExtensionManifestValidator(version, productDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment);
return validator.validate(extensionDescription);
});
}
@@ -552,7 +555,7 @@ export class ExtensionScanner {
let obsolete: { [folderName: string]: boolean; } = {};
if (!isBuiltin) {
try {
const obsoleteFileContents = await fs.promises.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8');
const obsoleteFileContents = await pfs.Promises.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8');
obsolete = JSON.parse(obsoleteFileContents);
} catch (err) {
// Don't care
@@ -569,7 +572,7 @@ export class ExtensionScanner {
}
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
let _extensionDescriptions = await Promise.all(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig)));
let _extensionDescriptions = await Promise.all(refs.map(r => this.scanExtension(input.ourVersion, input.ourProductDate, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig)));
let extensionDescriptions = arrays.coalesce(_extensionDescriptions);
extensionDescriptions = extensionDescriptions.filter(item => item !== null && !obsolete[new ExtensionIdentifierWithVersion({ id: getGalleryExtensionId(item.publisher, item.name) }, item.version).key()]);
@@ -604,7 +607,7 @@ export class ExtensionScanner {
return pfs.SymlinkSupport.existsFile(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
if (exists) {
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => {
return this.scanExtension(input.ourVersion, input.ourProductDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => {
if (extensionDescription === null) {
return [];
}
@@ -623,7 +626,7 @@ export class ExtensionScanner {
const isBuiltin = input.isBuiltin;
const isUnderDevelopment = input.isUnderDevelopment;
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig);
return this.scanExtension(input.ourVersion, input.ourProductDate, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig);
}
public static mergeBuiltinExtensions(builtinExtensions: Promise<IExtensionDescription[]>, extraBuiltinExtensions: Promise<IExtensionDescription[]>): Promise<IExtensionDescription[]> {

View File

@@ -12,11 +12,13 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { isWeb } from 'vs/base/common/platform';
import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
suite('ExtensionManifestPropertiesService - ExtensionKind', () => {
function check(manifest: Partial<IExtensionManifest>, expected: ExtensionKind[]): void {
const extensionManifestPropertiesService = new ExtensionManifestPropertiesService(TestProductService, new TestConfigurationService());
const extensionManifestPropertiesService = new ExtensionManifestPropertiesService(TestProductService, new TestConfigurationService(), new TestWorkspaceTrustManagementService());
assert.deepStrictEqual(extensionManifestPropertiesService.deduceExtensionKind(<IExtensionManifest>manifest), expected);
}
@@ -80,6 +82,7 @@ if (!isWeb) {
test('test extension workspace trust request when main entry point is missing', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
const extensionMaifest = getExtensionManifest();
assertUntrustedWorkspaceSupport(extensionMaifest, true);
@@ -87,7 +90,7 @@ if (!isWeb) {
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 } } });
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService(false));
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
@@ -95,37 +98,34 @@ if (!isWeb) {
test('test extension workspace trust request when override exists in settings.json', async () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionUntrustedSupport: { 'pub.a': { supported: true } } } } });
await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { '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>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
await testConfigurationService.setUserConfiguration('security', { workspace: { trust: { extensionUntrustedSupport: { 'pub.a': { supported: true, version: '1.0.0' } } } } });
await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { '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>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
await testConfigurationService.setUserConfiguration('security', {
workspace: {
trust: {
enabled: true,
extensionUntrustedSupport: { 'pub.a': { supported: true, version: '2.0.0' } }
}
}
});
await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { '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 } } });
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, true);
@@ -133,6 +133,7 @@ if (!isWeb) {
test('test extension workspace trust request when override exists in product.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{ extensionUntrustedWorkspaceSupport: { 'pub.a': { override: 'limited' } } });
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } });
assertUntrustedWorkspaceSupport(extensionMaifest, 'limited');
@@ -140,6 +141,7 @@ if (!isWeb) {
test('test extension workspace trust request when value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } });
assertUntrustedWorkspaceSupport(extensionMaifest, 'limited');
@@ -147,6 +149,7 @@ if (!isWeb) {
test('test extension workspace trust request when no value exists in package.json', () => {
instantiationService.stub(IProductService, <Partial<IProductService>>{});
instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService());
const extensionMaifest = getExtensionManifest({ main: './out/extension.js' });
assertUntrustedWorkspaceSupport(extensionMaifest, false);