Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)

* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79

* Fix breaks

* Extension management fixes

* Fix breaks in windows bundling

* Fix/skip failing tests

* Update distro

* Add clear to nuget.config

* Add hygiene task

* Bump distro

* Fix hygiene issue

* Add build to hygiene exclusion

* Update distro

* Update hygiene

* Hygiene exclusions

* Update tsconfig

* Bump distro for server breaks

* Update build config

* Update darwin path

* Add done calls to notebook tests

* Skip failing tests

* Disable smoke tests
This commit is contained in:
Karl Burtram
2021-02-09 16:15:05 -08:00
committed by GitHub
parent 6f192f9af5
commit ce612a3d96
1929 changed files with 68012 additions and 34564 deletions

View File

@@ -9,29 +9,30 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionService, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionHost, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IProductService } from 'vs/platform/product/common/productService';
import { AbstractExtensionService, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningLocation, ExtensionRunningLocationClassifier, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { RemoteExtensionHost, IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
private _disposables = new DisposableStore();
private _remoteInitData: IRemoteExtensionHostInitData | null = null;
private _runningLocation: Map<string, ExtensionRunningLocation>;
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@@ -41,14 +42,20 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,
@IFileService fileService: IFileService,
@IProductService productService: IProductService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService,
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IConfigurationService private readonly _configService: IConfigurationService,
@IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IUserDataInitializationService private readonly _userDataInitializationService: IUserDataInitializationService,
) {
super(
new ExtensionRunningLocationClassifier(
productService,
configurationService,
(extensionKinds, isInstalledLocally, isInstalledRemotely) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely)
),
instantiationService,
notificationService,
environmentService,
@@ -56,15 +63,15 @@ export class ExtensionService extends AbstractExtensionService implements IExten
extensionEnablementService,
fileService,
productService,
extensionManagementService,
contextService,
configurationService,
);
this._runningLocation = new Map<string, ExtensionRunningLocation>();
// Initialize extensions first and do it only after workbench is ready
this._lifecycleService.when(LifecyclePhase.Ready).then(async () => {
await this._userDataInitializationService.initializeExtensions(this._instantiationService);
this._initialize();
});
// Initialize only after workbench is ready
this._lifecycleService.when(LifecyclePhase.Ready).then(() => this._initialize());
this._initFetchFileSystem();
}
@@ -74,6 +81,33 @@ export class ExtensionService extends AbstractExtensionService implements IExten
super.dispose();
}
protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void {
super._onExtensionHostCrashed(extensionHost, code, signal);
if (extensionHost.kind === ExtensionHostKind.LocalWebWorker) {
if (code === ExtensionHostExitCode.StartTimeout10s) {
this._notificationService.prompt(
Severity.Error,
nls.localize('extensionService.startTimeout', "The Web Worker Extension Host did not start in 10s."),
[]
);
return;
}
}
}
protected async _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null> {
if (extension.location.scheme === Schemas.vscodeRemote) {
return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System);
}
const scannedExtension = await this._webExtensionsScannerService.scanAndTranslateSingleExtension(extension.location, extension.type);
if (scannedExtension) {
return parseScannedExtension(scannedExtension);
}
return null;
}
private _initFetchFileSystem(): void {
const provider = new FetchFileSystemProvider();
this._disposables.add(this._fileService.registerProvider(Schemas.http, provider));
@@ -103,6 +137,25 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation {
let canRunRemotely = false;
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledRemotely) {
// ui extensions run remotely if possible (but only as a last resort)
canRunRemotely = true;
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
}
if (extensionKind === 'web' && isInstalledLocally) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
}
}
return (canRunRemotely ? ExtensionRunningLocation.Remote : ExtensionRunningLocation.None);
}
protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
@@ -129,7 +182,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions);
const remoteAgentConnection = this._remoteAgentService.getConnection();
this._runningLocation = _determineRunningLocation(this._productService, this._configService, localExtensions, remoteExtensions, Boolean(remoteEnv && remoteAgentConnection));
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
localExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
@@ -164,53 +217,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
}
const enum ExtensionRunningLocation {
None,
LocalWebWorker,
Remote
}
export function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map<string, ExtensionKind[]>, hasRemote: boolean): Map<string, ExtensionRunningLocation> {
const localExtensionsSet = new Set<string>();
localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const remoteExtensionsSet = new Set<string>();
remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => {
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledRemotely) {
// ui extensions run remotely if possible
return ExtensionRunningLocation.Remote;
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
}
if (extensionKind === 'web' && isInstalledLocally) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
}
}
return ExtensionRunningLocation.None;
};
const runningLocation = new Map<string, ExtensionRunningLocation>();
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
return runningLocation;
}
function _determineRunningLocation(productService: IProductService, configurationService: IConfigurationService, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], hasRemote: boolean): Map<string, ExtensionRunningLocation> {
const allExtensionKinds = new Map<string, ExtensionKind[]>();
localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService)));
remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService)));
return determineRunningLocation(localExtensions, remoteExtensions, allExtensionKinds, hasRemote);
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}

View File

