mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 18:22:34 -05:00
Merge from vscode e3c4990c67c40213af168300d1cfeb71d680f877 (#16569)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.'),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[][]) => {
|
||||
|
||||
@@ -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,
|
||||
[{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user