@@ -13,7 +13,7 @@ import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbe
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -21,28 +21,30 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000;
const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle';
const CONFIRMED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds';
const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions';
const USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds';
const USER_TRUSTED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions';
function isExtensionId(value: string): boolean {
return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value);
}
class ConfirmedExtensionIdStorage {
class UserTrustedExtensionIdStorage {
get extensions(): string[] {
const confirmedExtensionIdsJson = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]');
const userTrustedExtensionIdsJson = this.storageService.get(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]');
try {
return JSON.parse(confirmedExtensionIdsJson);
return JSON.parse(userTrustedExtensionIdsJson);
} catch {
return [];
}
@@ -59,7 +61,7 @@ class ConfirmedExtensionIdStorage {
}
set(ids: string[]): void {
this.storageService.store(CONFIRMED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL);
this.storageService.store(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}
@@ -86,7 +88,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
private extensionHandlers = new Map<string, IURLHandler>();
private uriBuffer = new Map<string, { timestamp: number, uri: URI }[]>();
private storage: ConfirmedExtensionIdStorage;
private userTrustedExtensionsStorage: UserTrustedExtensionIdStorage;
private disposable: IDisposable;
constructor(
@@ -100,9 +102,10 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IStorageService private readonly storageService: IStorageService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProgressService private readonly progressService: IProgressService
@IProgressService private readonly progressService: IProgressService,
@IExtensionUrlTrustService private readonly extensionUrlTrustService: IExtensionUrlTrustService
) {
this.storage = new ConfirmedExtensionIdStorage(storageService);
this.userTrustedExtensionsStorage = new UserTrustedExtensionIdStorage(storageService);
const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS);
const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE);
@@ -117,7 +120,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
);
const cache = ExtensionUrlBootstrapHandler.cache;
setTimeout(() => cache.forEach(uri => this.handleURL(uri)));
setTimeout(() => cache.forEach(([uri, option]) => this.handleURL(uri, option)));
}
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
@@ -134,14 +137,11 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
return true;
}
let showConfirm: boolean;
if (options && options.trusted) {
showConfirm = false;
} else {
showConfirm = !this.isConfirmed(ExtensionIdentifier.toKey(extensionId));
}
const trusted = options?.trusted
|| (options?.originalUrl ? await this.extensionUrlTrustService.isExtensionUrlTrusted(extensionId, options.originalUrl) : false)
|| this.didUserTrustExtension(ExtensionIdentifier.toKey(extensionId));
if (showConfirm) {
if (!trusted) {
let uriString = uri.toString(false);
// {{SQL CARBON EDIT}} - Begin
@@ -171,7 +171,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
}
if (result.checkboxChecked) {
this.storage.add(ExtensionIdentifier.toKey(extensionId));
this.userTrustedExtensionsStorage.add(ExtensionIdentifier.toKey(extensionId));
}
}
@@ -300,7 +300,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
}
private async reloadAndHandle(url: URI): Promise<void> {
this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE);
this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE, StorageTarget.MACHINE);
await this.hostService.reload();
}
@@ -320,22 +320,22 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
this.uriBuffer = uriBuffer;
}
private isConfirmed(id: string): boolean {
if (this.storage.has(id)) {
private didUserTrustExtension(id: string): boolean {
if (this.userTrustedExtensionsStorage.has(id)) {
return true;
}
return this.getConfirmedExtensionIdsFromConfiguration().indexOf(id) > -1;
return this.getConfirmedTrustedExtensionIdsFromConfiguration().indexOf(id) > -1;
}
private getConfirmedExtensionIdsFromConfiguration(): Array<string> {
const confirmedExtensionIds = this.configurationService.getValue<Array<string>>(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY);
private getConfirmedTrustedExtensionIdsFromConfiguration(): Array<string> {
const trustedExtensionIds = this.configurationService.getValue<Array<string>>(USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY);
if (!Array.isArray(confirmedExtensionIds)) {
if (!Array.isArray(trustedExtensionIds)) {
return [];
}
return confirmedExtensionIds;
return trustedExtensionIds;
}
dispose(): void {
@@ -353,10 +353,10 @@ registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler);
*/
class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandler {
private static _cache: URI[] = [];
private static _cache: [URI, IOpenURLOptions | undefined][] = [];
private static disposable: IDisposable;
static get cache(): URI[] {
static get cache(): [URI, IOpenURLOptions | undefined][] {
ExtensionUrlBootstrapHandler.disposable.dispose();
const result = ExtensionUrlBootstrapHandler._cache;
@@ -368,12 +368,12 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle
ExtensionUrlBootstrapHandler.disposable = urlService.registerHandler(this);
}
async handleURL(uri: URI): Promise<boolean> {
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
if (!isExtensionId(uri.authority)) {
return false;
}
ExtensionUrlBootstrapHandler._cache.push(uri);
ExtensionUrlBootstrapHandler._cache.push([uri, options]);
return true;
}
}
@@ -388,19 +388,21 @@ class ManageAuthorizedExtensionURIsAction extends Action2 {
id: 'workbench.extensions.action.manageAuthorizedExtensionURIs',
title: { value: localize('manage', "Manage Authorized Extension URIs..."), original: 'Manage Authorized Extension URIs...' },
category: { value: localize('extensions', "Extensions"), original: 'Extensions' },
f1: true
menu: {
id: MenuId.CommandPalette,
when: IsWebContext.toNegated()
}
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const storageService = accessor.get(IStorageService);
const quickInputService = accessor.get(IQuickInputService);
const storage = new ConfirmedExtensionIdStorage(storageService);
const storage = new UserTrustedExtensionIdStorage(storageService);
const items = storage.extensions.map(label => ({ label, picked: true } as IQuickPickItem));
if (items.length === 0) {
await quickInputService.pick([{ label: localize('no', 'There are currently no authorized extension URIs.') }]);
return;
}

View File

@@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { toDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { VSBuffer } from 'vs/base/common/buffer';
import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { createMessageOfType, MessageType, isMessageOfType, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
@@ -27,8 +27,9 @@ import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output
import { localize } from 'vs/nls';
import { generateUuid } from 'vs/base/common/uuid';
import { canceled, onUnexpectedError } from 'vs/base/common/errors';
import { WEB_WORKER_IFRAME } from 'vs/workbench/services/extensions/common/webWorkerIframe';
import { Barrier } from 'vs/base/common/async';
import { FileAccess } from 'vs/base/common/network';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
export interface IWebWorkerExtensionHostInitData {
readonly autoStart: boolean;
@@ -62,19 +63,56 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
@ILogService private readonly _logService: ILogService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IProductService private readonly _productService: IProductService,
@ILayoutService private readonly _layoutService: ILayoutService,
) {
super();
this._isTerminating = false;
this._protocolPromise = null;
this._protocol = null;
this._extensionHostLogsLocation = URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme });
this._extensionHostLogsLocation = joinPath(this._environmentService.extHostLogsPath, 'webWorker');
this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`);
}
private _wrapInIframe(): boolean {
if (this._environmentService.options && typeof this._environmentService.options._wrapWebWorkerExtHostInIframe === 'boolean') {
return this._environmentService.options._wrapWebWorkerExtHostInIframe;
}
// wrap in <iframe> by default
return true;
}
private _webWorkerExtensionHostIframeSrc(): string | null {
if (this._environmentService.options && this._environmentService.options.webWorkerExtensionHostIframeSrc) {
return this._environmentService.options.webWorkerExtensionHostIframeSrc;
}
if (this._productService.webEndpointUrl) {
const forceHTTPS = (location.protocol === 'https:');
let baseUrl = this._productService.webEndpointUrl;
if (this._productService.quality) {
baseUrl += `/${this._productService.quality}`;
}
if (this._productService.commit) {
baseUrl += `/${this._productService.commit}`;
}
return (
forceHTTPS
? `${baseUrl}/out/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html`
: `${baseUrl}/out/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html`
);
}
return null;
}
public async start(): Promise<IMessagePassingProtocol> {
if (!this._protocolPromise) {
if (platform.isWeb && this._environmentService.options && this._environmentService.options._wrapWebWorkerExtHostInIframe) {
this._protocolPromise = this._startInsideIframe();
if (platform.isWeb) {
const webWorkerExtensionHostIframeSrc = this._webWorkerExtensionHostIframeSrc();
if (webWorkerExtensionHostIframeSrc && this._wrapInIframe()) {
this._protocolPromise = this._startInsideIframe(webWorkerExtensionHostIframeSrc);
} else {
console.warn(`The web worker extension host is started without an iframe sandbox!`);
this._protocolPromise = this._startOutsideIframe();
}
} else {
this._protocolPromise = this._startOutsideIframe();
}
@@ -83,37 +121,41 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
return this._protocolPromise;
}
private async _startInsideIframe(): Promise<IMessagePassingProtocol> {
private async _startInsideIframe(webWorkerExtensionHostIframeSrc: string): Promise<IMessagePassingProtocol> {
const emitter = this._register(new Emitter<VSBuffer>());
const iframe = document.createElement('iframe');
iframe.setAttribute('class', 'web-worker-ext-host-iframe');
iframe.setAttribute('sandbox', 'allow-scripts');
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
iframe.style.display = 'none';
const vscodeWebWorkerExtHostId = generateUuid();
const workerUrl = require.toUrl('../worker/extensionHostWorkerMain.js');
const workerSrc = getWorkerBootstrapUrl(workerUrl, 'WorkerExtensionHost', true);
const escapeAttribute = (value: string): string => {
return value.replace(/"/g, '&quot;');
};
const isBuilt = this._environmentService.isBuilt;
const html = `<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-eval' '${WEB_WORKER_IFRAME.sha}' ${isBuilt ? 'https:' : 'http: https:'}; worker-src data:; connect-src ${isBuilt ? 'https:' : 'http: https:'}" />
<meta id="vscode-worker-src" data-value="${escapeAttribute(workerSrc)}" />
<meta id="vscode-web-worker-ext-host-id" data-value="${escapeAttribute(vscodeWebWorkerExtHostId)}" />
</head>
<body>
<script>${WEB_WORKER_IFRAME.js}</script>
</body>
</html>`;
const iframeContent = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
iframe.setAttribute('src', iframeContent);
iframe.setAttribute('src', `${webWorkerExtensionHostIframeSrc}?vscodeWebWorkerExtHostId=${vscodeWebWorkerExtHostId}`);
const barrier = new Barrier();
let port!: MessagePort;
let barrierError: Error | null = null;
let barrierHasError = false;
let startTimeout: any = null;
const rejectBarrier = (exitCode: number, error: Error) => {
barrierError = error;
barrierHasError = true;
onUnexpectedError(barrierError);
clearTimeout(startTimeout);
this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, barrierError.message]);
barrier.open();
};
const resolveBarrier = (messagePort: MessagePort) => {
port = messagePort;
clearTimeout(startTimeout);
barrier.open();
};
startTimeout = setTimeout(() => {
rejectBarrier(ExtensionHostExitCode.StartTimeout10s, new Error('The Web Worker Extension Host did not start in 10s'));
}, 10000);
this._register(dom.addDisposableListener(window, 'message', (event) => {
if (event.source !== iframe.contentWindow) {
@@ -128,27 +170,28 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
err.message = message;
err.name = name;
err.stack = stack;
onUnexpectedError(err);
this._onDidExit.fire([18, err.message]);
return;
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
}
const { data } = event.data;
if (barrier.isOpen() || !(data instanceof MessagePort)) {
console.warn('UNEXPECTED message', event);
this._onDidExit.fire([81, 'UNEXPECTED message']);
return;
const err = new Error('UNEXPECTED message');
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
}
port = data;
barrier.open();
resolveBarrier(data);
}));
document.body.appendChild(iframe);
this._layoutService.container.appendChild(iframe);
this._register(toDisposable(() => iframe.remove()));
// await MessagePort and use it to directly communicate
// with the worker extension host
await barrier.wait();
if (barrierHasError) {
throw barrierError;
}
port.onmessage = (event) => {
const { data } = event;
if (!(data instanceof ArrayBuffer)) {
@@ -173,7 +216,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
private async _startOutsideIframe(): Promise<IMessagePassingProtocol> {
const emitter = new Emitter<VSBuffer>();
const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost');
const url = getWorkerBootstrapUrl(FileAccess.asBrowserUri('../worker/extensionHostWorkerMain.js', require).toString(true), 'WorkerExtensionHost');
const worker = new Worker(url, { name: 'WorkerExtensionHost' });
const barrier = new Barrier();
@@ -183,7 +226,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
const { data } = event;
if (barrier.isOpen() || !(data instanceof MessagePort)) {
console.warn('UNEXPECTED message', event);
this._onDidExit.fire([81, 'UNEXPECTED message']);
this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, 'UNEXPECTED message']);
return;
}
port = data;
@@ -207,7 +250,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
worker.onerror = (event) => {
console.error(event.message, event.error);
this._onDidExit.fire([81, event.message || event.error]);
this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, event.message || event.error]);
};
// keep for cleanup
@@ -278,7 +321,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
vscodeVersion: this._productService.vscodeVersion, // {{SQL CARBON EDIT}} add vscode version
parentPid: -1,
environment: {
isExtensionDevelopmentDebug: false, //todo@jrieken web
isExtensionDevelopmentDebug: this._environmentService.debugRenderer,
appName: this._productService.nameLong,
appUriScheme: this._productService.urlProtocol,
appLanguage: platform.language,
@@ -303,7 +346,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
logFile: this._extensionHostLogFile,
autoStart: initData.autoStart,
remote: {
authority: this._environmentService.configuration.remoteAuthority,
authority: this._environmentService.remoteAuthority,
connectionData: null,
isRemote: false
},

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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';
@@ -15,16 +16,22 @@ import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionM
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { IProductService } from 'vs/platform/product/common/productService';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Schemas } from 'vs/base/common/network';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@@ -33,12 +40,27 @@ export function parseScannedExtension(extension: ITranslatedScannedExtension): I
return {
identifier: new ExtensionIdentifier(`${extension.packageJSON.publisher}.${extension.packageJSON.name}`),
isBuiltin: extension.type === ExtensionType.System,
isUserBuiltin: false,
isUnderDevelopment: false,
extensionLocation: extension.location,
...extension.packageJSON,
};
}
class DeltaExtensionsQueueItem {
constructor(
public readonly toAdd: IExtension[],
public readonly toRemove: string[]
) { }
}
export const enum ExtensionRunningLocation {
None,
LocalProcess,
LocalWebWorker,
Remote
}
export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: undefined;
@@ -66,6 +88,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private readonly _proposedApiController: ProposedApiController;
private readonly _isExtensionDevHost: boolean;
protected readonly _isExtensionDevTestFromCli: boolean;
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
private _inHandleDeltaExtensions: boolean;
protected _runningLocation: Map<string, ExtensionRunningLocation>;
// --- Members used per extension host process
protected _extensionHostManagers: ExtensionHostManager[];
@@ -74,13 +99,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private _extensionHostExtensionRuntimeErrors: Map<string, Error[]>;
constructor(
protected readonly _runningLocationClassifier: ExtensionRunningLocationClassifier,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@INotificationService protected readonly _notificationService: INotificationService,
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
@ITelemetryService protected readonly _telemetryService: ITelemetryService,
@IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
@IFileService protected readonly _fileService: IFileService,
@IProductService protected readonly _productService: IProductService
@IProductService protected readonly _productService: IProductService,
@IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@IConfigurationService protected readonly _configurationService: IConfigurationService,
) {
super();
@@ -103,8 +132,280 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli;
this._deltaExtensionsQueue = [];
this._inHandleDeltaExtensions = false;
this._runningLocation = new Map<string, ExtensionRunningLocation>();
this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {
let toAdd: IExtension[] = [];
let toRemove: string[] = [];
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);
}
}
this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));
}));
this._register(this._extensionManagementService.onDidInstallExtension((event) => {
if (event.local) {
if (this._safeInvokeIsEnabled(event.local)) {
// an extension has been installed
this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], []));
}
}
}));
this._register(this._extensionManagementService.onDidUninstallExtension((event) => {
if (!event.error) {
// an extension has been uninstalled
this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id]));
}
}));
}
protected _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null {
for (const extensionHostManager of this._extensionHostManagers) {
if (extensionHostManager.kind === kind) {
return extensionHostManager;
}
}
return null;
}
//#region deltaExtensions
private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {
this._deltaExtensionsQueue.push(item);
if (this._inHandleDeltaExtensions) {
// Let the current item finish, the new one will be picked up
return;
}
while (this._deltaExtensionsQueue.length > 0) {
const item = this._deltaExtensionsQueue.shift()!;
try {
this._inHandleDeltaExtensions = true;
await this._deltaExtensions(item.toAdd, item.toRemove);
} finally {
this._inHandleDeltaExtensions = false;
}
}
}
private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise<void> {
let toAdd: IExtensionDescription[] = [];
for (let i = 0, len = _toAdd.length; i < len; i++) {
const extension = _toAdd[i];
if (!this._canAddExtension(extension)) {
continue;
}
const extensionDescription = await this._scanSingleExtension(extension);
if (!extensionDescription) {
// could not scan extension...
continue;
}
toAdd.push(extensionDescription);
}
let toRemove: IExtensionDescription[] = [];
for (let i = 0, len = _toRemove.length; i < len; i++) {
const extensionId = _toRemove[i];
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
// ignore disabling/uninstalling an extension which is not running
continue;
}
if (!this.canRemoveExtension(extensionDescription)) {
// uses non-dynamic extension point or is activated
continue;
}
toRemove.push(extensionDescription);
}
if (toAdd.length === 0 && toRemove.length === 0) {
return;
}
// Update the local registry
const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
this._onDidChangeExtensions.fire(undefined);
toRemove = toRemove.concat(result.removedDueToLooping);
if (result.removedDueToLooping.length > 0) {
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));
}
// enable or disable proposed API per extension
this._checkEnableProposedApi(toAdd);
// Update extension points
this._doHandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove));
// Update the extension host
await this._updateExtensionsOnExtHosts(toAdd, toRemove.map(e => e.identifier));
for (let i = 0; i < toAdd.length; i++) {
this._activateAddedExtensionIfNeeded(toAdd[i]);
}
}
private async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const groupedToRemove: ExtensionIdentifier[][] = [];
const groupRemove = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToRemove[extensionHostKind] = filterByRunningLocation(toRemove, extId => extId, this._runningLocation, extensionRunningLocation);
};
groupRemove(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupRemove(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupRemove(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
for (const extensionId of toRemove) {
this._runningLocation.delete(ExtensionIdentifier.toKey(extensionId));
}
const groupedToAdd: IExtensionDescription[][] = [];
const groupAdd = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToAdd[extensionHostKind] = filterByRunningLocation(toAdd, ext => ext.identifier, this._runningLocation, extensionRunningLocation);
};
for (const extension of toAdd) {
const extensionKind = getExtensionKind(extension, this._productService, this._configurationService);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote);
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
groupAdd(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupAdd(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupAdd(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
const promises: Promise<void>[] = [];
for (const extensionHostKind of [ExtensionHostKind.LocalProcess, ExtensionHostKind.LocalWebWorker, ExtensionHostKind.Remote]) {
const toAdd = groupedToAdd[extensionHostKind];
const toRemove = groupedToRemove[extensionHostKind];
if (toAdd.length > 0 || toRemove.length > 0) {
const extensionHostManager = this._getExtensionHostManager(extensionHostKind);
if (extensionHostManager) {
promises.push(extensionHostManager.deltaExtensions(toAdd, toRemove));
}
}
}
await Promise.all(promises);
}
public canAddExtension(extensionDescription: IExtensionDescription): boolean {
return this._canAddExtension(toExtension(extensionDescription));
}
private _canAddExtension(extension: IExtension): boolean {
const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id);
if (extensionDescription) {
// this extension is already running (most likely at a different version)
return false;
}
// Check if extension is renamed
if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) {
return false;
}
const extensionKind = getExtensionKind(extension.manifest, this._productService, this._configurationService);
const isRemote = extension.location.scheme === Schemas.vscodeRemote;
const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote);
if (runningLocation === ExtensionRunningLocation.None) {
return false;
}
return true;
}
public canRemoveExtension(extension: IExtensionDescription): boolean {
const extensionDescription = this._registry.getExtensionDescription(extension.identifier);
if (!extensionDescription) {
// ignore removing an extension which is not running
return false;
}
if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extensionDescription.identifier))) {
// Extension is running, cannot remove it safely
return false;
}
return true;
}
private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise<void> {
let shouldActivate = false;
let shouldActivateReason: string | null = null;
let hasWorkspaceContains = false;
if (Array.isArray(extensionDescription.activationEvents)) {
for (let activationEvent of extensionDescription.activationEvents) {
// TODO@joao: there's no easy way to contribute this
if (activationEvent === 'onUri') {
activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`;
}
if (this._allRequestedActivateEvents.has(activationEvent)) {
// This activation event was fired before the extension was added
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
if (activationEvent === '*') {
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
if (/^workspaceContains/.test(activationEvent)) {
hasWorkspaceContains = true;
}
if (activationEvent === 'onStartupFinished') {
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
}
}
if (shouldActivate) {
await Promise.all(
this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! }))
).then(() => { });
} else if (hasWorkspaceContains) {
const workspace = await this._contextService.getCompleteWorkspace();
const forceUsingSearch = !!this._environmentService.remoteAuthority;
const host: IWorkspaceContainsActivationHost = {
folders: workspace.folders.map(folder => folder.uri),
forceUsingSearch: forceUsingSearch,
exists: (uri) => this._fileService.exists(uri),
checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token))
};
const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription);
if (!result) {
return;
}
await Promise.all(
this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent }))
).then(() => { });
}
}
//#endregion
protected async _initialize(): Promise<void> {
perf.mark('willLoadExtensions');
this._startExtensionHosts(true, []);
@@ -169,14 +470,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
//#region IExtensionService
public canAddExtension(extension: IExtensionDescription): boolean {
return false;
}
public canRemoveExtension(extension: IExtensionDescription): boolean {
return false;
}
public restartExtensionHost(): void {
this._stopExtensionHosts();
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
@@ -325,7 +618,15 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return false;
}
return this._extensionEnablementService.isEnabled(toExtension(extension));
return this._safeInvokeIsEnabled(toExtension(extension));
}
protected _safeInvokeIsEnabled(extension: IExtension): boolean {
try {
return this._extensionEnablementService.isEnabled(extension);
} catch (err) {
return false;
}
}
protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void {
@@ -463,9 +764,43 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[];
protected abstract _scanAndHandleExtensions(): Promise<void>;
protected abstract _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null>;
public abstract _onExtensionHostExit(code: number): void;
}
export class ExtensionRunningLocationClassifier {
constructor(
@IProductService private readonly _productService: IProductService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
public readonly pickRunningLocation: (extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean) => ExtensionRunningLocation,
) {
}
public determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation> {
const allExtensionKinds = new Map<string, ExtensionKind[]>();
localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService)));
remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService)));
const localExtensionsSet = new Set<string>();
localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const remoteExtensionsSet = new Set<string>();
remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => {
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
return this.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely);
};
const runningLocation = new Map<string, ExtensionRunningLocation>();
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
return runningLocation;
}
}
class ProposedApiController {
private readonly enableProposedApiFor: string[];
@@ -473,16 +808,16 @@ class ProposedApiController {
private readonly productAllowProposedApi: Set<string>;
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IProductService productService: IProductService
) {
// Make enabled proposed API be lowercase for case insensitive comparison
this.enableProposedApiFor = (environmentService.extensionEnabledProposedApi || []).map(id => id.toLowerCase());
this.enableProposedApiFor = (_environmentService.extensionEnabledProposedApi || []).map(id => id.toLowerCase());
this.enableProposedApiForAll =
!environmentService.isBuilt || // always allow proposed API when running out of sources
(!!environmentService.extensionDevelopmentLocationURI && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this.enableProposedApiFor.length === 0 && Array.isArray(environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
!_environmentService.isBuilt || // always allow proposed API when running out of sources
(!!_environmentService.extensionDevelopmentLocationURI && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension
(this.enableProposedApiFor.length === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID
this.productAllowProposedApi = new Set<string>();
if (isNonEmptyArray(productService.extensionAllowedProposedApi)) {
@@ -504,7 +839,7 @@ class ProposedApiController {
extension.enableProposedApi = false;
console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
} else {
} else if (this._environmentService.isBuilt) {
// proposed api is available when developing or when an extension was explicitly
// spelled out via a command line argument
console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`);
@@ -516,3 +851,7 @@ class ProposedApiController {
return this.productAllowProposedApi.has(ExtensionIdentifier.toKey(id));
}
}
function filterByRunningLocation<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): T[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(extId(ext))) === desiredRunningLocation);
}

View File

@@ -21,6 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IExtHostRpcService, ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IURITransformerService, URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
import { IExtHostExtensionService, IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
export interface IExitFn {
(code?: number): any;
@@ -59,15 +60,16 @@ export class ExtensionHostMain {
const instaService: IInstantiationService = new InstantiationService(services, true);
// todo@joh
// ugly self - inject
const terminalService = instaService.invokeFunction(accessor => accessor.get(IExtHostTerminalService));
this._disposables.add(terminalService);
const logService = instaService.invokeFunction(accessor => accessor.get(ILogService));
this._disposables.add(logService);
logService.info('extension host started');
logService.trace('initData', initData);
// todo@joh
// ugly self - inject
// must call initialize *after* creating the extension service
// because `initialize` itself creates instances that depend on it

View File

@@ -6,7 +6,6 @@
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -23,6 +22,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHost, ExtensionHostKind, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { CATEGORIES } from 'vs/workbench/common/actions';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
@@ -267,7 +267,8 @@ export class ExtensionHostManager extends Disposable {
authority: {
authority: remoteAuthority,
host: pieces[0],
port: parseInt(pieces[1], 10)
port: parseInt(pieces[1], 10),
connectionToken: undefined
}
});
}
@@ -345,7 +346,7 @@ class RPCLogger implements IRPCProtocolLogger {
const colorTable = colorTables[initiator];
const color = LOG_USE_COLORS ? colorTable[req % colorTable.length] : '#000000';
let args = [`%c[${direction}]%c[${strings.pad(totalLength, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]%c${strings.pad(req, 5, ' ')} - ${str}`, 'color: darkgreen', 'color: grey', 'color: grey', `color: ${color}`];
let args = [`%c[${direction}]%c[${String(totalLength).padStart(7)}]%c[len: ${String(msgLength).padStart(5)}]%c${String(req).padStart(5)} - ${str}`, 'color: darkgreen', 'color: grey', 'color: grey', `color: ${color}`];
if (/\($/.test(str)) {
args = args.concat(data);
args.push(')');
@@ -405,7 +406,7 @@ registerAction2(class MeasureExtHostLatencyAction extends Action2 {
value: nls.localize('measureExtHostLatency', "Measure Extension Host Latency"),
original: 'Measure Extension Host Latency'
},
category: { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' },
category: CATEGORIES.Developer,
f1: true
});
}

View File

@@ -5,6 +5,13 @@
import { VSBuffer } from 'vs/base/common/buffer';
export const enum ExtensionHostExitCode {
// nodejs uses codes 1-13 and exit codes >128 are signal exits
VersionMismatch = 55,
StartTimeout10s = 56,
UnexpectedError = 81,
}
export interface IExtHostReadyMessage {
type: 'VSCODE_EXTHOST_IPC_READY';
}

View File

@@ -273,6 +273,7 @@ export function throwProposedApiError(extension: IExtensionDescription): never {
export function toExtension(extensionDescription: IExtensionDescription): IExtension {
return {
type: extensionDescription.isBuiltin ? ExtensionType.System : ExtensionType.User,
isBuiltin: extensionDescription.isBuiltin || extensionDescription.isUserBuiltin,
identifier: { id: getGalleryExtensionId(extensionDescription.publisher, extensionDescription.name), uuid: extensionDescription.uuid },
manifest: extensionDescription,
location: extensionDescription.extensionLocation,
@@ -283,6 +284,7 @@ export function toExtensionDescription(extension: IExtension): IExtensionDescrip
return {
identifier: new ExtensionIdentifier(extension.identifier.id),
isBuiltin: extension.type === ExtensionType.System,
isUserBuiltin: extension.type === ExtensionType.User && extension.isBuiltin,
isUnderDevelopment: false,
extensionLocation: extension.location,
...extension.manifest,

View File

@@ -19,9 +19,9 @@ import { IRemoteAuthorityResolverService, IRemoteConnectionData } from 'vs/platf
import * as platform from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -92,7 +92,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
addressProvider: {
getAddress: async () => {
const { authority } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority);
return { host: authority.host, port: authority.port };
return { host: authority.host, port: authority.port, connectionToken: authority.connectionToken };
}
},
signService: this._signService,
@@ -205,8 +205,15 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
// Collect all identifiers for extension ids which can be considered "resolved"
const remoteExtensions = new Set<string>();
remoteInitData.extensions.forEach((extension) => remoteExtensions.add(ExtensionIdentifier.toKey(extension.identifier.value)));
const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier);
const hostExtensions = remoteInitData.allExtensions.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier);
const hostExtensions = (
remoteInitData.allExtensions
.filter(extension => !remoteExtensions.has(ExtensionIdentifier.toKey(extension.identifier.value)))
.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier)
);
const workspace = this._contextService.getWorkspace();
return {
commit: this._productService.commit,

View File

@@ -1,47 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const WEB_WORKER_IFRAME = {
sha: 'sha256-r24mDVsMuFEo8ChaY9ppVJKbY3CUM4I12Aw/yscWZbg=',
js: `
(function() {
const workerSrc = document.getElementById('vscode-worker-src').getAttribute('data-value');
const worker = new Worker(workerSrc, { name: 'WorkerExtensionHost' });
const vscodeWebWorkerExtHostId = document.getElementById('vscode-web-worker-ext-host-id').getAttribute('data-value');
worker.onmessage = (event) => {
const { data } = event;
if (!(data instanceof MessagePort)) {
console.warn('Unknown data received', event);
window.parent.postMessage({
vscodeWebWorkerExtHostId,
error: {
name: 'Error',
message: 'Unknown data received',
stack: []
}
}, '*');
return;
}
window.parent.postMessage({
vscodeWebWorkerExtHostId,
data: data
}, '*', [data]);
};
worker.onerror = (event) => {
console.error(event.message, event.error);
window.parent.postMessage({
vscodeWebWorkerExtHostId,
error: {
name: event.error ? event.error.name : '',
message: event.error ? event.error.message : '',
stack: event.error ? event.error.stack : []
}
}, '*');
};
})();
`
};

View File

@@ -4,17 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import * as errors from 'vs/base/common/errors';
import { Schemas } from 'vs/base/common/network';
import { FileAccess, Schemas } from 'vs/base/common/network';
import * as objects from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';
import { originalFSPath } from 'vs/base/common/resources';
import { joinPath, originalFSPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -32,7 +29,7 @@ interface IExtensionCacheData {
let _SystemExtensionsRoot: string | null = null;
function getSystemExtensionsRoot(): string {
if (!_SystemExtensionsRoot) {
_SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions'));
_SystemExtensionsRoot = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions'));
}
return _SystemExtensionsRoot;
}
@@ -40,7 +37,7 @@ function getSystemExtensionsRoot(): string {
let _ExtraDevSystemExtensionsRoot: string | null = null;
function getExtraDevSystemExtensionsRoot(): string {
if (!_ExtraDevSystemExtensionsRoot) {
_ExtraDevSystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions'));
_ExtraDevSystemExtensionsRoot = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', '.build', 'builtInExtensions'));
}
return _ExtraDevSystemExtensionsRoot;
}
@@ -54,7 +51,7 @@ export class CachedExtensionScanner {
constructor(
@INotificationService private readonly _notificationService: INotificationService,
@IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
@IHostService private readonly _hostService: IHostService,
@IProductService private readonly _productService: IProductService
@@ -264,7 +261,7 @@ export class CachedExtensionScanner {
if (devMode) {
const builtInExtensions = Promise.resolve<IBuiltInExtension[]>(productService.builtInExtensions || []);
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
const controlFilePath = joinPath(environmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json').fsPath;
const controlFile = pfs.readFile(controlFilePath, 'utf8')
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
@@ -276,18 +273,14 @@ export class CachedExtensionScanner {
finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions);
}
const userExtensions = (
extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath
? Promise.resolve([])
: this._scanExtensionsWithCache(
hostService,
notificationService,
environmentService,
USER_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
log
)
);
const userExtensions = (this._scanExtensionsWithCache(
hostService,
notificationService,
environmentService,
USER_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
log
));
// Always load developed extensions while extensions development
let developedExtensions: Promise<IExtensionDescription[]> = Promise.resolve([]);

View File

@@ -6,7 +6,7 @@
import { LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractExtensionService, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningLocation, ExtensionRunningLocationClassifier, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import * as nls from 'vs/nls';
import { runWhenIdle } from 'vs/base/common/async';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -16,46 +16,36 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { Schemas } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
import { IProductService } from 'vs/platform/product/common/productService';
import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints';
import { flatten } from 'vs/base/common/arrays';
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { exists } from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
class DeltaExtensionsQueueItem {
constructor(
public readonly toAdd: IExtension[],
public readonly toRemove: string[]
) { }
}
import { CATEGORIES } from 'vs/workbench/common/actions';
import { Schemas } from 'vs/base/common/network';
import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
private readonly _enableLocalWebWorker: boolean;
private readonly _remoteInitData: Map<string, IRemoteExtensionHostInitData>;
private _runningLocation: Map<string, ExtensionRunningLocation>;
private readonly _extensionScanner: CachedExtensionScanner;
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@@ -65,316 +55,63 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,
@IFileService fileService: IFileService,
@IProductService productService: IProductService,
@IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService,
@IElectronService private readonly _electronService: IElectronService,
@INativeHostService private readonly _nativeHostService: INativeHostService,
@IHostService private readonly _hostService: IHostService,
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ILogService private readonly _logService: ILogService,
) {
super(
new ExtensionRunningLocationClassifier(
productService,
configurationService,
(extensionKinds, isInstalledLocally, isInstalledRemotely) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely)
),
instantiationService,
notificationService,
_environmentService,
telemetryService,
extensionEnablementService,
fileService,
productService
productService,
extensionManagementService,
contextService,
configurationService,
);
this._enableLocalWebWorker = this._configurationService.getValue<boolean>(webWorkerExtHostConfig);
this._remoteInitData = new Map<string, IRemoteExtensionHostInitData>();
this._runningLocation = new Map<string, ExtensionRunningLocation>();
this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
this._deltaExtensionsQueue = [];
this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {
let toAdd: IExtension[] = [];
let toRemove: string[] = [];
for (const extension of extensions) {
if (this._extensionEnablementService.isEnabled(extension)) {
// an extension has been enabled
toAdd.push(extension);
} else {
// an extension has been disabled
toRemove.push(extension.identifier.id);
}
}
this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));
}));
this._register(this._extensionManagementService.onDidInstallExtension((event) => {
if (event.local) {
if (this._extensionEnablementService.isEnabled(event.local)) {
// an extension has been installed
this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], []));
}
}
}));
this._register(this._extensionManagementService.onDidUninstallExtension((event) => {
if (!event.error) {
// an extension has been uninstalled
this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id]));
}
}));
// delay extension host creation and extension scanning
// until the workbench is running. we cannot defer the
// extension host more (LifecyclePhase.Restored) because
// some editors require the extension host to restore
// and this would result in a deadlock
// see https://github.com/Microsoft/vscode/issues/41322
// see https://github.com/microsoft/vscode/issues/41322
this._lifecycleService.when(LifecyclePhase.Ready).then(() => {
// reschedule to ensure this runs after restoring viewlets, panels, and editors
runWhenIdle(() => {
this._initialize();
}, 50 /*max delay*/);
});
// delay notification for extensions disabled until workbench restored
if (this._extensionEnablementService.allUserExtensionsDisabled) {
this._lifecycleService.when(LifecyclePhase.Restored).then(() => {
this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{
label: nls.localize('Reload', "Reload"),
run: () => {
this._hostService.reload();
}
}]);
});
}
}
private _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null {
for (const extensionHostManager of this._extensionHostManagers) {
if (extensionHostManager.kind === kind) {
return extensionHostManager;
}
protected _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null> {
if (extension.location.scheme === Schemas.vscodeRemote) {
return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System);
}
return null;
return this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger());
}
//#region deltaExtensions
private _inHandleDeltaExtensions = false;
private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {
this._deltaExtensionsQueue.push(item);
if (this._inHandleDeltaExtensions) {
// Let the current item finish, the new one will be picked up
return;
}
while (this._deltaExtensionsQueue.length > 0) {
const item = this._deltaExtensionsQueue.shift()!;
try {
this._inHandleDeltaExtensions = true;
await this._deltaExtensions(item.toAdd, item.toRemove);
} finally {
this._inHandleDeltaExtensions = false;
}
}
}
private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise<void> {
if (this._environmentService.configuration.remoteAuthority) {
return;
}
let toAdd: IExtensionDescription[] = [];
for (let i = 0, len = _toAdd.length; i < len; i++) {
const extension = _toAdd[i];
if (!this._canAddExtension(extension)) {
continue;
}
const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger());
if (!extensionDescription) {
// could not scan extension...
continue;
}
toAdd.push(extensionDescription);
}
let toRemove: IExtensionDescription[] = [];
for (let i = 0, len = _toRemove.length; i < len; i++) {
const extensionId = _toRemove[i];
const extensionDescription = this._registry.getExtensionDescription(extensionId);
if (!extensionDescription) {
// ignore disabling/uninstalling an extension which is not running
continue;
}
if (!this._canRemoveExtension(extensionDescription)) {
// uses non-dynamic extension point or is activated
continue;
}
toRemove.push(extensionDescription);
}
if (toAdd.length === 0 && toRemove.length === 0) {
return;
}
// Update the local registry
const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
this._onDidChangeExtensions.fire(undefined);
toRemove = toRemove.concat(result.removedDueToLooping);
if (result.removedDueToLooping.length > 0) {
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));
}
// enable or disable proposed API per extension
this._checkEnableProposedApi(toAdd);
// Update extension points
this._doHandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove));
// Update the extension host
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
await localProcessExtensionHost.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
}
for (let i = 0; i < toAdd.length; i++) {
this._activateAddedExtensionIfNeeded(toAdd[i]);
}
}
public canAddExtension(extensionDescription: IExtensionDescription): boolean {
return this._canAddExtension(toExtension(extensionDescription));
}
private _canAddExtension(extension: IExtension): boolean {
if (this._environmentService.configuration.remoteAuthority) {
return false;
}
if (extension.location.scheme !== Schemas.file) {
return false;
}
// {{ SQL CARBON EDIT }}
if (extension.manifest.forceReload) {
return false;
}
const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id);
if (extensionDescription) {
// this extension is already running (most likely at a different version)
return false;
}
// Check if extension is renamed
if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) {
return false;
}
return true;
}
public canRemoveExtension(extension: IExtensionDescription): boolean {
if (this._environmentService.configuration.remoteAuthority) {
return false;
}
if (extension.extensionLocation.scheme !== Schemas.file) {
return false;
}
const extensionDescription = this._registry.getExtensionDescription(extension.identifier);
if (!extensionDescription) {
// ignore removing an extension which is not running
return false;
}
return this._canRemoveExtension(extensionDescription);
}
private _canRemoveExtension(extension: IExtensionDescription): boolean {
if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) {
// Extension is running, cannot remove it safely
return false;
}
return true;
}
private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise<void> {
let shouldActivate = false;
let shouldActivateReason: string | null = null;
let hasWorkspaceContains = false;
if (Array.isArray(extensionDescription.activationEvents)) {
for (let activationEvent of extensionDescription.activationEvents) {
// TODO@joao: there's no easy way to contribute this
if (activationEvent === 'onUri') {
activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`;
}
if (this._allRequestedActivateEvents.has(activationEvent)) {
// This activation event was fired before the extension was added
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
if (activationEvent === '*') {
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
if (/^workspaceContains/.test(activationEvent)) {
hasWorkspaceContains = true;
}
if (activationEvent === 'onStartupFinished') {
shouldActivate = true;
shouldActivateReason = activationEvent;
break;
}
}
}
if (shouldActivate) {
await Promise.all(
this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! }))
).then(() => { });
} else if (hasWorkspaceContains) {
const workspace = await this._contextService.getCompleteWorkspace();
const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority;
const host: IWorkspaceContainsActivationHost = {
folders: workspace.folders.map(folder => folder.uri),
forceUsingSearch: forceUsingSearch,
exists: (path) => exists(path),
checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token))
};
const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription);
if (!result) {
return;
}
await Promise.all(
this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent }))
).then(() => { });
}
}
//#endregion
private async _scanAllLocalExtensions(): Promise<IExtensionDescription[]> {
return flatten(await Promise.all([
this._extensionScanner.scannedExtensions,
@@ -387,7 +124,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
getInitData: async () => {
if (isInitialStart) {
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
const runningLocation = _determineRunningLocation(this._productService, this._configurationService, localExtensions, [], false, this._enableLocalWebWorker);
const runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, []);
const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation);
return {
autoStart: false,
@@ -416,6 +153,28 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
private _pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation {
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledLocally) {
// ui extensions run locally if possible
return ExtensionRunningLocation.LocalProcess;
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
}
if (extensionKind === 'workspace' && !this._environmentService.remoteAuthority) {
// workspace extensions also run locally if there is no remote
return ExtensionRunningLocation.LocalProcess;
}
if (extensionKind === 'web' && isInstalledLocally && this._enableLocalWebWorker) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
}
}
return ExtensionRunningLocation.None;
}
protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
@@ -441,7 +200,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
super._onExtensionHostCrashed(extensionHost, code, signal);
if (extensionHost.kind === ExtensionHostKind.LocalProcess) {
if (code === 55) {
if (code === ExtensionHostExitCode.VersionMismatch) {
this._notificationService.prompt(
Severity.Error,
nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."),
@@ -464,7 +223,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly."),
[{
label: nls.localize('devTools', "Open Developer Tools"),
run: () => this._electronService.openDevTools()
run: () => this._nativeHostService.openDevTools()
},
{
label: nls.localize('restart', "Restart Extension Host"),
@@ -503,7 +262,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
private async _resolveAuthorityAgain(): Promise<void> {
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
const remoteAuthority = this._environmentService.remoteAuthority;
if (!remoteAuthority) {
return;
}
@@ -521,7 +280,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
protected async _scanAndHandleExtensions(): Promise<void> {
this._extensionScanner.startScanningExtensions(this.createLogger());
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
const remoteAuthority = this._environmentService.remoteAuthority;
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
@@ -584,7 +343,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
private async _startLocalExtensionHost(localExtensions: IExtensionDescription[], remoteAuthority: string | undefined = undefined, remoteEnv: IRemoteAgentEnvironment | null = null, remoteExtensions: IExtensionDescription[] = []): Promise<void> {
this._runningLocation = _determineRunningLocation(this._productService, this._configurationService, localExtensions, remoteExtensions, Boolean(remoteAuthority), this._enableLocalWebWorker);
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
// remove non-UI extensions from the local extensions
const localProcessExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalProcess);
@@ -633,10 +392,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
public _onExtensionHostExit(code: number): void {
if (this._isExtensionDevTestFromCli) {
// When CLI testing make sure to exit with proper exit code
this._electronService.exit(code);
this._nativeHostService.exit(code);
} else {
// Expected development extension termination: When the extension host goes down we also shutdown the window
this._electronService.closeWindow();
this._nativeHostService.closeWindow();
}
}
@@ -703,58 +462,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
}
const enum ExtensionRunningLocation {
None,
LocalProcess,
LocalWebWorker,
Remote
}
export function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map<string, ExtensionKind[]>, hasRemote: boolean, hasLocalWebWorker: boolean): Map<string, ExtensionRunningLocation> {
const localExtensionsSet = new Set<string>();
localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const remoteExtensionsSet = new Set<string>();
remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => {
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledLocally) {
// ui extensions run locally if possible
return ExtensionRunningLocation.LocalProcess;
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
return ExtensionRunningLocation.Remote;
}
if (extensionKind === 'workspace' && !hasRemote) {
// workspace extensions also run locally if there is no remote
return ExtensionRunningLocation.LocalProcess;
}
if (extensionKind === 'web' && isInstalledLocally && hasLocalWebWorker) {
// web worker extensions run in the local web worker if possible
return ExtensionRunningLocation.LocalWebWorker;
}
}
return ExtensionRunningLocation.None;
};
const runningLocation = new Map<string, ExtensionRunningLocation>();
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
return runningLocation;
}
function _determineRunningLocation(productService: IProductService, configurationService: IConfigurationService, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], hasRemote: boolean, hasLocalWebWorker: boolean): Map<string, ExtensionRunningLocation> {
const allExtensionKinds = new Map<string, ExtensionKind[]>();
localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService)));
remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, productService, configurationService)));
return determineRunningLocation(localExtensions, remoteExtensions, allExtensionKinds, hasRemote, hasLocalWebWorker);
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}
@@ -767,7 +474,7 @@ class RestartExtensionHostAction extends Action2 {
super({
id: 'workbench.action.restartExtensionHost',
title: { value: nls.localize('restartExtensionHost', "Restart Extension Host"), original: 'Restart Extension Host' },
category: { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' },
category: CATEGORIES.Developer,
f1: true
});
}

View File

@@ -6,8 +6,8 @@
import * as nls from 'vs/nls';
import { ChildProcess, fork } from 'child_process';
import { Server, Socket, createServer } from 'net';
import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
import { FileAccess } from 'vs/base/common/network';
import { timeout } from 'vs/base/common/async';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Emitter, Event } from 'vs/base/common/event';
@@ -20,16 +20,15 @@ import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteCo
import { findFreePort } from 'vs/base/node/ports';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
import { generateRandomPipeName, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol';
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
@@ -90,9 +89,9 @@ export class LocalProcessExtensionHost implements IExtensionHost {
private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@INotificationService private readonly _notificationService: INotificationService,
@IElectronService private readonly _electronService: IElectronService,
@INativeHostService private readonly _nativeHostService: INativeHostService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILogService private readonly _logService: ILogService,
@ILabelService private readonly _labelService: ILabelService,
@@ -122,7 +121,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate()));
this._toDispose.add(this._extensionHostDebugService.onClose(event => {
if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) {
this._electronService.closeWindow();
this._nativeHostService.closeWindow();
}
}));
this._toDispose.add(this._extensionHostDebugService.onReload(event => {
@@ -171,6 +170,12 @@ export class LocalProcessExtensionHost implements IExtensionHost {
delete env['DYLD_LIBRARY_PATH'];
}
if (this._isExtensionDevHost) {
// Unset `VSCODE_NODE_CACHED_DATA_DIR` when developing extensions because it might
// be that dependencies, that otherwise would be cached, get modified.
delete env['VSCODE_NODE_CACHED_DATA_DIR'];
}
const opts = {
env,
// We only detach the extension host on windows. Linux and Mac orphan by default
@@ -216,7 +221,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
}
// Run Extension Host as fork of current process
this._extensionHostProcess = fork(getPathFromAmdModule(require, 'bootstrap-fork'), ['--type=extensionHost'], opts);
this._extensionHostProcess = fork(FileAccess.asFileUri('bootstrap-fork', require).fsPath, ['--type=extensionHost'], opts);
// Catch all output coming from the extension host process
type Output = { data: string, format: string[] };
@@ -278,7 +283,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
// Help in case we fail to start it
let startupTimeoutHandle: any;
if (!this._environmentService.isBuilt && !this._environmentService.configuration.remoteAuthority || this._isExtensionDevHost) {
if (!this._environmentService.isBuilt && !this._environmentService.remoteAuthority || this._isExtensionDevHost) {
startupTimeoutHandle = setTimeout(() => {
const msg = this._isExtensionDevDebugBrk
? nls.localize('extensionHost.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.")
@@ -310,7 +315,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
*/
private _tryListenOnPipe(): Promise<string> {
return new Promise<string>((resolve, reject) => {
const pipeName = generateRandomPipeName();
const pipeName = createRandomIPCHandle();
this._namedPipeServer = createServer();
this._namedPipeServer.on('error', reject);
@@ -467,7 +472,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false
},
remote: {
authority: this._environmentService.configuration.remoteAuthority,
authority: this._environmentService.remoteAuthority,
connectionData: null,
isRemote: false
},

View File

@@ -13,7 +13,7 @@ import { PersistentProtocol, ProtocolConstants, BufferedEmitter } from 'vs/base/
import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import product from 'vs/platform/product/common/product';
import { IInitData } from 'vs/workbench/api/common/extHost.protocol';
import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
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';
@@ -225,7 +225,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
if (rendererCommit && myCommit) {
// Running in the built version where commits are defined
if (rendererCommit !== myCommit) {
nativeExit(55);
nativeExit(ExtensionHostExitCode.VersionMismatch);
}
}
@@ -300,7 +300,7 @@ export async function startExtensionHostProcess(): Promise<void> {
const renderer = await connectToRenderer(protocol);
const { initData } = renderer;
// setup things
patchProcess(!!initData.environment.extensionTestsLocationURI); // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708)
patchProcess(!!initData.environment.extensionTestsLocationURI); // to support other test frameworks like Jasmin that use process.exit (https://github.com/microsoft/vscode/issues/37708)
initData.environment.useHostProxy = args.useHostProxy !== undefined ? args.useHostProxy !== 'false' : undefined;
// host abstraction

View File

@@ -5,7 +5,7 @@
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as semver from 'semver-umd';
import * as semver from 'vs/base/common/semver/semver';
import * as json from 'vs/base/common/json';
import * as arrays from 'vs/base/common/arrays';
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
@@ -51,7 +51,9 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
const errors: json.ParseError[] = [];
const manifest = json.parse(manifestContents.toString(), errors);
if (errors.length === 0 && json.getNodeType(manifest) === 'object') {
if (json.getNodeType(manifest) !== 'object') {
this._log.error(this._absoluteFolderPath, nls.localize('jsonParseInvalidType', "Invalid manifest file {0}: Not an JSON object.", this._absoluteManifestPath));
} else if (errors.length === 0) {
if (manifest.__metadata) {
manifest.uuid = manifest.__metadata.id;
}
@@ -291,6 +293,7 @@ export interface IRelaxedExtensionDescription {
version: string;
publisher: string;
isBuiltin: boolean;
isUserBuiltin: boolean;
isUnderDevelopment: boolean;
extensionLocation: URI;
engines: {
@@ -304,6 +307,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
validate(_extensionDescription: IExtensionDescription): IExtensionDescription | null {
let extensionDescription = <IRelaxedExtensionDescription>_extensionDescription;
extensionDescription.isBuiltin = this._isBuiltin;
extensionDescription.isUserBuiltin = !this._isBuiltin && !!extensionDescription.isUserBuiltin;
extensionDescription.isUnderDevelopment = this._isUnderDevelopment;
let notices: string[] = [];
@@ -448,7 +452,7 @@ export class ExtensionScannerInput {
public readonly absoluteFolderPath: string,
public readonly isBuiltin: boolean,
public readonly isUnderDevelopment: boolean,
public readonly tanslations: Translations
public readonly translations: Translations
) {
// Keep empty!! (JSON.parse)
}
@@ -458,7 +462,7 @@ export class ExtensionScannerInput {
devMode: input.devMode,
locale: input.locale,
pseudo: input.locale === 'pseudo',
translations: input.tanslations
translations: input.translations
};
}
@@ -472,7 +476,7 @@ export class ExtensionScannerInput {
&& a.isBuiltin === b.isBuiltin
&& a.isUnderDevelopment === b.isUnderDevelopment
&& a.mtime === b.mtime
&& Translations.equals(a.tanslations, b.tanslations)
&& Translations.equals(a.translations, b.translations)
);
}
}

View File

@@ -11,8 +11,6 @@ import * as os from 'os';
import * as fs from 'fs';
import * as cp from 'child_process';
import { assign } from 'vs/base/common/objects';
import { endsWith } from 'vs/base/common/strings';
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ProxyAgent } from 'vscode-proxy-agent';
@@ -290,7 +288,7 @@ function noProxyFromEnv(envValue?: string) {
return () => false;
}
return (hostname: string, port: string) => filters.some(({ domain, port: filterPort }) => {
return endsWith(`.${hostname.toLowerCase()}`, domain) && (!filterPort || port === filterPort);
return `.${hostname.toLowerCase()}`.endsWith(domain) && (!filterPort || port === filterPort);
});
}
@@ -314,20 +312,20 @@ function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProx
return {
http: {
off: assign({}, http, patches(http, resolveProxy, { config: 'off' }, certSetting, true)),
on: assign({}, http, patches(http, resolveProxy, { config: 'on' }, certSetting, true)),
override: assign({}, http, patches(http, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: assign({}, http, patches(http, resolveProxy, proxySetting, certSetting, true)),
default: assign(http, patches(http, resolveProxy, proxySetting, certSetting, false)) // run last
off: Object.assign({}, http, patches(http, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, http, patches(http, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, http, patches(http, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, http, patches(http, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(http, patches(http, resolveProxy, proxySetting, certSetting, false)) // run last
} as Record<string, typeof http>,
https: {
off: assign({}, https, patches(https, resolveProxy, { config: 'off' }, certSetting, true)),
on: assign({}, https, patches(https, resolveProxy, { config: 'on' }, certSetting, true)),
override: assign({}, https, patches(https, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: assign({}, https, patches(https, resolveProxy, proxySetting, certSetting, true)),
default: assign(https, patches(https, resolveProxy, proxySetting, certSetting, false)) // run last
off: Object.assign({}, https, patches(https, resolveProxy, { config: 'off' }, certSetting, true)),
on: Object.assign({}, https, patches(https, resolveProxy, { config: 'on' }, certSetting, true)),
override: Object.assign({}, https, patches(https, resolveProxy, { config: 'override' }, certSetting, true)),
onRequest: Object.assign({}, https, patches(https, resolveProxy, proxySetting, certSetting, true)),
default: Object.assign(https, patches(https, resolveProxy, proxySetting, certSetting, false)) // run last
} as Record<string, typeof https>,
tls: assign(tls, tlsPatches(tls))
tls: Object.assign(tls, tlsPatches(tls))
};
}
@@ -470,9 +468,12 @@ let _caCertificates: ReturnType<typeof readCaCertificates> | Promise<undefined>;
async function getCaCertificates(extHostLogService: ILogService) {
if (!_caCertificates) {
_caCertificates = readCaCertificates()
.then(res => res && res.certs.length ? res : undefined)
.then(res => {
extHostLogService.debug('ProxyResolver#getCaCertificates count', res && res.certs.length);
return res && res.certs.length ? res : undefined;
})
.catch(err => {
extHostLogService.error('ProxyResolver#getCertificates', toErrorMessage(err));
extHostLogService.error('ProxyResolver#getCaCertificates error', toErrorMessage(err));
return undefined;
});
}

View File

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

View File

@@ -22,7 +22,7 @@ declare function postMessage(data: any, transferables?: Transferable[]): void;
declare namespace self {
let close: any;
let postMessage: any;
let addEventLister: any;
let addEventListener: any;
let indexedDB: { open: any, [k: string]: any };
let caches: { open: any, [k: string]: any };
}
@@ -33,8 +33,8 @@ self.close = () => console.trace(`'close' has been blocked`);
const nativePostMessage = postMessage.bind(self);
self.postMessage = () => console.trace(`'postMessage' has been blocked`);
// const nativeAddEventLister = addEventListener.bind(self);
self.addEventLister = () => console.trace(`'addEventListener' has been blocked`);
// const nativeAddEventListener = addEventListener.bind(self);
self.addEventListener = () => console.trace(`'addEventListener' has been blocked`);
(<any>self)['AMDLoader'] = undefined;
(<any>self)['NLSLoaderPlugin'] = undefined;
@@ -45,9 +45,9 @@ self.addEventLister = () => console.trace(`'addEventListener' has been blocked`)
(<any>self)['webkitResolveLocalFileSystemSyncURL'] = undefined;
(<any>self)['webkitResolveLocalFileSystemURL'] = undefined;
if (location.protocol === 'data:') {
if ((<any>self).Worker) {
// make sure new Worker(...) always uses data:
const _Worker = Worker;
const _Worker = (<any>self).Worker;
Worker = <any>function (stringUrl: string | URL, options?: WorkerOptions) {
const js = `importScripts('${stringUrl}');`;
options = options || {};

View File

@@ -8,14 +8,20 @@
let MonacoEnvironment = (<any>self).MonacoEnvironment;
let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../';
const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value });
if (typeof (<any>self).define !== 'function' || !(<any>self).define.amd) {
importScripts(monacoBaseUrl + 'vs/loader.js');
let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js';
if (trustedTypesPolicy) {
loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc);
}
importScripts(loaderSrc as string);
}
require.config({
baseUrl: monacoBaseUrl,
catchError: true,
createTrustedScriptURL: (value: string) => value
trustedTypesPolicy
});
require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err));

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data:; script-src 'unsafe-eval' 'sha256-DhNBVT9y4y9LG937ZrEbN5CwALd+WSpQnG3z5u1MOFk=' http: https:; connect-src http: https:" />
</head>
<body>
<script>
(function() {
const idMatch = document.location.search.match(/\bvscodeWebWorkerExtHostId=([0-9a-f\-]+)/i);
const vscodeWebWorkerExtHostId = idMatch ? idMatch[1] : '';
function sendError(error) {
window.parent.postMessage({
vscodeWebWorkerExtHostId,
error: {
name: error ? error.name : '',
message: error ? error.message : '',
stack: error ? error.stack : []
}
}, '*');
}
try {
const worker = new Worker('extensionHostWorkerMain.js', { name: 'WorkerExtensionHost' });
worker.onmessage = (event) => {
const { data } = event;
window.parent.postMessage({
vscodeWebWorkerExtHostId,
data
}, '*', [data]);
};
worker.onerror = (event) => {
console.error(event.message, event.error);
sendError(event.error);
};
} catch(err) {
console.error(err);
sendError(err);
}
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; child-src 'self' data:; script-src 'unsafe-eval' 'sha256-DhNBVT9y4y9LG937ZrEbN5CwALd+WSpQnG3z5u1MOFk=' https:; connect-src https:" />
</head>
<body>
<script>
(function() {
const idMatch = document.location.search.match(/\bvscodeWebWorkerExtHostId=([0-9a-f\-]+)/i);
const vscodeWebWorkerExtHostId = idMatch ? idMatch[1] : '';
function sendError(error) {
window.parent.postMessage({
vscodeWebWorkerExtHostId,
error: {
name: error ? error.name : '',
message: error ? error.message : '',
stack: error ? error.stack : []
}
}, '*');
}
try {
const worker = new Worker('extensionHostWorkerMain.js', { name: 'WorkerExtensionHost' });
worker.onmessage = (event) => {
const { data } = event;
window.parent.postMessage({
vscodeWebWorkerExtHostId,
data
}, '*', [data]);
};
worker.onerror = (event) => {
console.error(event.message, event.error);
sendError(event.error);
};
} catch(err) {
console.error(err);
sendError(err);
}
})();
</script>
</body>
</html>