mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -8,18 +8,9 @@ import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ExtensionIdentifier, IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
|
||||
export interface IExtensionDescription extends IExtensionManifest {
|
||||
readonly identifier: ExtensionIdentifier;
|
||||
readonly uuid?: string;
|
||||
readonly isBuiltin: boolean;
|
||||
readonly isUnderDevelopment: boolean;
|
||||
readonly extensionLocation: URI;
|
||||
enableProposedApi?: boolean;
|
||||
}
|
||||
|
||||
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
|
||||
identifier: new ExtensionIdentifier('nullExtensionDescription'),
|
||||
name: 'Null Extension Description',
|
||||
@@ -42,10 +33,15 @@ export interface IMessage {
|
||||
|
||||
export interface IExtensionsStatus {
|
||||
messages: IMessage[];
|
||||
activationTimes: ActivationTimes;
|
||||
activationTimes: ActivationTimes | undefined;
|
||||
runtimeErrors: Error[];
|
||||
}
|
||||
|
||||
export type ExtensionActivationError = string | MissingDependencyError;
|
||||
export class MissingDependencyError {
|
||||
constructor(readonly dependency: string) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* e.g.
|
||||
* ```
|
||||
@@ -89,7 +85,7 @@ export interface IExtensionHostProfile {
|
||||
/**
|
||||
* Extension id or one of the four known program states.
|
||||
*/
|
||||
export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self' | null;
|
||||
export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self';
|
||||
|
||||
export class ActivationTimes {
|
||||
constructor(
|
||||
@@ -259,3 +255,27 @@ export function toExtension(extensionDescription: IExtensionDescription): IExten
|
||||
location: extensionDescription.extensionLocation,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export class NullExtensionService implements IExtensionService {
|
||||
_serviceBrand: any;
|
||||
onDidRegisterExtensions: Event<void> = Event.None;
|
||||
onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]> = Event.None;
|
||||
onDidChangeExtensions: Event<void> = Event.None;
|
||||
onWillActivateByEvent: Event<IWillActivateEvent> = Event.None;
|
||||
onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = Event.None;
|
||||
activateByEvent(_activationEvent: string): Promise<void> { return Promise.resolve(undefined); }
|
||||
whenInstalledExtensionsRegistered(): Promise<boolean> { return Promise.resolve(true); }
|
||||
getExtensions(): Promise<IExtensionDescription[]> { return Promise.resolve([]); }
|
||||
getExtension() { return Promise.resolve(undefined); }
|
||||
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
|
||||
getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); }
|
||||
canProfileExtensionHost(): boolean { return false; }
|
||||
getInspectPort(): number { return 0; }
|
||||
startExtensionHostProfile(): Promise<ProfileSession> { return Promise.resolve(Object.create(null)); }
|
||||
restartExtensionHost(): void { }
|
||||
startExtensionHost(): void { }
|
||||
stopExtensionHost(): void { }
|
||||
canAddExtension(): boolean { return false; }
|
||||
canRemoveExtension(): boolean { return false; }
|
||||
}
|
||||
@@ -10,11 +10,12 @@ import Severity from 'vs/base/common/severity';
|
||||
import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionDescription, IMessage } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IMessage } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
|
||||
export type ExtensionKind = 'workspace' | 'ui' | undefined;
|
||||
|
||||
export class ExtensionMessageCollector {
|
||||
|
||||
@@ -67,6 +68,7 @@ export interface IExtensionPointHandler<T> {
|
||||
export interface IExtensionPoint<T> {
|
||||
name: string;
|
||||
setHandler(handler: IExtensionPointHandler<T>): void;
|
||||
defaultExtensionKind: ExtensionKind;
|
||||
}
|
||||
|
||||
export class ExtensionPointUserDelta<T> {
|
||||
@@ -105,18 +107,16 @@ export class ExtensionPointUserDelta<T> {
|
||||
export class ExtensionPoint<T> implements IExtensionPoint<T> {
|
||||
|
||||
public readonly name: string;
|
||||
public readonly isDynamic: boolean;
|
||||
public readonly defaultExtensionKind: ExtensionKind;
|
||||
|
||||
private _handler: IExtensionPointHandler<T> | null;
|
||||
private _handlerCalled: boolean;
|
||||
private _users: IExtensionPointUser<T>[] | null;
|
||||
private _delta: ExtensionPointUserDelta<T> | null;
|
||||
|
||||
constructor(name: string, isDynamic: boolean) {
|
||||
constructor(name: string, defaultExtensionKind: ExtensionKind) {
|
||||
this.name = name;
|
||||
this.isDynamic = isDynamic;
|
||||
this.defaultExtensionKind = defaultExtensionKind;
|
||||
this._handler = null;
|
||||
this._handlerCalled = false;
|
||||
this._users = null;
|
||||
this._delta = null;
|
||||
}
|
||||
@@ -140,12 +140,7 @@ export class ExtensionPoint<T> implements IExtensionPoint<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._handlerCalled && !this.isDynamic) {
|
||||
throw new Error('The extension point is not dynamic!');
|
||||
}
|
||||
|
||||
try {
|
||||
this._handlerCalled = true;
|
||||
this._handler(this._users, this._delta);
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
@@ -367,10 +362,10 @@ export const schema = {
|
||||
};
|
||||
|
||||
export interface IExtensionPointDescriptor {
|
||||
isDynamic?: boolean;
|
||||
extensionPoint: string;
|
||||
deps?: IExtensionPoint<any>[];
|
||||
jsonSchema: IJSONSchema;
|
||||
defaultExtensionKind?: ExtensionKind;
|
||||
}
|
||||
|
||||
export class ExtensionsRegistryImpl {
|
||||
@@ -385,7 +380,7 @@ export class ExtensionsRegistryImpl {
|
||||
if (hasOwnProperty.call(this._extensionPoints, desc.extensionPoint)) {
|
||||
throw new Error('Duplicate extension point: ' + desc.extensionPoint);
|
||||
}
|
||||
let result = new ExtensionPoint<T>(desc.extensionPoint, desc.isDynamic || false);
|
||||
let result = new ExtensionPoint<T>(desc.extensionPoint, desc.defaultExtensionKind);
|
||||
this._extensionPoints[desc.extensionPoint] = result;
|
||||
|
||||
schema.properties['contributes'].properties[desc.extensionPoint] = desc.jsonSchema;
|
||||
|
||||
@@ -5,23 +5,22 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
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 * as objects from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { fsPath } from 'vs/base/common/resources';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints';
|
||||
|
||||
interface IExtensionCacheData {
|
||||
@@ -71,7 +70,7 @@ export class CachedExtensionScanner {
|
||||
const version = pkg.version;
|
||||
const commit = product.commit;
|
||||
const devMode = !!process.env['VSCODE_DEV'];
|
||||
const locale = platform.locale;
|
||||
const locale = platform.language;
|
||||
const input = new ExtensionScannerInput(version, commit, locale, devMode, path, isBuiltin, false, translations);
|
||||
return ExtensionScanner.scanSingleExtension(input, log);
|
||||
}
|
||||
@@ -250,7 +249,7 @@ export class CachedExtensionScanner {
|
||||
const version = pkg.version;
|
||||
const commit = product.commit;
|
||||
const devMode = !!process.env['VSCODE_DEV'];
|
||||
const locale = platform.locale;
|
||||
const locale = platform.language;
|
||||
|
||||
const builtinExtensions = this._scanExtensionsWithCache(
|
||||
windowService,
|
||||
@@ -297,7 +296,7 @@ export class CachedExtensionScanner {
|
||||
let developedExtensions: Promise<IExtensionDescription[]> = Promise.resolve([]);
|
||||
if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) {
|
||||
developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions(
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, fsPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, originalFSPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,26 +21,26 @@ import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console';
|
||||
import { findFreePort, randomPort } from 'vs/base/node/ports';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { Protocol, generateRandomPipeName, BufferedProtocol } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { IBroadcast, IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService';
|
||||
import { IBroadcast, IBroadcastService } from 'vs/workbench/services/broadcast/electron-browser/broadcastService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import product from 'vs/platform/node/product';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInitData } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol';
|
||||
import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/node/extensionHostProtocol';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export interface IExtensionHostStarter {
|
||||
readonly onCrashed: Event<[number, string]>;
|
||||
start(): Promise<IMessagePassingProtocol>;
|
||||
getInspectPort(): number;
|
||||
readonly onCrashed: Event<[number, string | null]>;
|
||||
start(): Promise<IMessagePassingProtocol> | null;
|
||||
getInspectPort(): number | undefined;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function parseExtensionDevOptions(environmentService: IEnvironmentService
|
||||
const debugOk = !extDevLoc || extDevLoc.scheme === Schemas.file;
|
||||
let isExtensionDevDebug = debugOk && typeof environmentService.debugExtensionHost.port === 'number';
|
||||
let isExtensionDevDebugBrk = debugOk && !!environmentService.debugExtensionHost.break;
|
||||
let isExtensionDevTestFromCli = isExtensionDevHost && !!environmentService.extensionTestsPath && !environmentService.debugExtensionHost.break;
|
||||
let isExtensionDevTestFromCli = isExtensionDevHost && !!environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break;
|
||||
return {
|
||||
isExtensionDevHost,
|
||||
isExtensionDevDebug,
|
||||
@@ -79,15 +79,15 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
private readonly _isExtensionDevTestFromCli: boolean;
|
||||
|
||||
// State
|
||||
private _lastExtensionHostError: string;
|
||||
private _lastExtensionHostError: string | null;
|
||||
private _terminating: boolean;
|
||||
|
||||
// Resources, in order they get acquired/created when .start() is called:
|
||||
private _namedPipeServer: Server;
|
||||
private _namedPipeServer: Server | null;
|
||||
private _inspectPort: number;
|
||||
private _extensionHostProcess: ChildProcess;
|
||||
private _extensionHostConnection: Socket;
|
||||
private _messageProtocol: Promise<IMessagePassingProtocol>;
|
||||
private _extensionHostProcess: ChildProcess | null;
|
||||
private _extensionHostConnection: Socket | null;
|
||||
private _messageProtocol: Promise<IMessagePassingProtocol> | null;
|
||||
|
||||
constructor(
|
||||
private readonly _autoStart: boolean,
|
||||
@@ -101,7 +101,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ICrashReporterService private readonly _crashReporterService: ICrashReporterService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService
|
||||
) {
|
||||
@@ -159,7 +158,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
}
|
||||
}
|
||||
|
||||
public start(): Promise<IMessagePassingProtocol> {
|
||||
public start(): Promise<IMessagePassingProtocol> | null {
|
||||
if (this._terminating) {
|
||||
// .terminate() was called
|
||||
return null;
|
||||
@@ -185,7 +184,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
// We detach because we have noticed that when the renderer exits, its child processes
|
||||
// (i.e. extension host) are taken down in a brutal fashion by the OS
|
||||
detached: !!isWindows,
|
||||
execArgv: <string[]>undefined,
|
||||
execArgv: undefined as string[] | undefined,
|
||||
silent: true
|
||||
};
|
||||
|
||||
@@ -201,7 +200,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
}
|
||||
}
|
||||
|
||||
const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost');
|
||||
const crashReporterOptions = undefined; // TODO@electron pass this in as options to the extension host after verifying this actually works
|
||||
if (crashReporterOptions) {
|
||||
opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions);
|
||||
}
|
||||
@@ -229,9 +228,14 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
|
||||
// Print out extension host output
|
||||
onDebouncedOutput(output => {
|
||||
const inspectorUrlMatch = !this._environmentService.isBuilt && output.data && output.data.match(/ws:\/\/([^\s]+)/);
|
||||
const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/);
|
||||
if (inspectorUrlMatch) {
|
||||
console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black');
|
||||
if (!this._environmentService.isBuilt) {
|
||||
console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black');
|
||||
}
|
||||
if (!this._inspectPort) {
|
||||
this._inspectPort = Number(inspectorUrlMatch[2]);
|
||||
}
|
||||
} else {
|
||||
console.group('Extension Host');
|
||||
console.log(output.data, ...output.format);
|
||||
@@ -301,7 +305,9 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
this._namedPipeServer = createServer();
|
||||
this._namedPipeServer.on('error', reject);
|
||||
this._namedPipeServer.listen(pipeName, () => {
|
||||
this._namedPipeServer.removeListener('error', reject);
|
||||
if (this._namedPipeServer) {
|
||||
this._namedPipeServer.removeListener('error', reject);
|
||||
}
|
||||
resolve(pipeName);
|
||||
});
|
||||
});
|
||||
@@ -342,15 +348,19 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
// Wait for the extension host to connect to our named pipe
|
||||
// and wrap the socket in the message passing protocol
|
||||
let handle = setTimeout(() => {
|
||||
this._namedPipeServer.close();
|
||||
this._namedPipeServer = null;
|
||||
if (this._namedPipeServer) {
|
||||
this._namedPipeServer.close();
|
||||
this._namedPipeServer = null;
|
||||
}
|
||||
reject('timeout');
|
||||
}, 60 * 1000);
|
||||
|
||||
this._namedPipeServer.on('connection', socket => {
|
||||
this._namedPipeServer!.on('connection', socket => {
|
||||
clearTimeout(handle);
|
||||
this._namedPipeServer.close();
|
||||
this._namedPipeServer = null;
|
||||
if (this._namedPipeServer) {
|
||||
this._namedPipeServer.close();
|
||||
this._namedPipeServer = null;
|
||||
}
|
||||
this._extensionHostConnection = socket;
|
||||
|
||||
// using a buffered message protocol here because between now
|
||||
@@ -426,16 +436,17 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined,
|
||||
appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined,
|
||||
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
|
||||
extensionTestsPath: this._environmentService.extensionTestsPath,
|
||||
globalStorageHome: URI.file(this._environmentService.globalStorageHome)
|
||||
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
|
||||
globalStorageHome: URI.file(this._environmentService.globalStorageHome),
|
||||
userHome: URI.file(this._environmentService.userHome)
|
||||
},
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
|
||||
configuration: workspace.configuration,
|
||||
folders: workspace.folders,
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
|
||||
configuration: withNullAsUndefined(workspace.configuration),
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace)
|
||||
},
|
||||
resolvedExtensions: [],
|
||||
hostExtensions: [],
|
||||
extensions: extensionDescriptions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
@@ -503,6 +514,18 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
}
|
||||
}
|
||||
|
||||
public enableInspector(): Promise<void> {
|
||||
if (this._inspectPort) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// send SIGUSR1 and wait a little the actual port is read from the process stdout which we
|
||||
// scan here: https://github.com/Microsoft/vscode/blob/67ffab8dcd1a6752d8b62bcd13d7020101eef568/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts#L225-L240
|
||||
if (this._extensionHostProcess) {
|
||||
this._extensionHostProcess.kill('SIGUSR1');
|
||||
}
|
||||
return timeout(1000);
|
||||
}
|
||||
|
||||
public getInspectPort(): number {
|
||||
return this._inspectPort;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { ProfileSession, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
|
||||
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
|
||||
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
||||
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
@@ -36,7 +36,7 @@ const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
|
||||
export class ExtensionHostProcessManager extends Disposable {
|
||||
|
||||
public readonly onDidCrash: Event<[number, string]>;
|
||||
public readonly onDidCrash: Event<[number, string | null]>;
|
||||
|
||||
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
|
||||
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
|
||||
@@ -45,13 +45,13 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
|
||||
*/
|
||||
private readonly _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
|
||||
private _extensionHostProcessRPCProtocol: RPCProtocol;
|
||||
private _extensionHostProcessRPCProtocol: RPCProtocol | null;
|
||||
private readonly _extensionHostProcessCustomers: IDisposable[];
|
||||
private readonly _extensionHostProcessWorker: IExtensionHostStarter;
|
||||
/**
|
||||
* winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
|
||||
*/
|
||||
private _extensionHostProcessProxy: Promise<{ value: ExtHostExtensionServiceShape; }>;
|
||||
private _extensionHostProcessProxy: Promise<{ value: ExtHostExtensionServiceShape; } | null> | null;
|
||||
|
||||
constructor(
|
||||
extensionHostProcessWorker: IExtensionHostStarter,
|
||||
@@ -67,7 +67,7 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
|
||||
this._extensionHostProcessWorker = extensionHostProcessWorker;
|
||||
this.onDidCrash = this._extensionHostProcessWorker.onCrashed;
|
||||
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
|
||||
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start()!.then(
|
||||
(protocol) => {
|
||||
return { value: this._createExtensionHostCustomers(protocol) };
|
||||
},
|
||||
@@ -105,16 +105,14 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} - Add new getExtensionHostProcessWorker method
|
||||
public getExtenstionHostProcessWorker(): IExtensionHostStarter {
|
||||
return this._extensionHostProcessWorker;
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
private async measure(): Promise<ExtHostLatencyResult> {
|
||||
const latency = await this._measureLatency();
|
||||
const down = await this._measureDown();
|
||||
const up = await this._measureUp();
|
||||
private async measure(): Promise<ExtHostLatencyResult | null> {
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
return null;
|
||||
}
|
||||
const latency = await this._measureLatency(proxy);
|
||||
const down = await this._measureDown(proxy);
|
||||
const up = await this._measureUp(proxy);
|
||||
return {
|
||||
remoteAuthority: this._remoteAuthority,
|
||||
latency,
|
||||
@@ -123,10 +121,20 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
private async _measureLatency(): Promise<number> {
|
||||
private async _getExtensionHostProcessProxy(): Promise<ExtHostExtensionServiceShape | null> {
|
||||
if (!this._extensionHostProcessProxy) {
|
||||
return null;
|
||||
}
|
||||
const p = await this._extensionHostProcessProxy;
|
||||
if (!p) {
|
||||
return null;
|
||||
}
|
||||
return p.value;
|
||||
}
|
||||
|
||||
private async _measureLatency(proxy: ExtHostExtensionServiceShape): Promise<number> {
|
||||
const COUNT = 10;
|
||||
|
||||
const { value: proxy } = await this._extensionHostProcessProxy;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < COUNT; i++) {
|
||||
const sw = StopWatch.create(true);
|
||||
@@ -141,10 +149,9 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return (byteCount * 1000 * 8) / elapsedMillis;
|
||||
}
|
||||
|
||||
private async _measureUp(): Promise<number> {
|
||||
private async _measureUp(proxy: ExtHostExtensionServiceShape): Promise<number> {
|
||||
const SIZE = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
const { value: proxy } = await this._extensionHostProcessProxy;
|
||||
let b = Buffer.alloc(SIZE, Math.random() % 256);
|
||||
const sw = StopWatch.create(true);
|
||||
await proxy.$test_up(b);
|
||||
@@ -152,10 +159,9 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return ExtensionHostProcessManager._convert(SIZE, sw.elapsed());
|
||||
}
|
||||
|
||||
private async _measureDown(): Promise<number> {
|
||||
private async _measureDown(proxy: ExtHostExtensionServiceShape): Promise<number> {
|
||||
const SIZE = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
const { value: proxy } = await this._extensionHostProcessProxy;
|
||||
const sw = StopWatch.create(true);
|
||||
await proxy.$test_down(SIZE);
|
||||
sw.stop();
|
||||
@@ -177,9 +183,9 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
this._register(this._extensionHostProcessRPCProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState)));
|
||||
const extHostContext: IExtHostContext = {
|
||||
remoteAuthority: this._remoteAuthority,
|
||||
getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._extensionHostProcessRPCProtocol.getProxy(identifier),
|
||||
set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._extensionHostProcessRPCProtocol.set(identifier, instance),
|
||||
assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._extensionHostProcessRPCProtocol.assertRegistered(identifiers),
|
||||
getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._extensionHostProcessRPCProtocol!.getProxy(identifier),
|
||||
set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._extensionHostProcessRPCProtocol!.set(identifier, instance),
|
||||
assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._extensionHostProcessRPCProtocol!.assertRegistered(identifiers),
|
||||
};
|
||||
|
||||
// Named customers
|
||||
@@ -205,10 +211,12 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
|
||||
}
|
||||
|
||||
public activate(extension: ExtensionIdentifier, activationEvent: string): Promise<void> {
|
||||
return this._extensionHostProcessProxy.then((proxy) => {
|
||||
return proxy.value.$activate(extension, activationEvent);
|
||||
});
|
||||
public async activate(extension: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
return false;
|
||||
}
|
||||
return proxy.$activate(extension, activationEvent);
|
||||
}
|
||||
|
||||
public activateByEvent(activationEvent: string): Promise<void> {
|
||||
@@ -247,7 +255,7 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
|
||||
public async resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
|
||||
const authorityPlusIndex = remoteAuthority.indexOf('+');
|
||||
if (authorityPlusIndex === -1) {
|
||||
// This authority does not need to be resolved, simply parse the port number
|
||||
@@ -255,19 +263,30 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return Promise.resolve({
|
||||
authority: remoteAuthority,
|
||||
host: pieces[0],
|
||||
port: parseInt(pieces[1], 10),
|
||||
syncExtensions: false
|
||||
port: parseInt(pieces[1], 10)
|
||||
});
|
||||
}
|
||||
return this._extensionHostProcessProxy.then(proxy => proxy.value.$resolveAuthority(remoteAuthority));
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
throw new Error(`Cannot resolve authority`);
|
||||
}
|
||||
return proxy.$resolveAuthority(remoteAuthority);
|
||||
}
|
||||
|
||||
public start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
|
||||
return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds));
|
||||
public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
return proxy.$startExtensionHost(enabledExtensionIds);
|
||||
}
|
||||
|
||||
public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
|
||||
return this._extensionHostProcessProxy.then(proxy => proxy.value.$deltaExtensions(toAdd, toRemove));
|
||||
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
return proxy.$deltaExtensions(toAdd, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +354,7 @@ interface ExtHostLatencyResult {
|
||||
}
|
||||
|
||||
interface ExtHostLatencyProvider {
|
||||
measure(): Promise<ExtHostLatencyResult>;
|
||||
measure(): Promise<ExtHostLatencyResult | null>;
|
||||
}
|
||||
|
||||
let providers: ExtHostLatencyProvider[] = [];
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import { Profile, ProfileNode } from 'v8-inspect-profiler';
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { realpathSync } from 'vs/base/node/extfs';
|
||||
import { IExtensionDescription, IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export class ExtensionHostProfiler {
|
||||
|
||||
@@ -33,12 +34,12 @@ export class ExtensionHostProfiler {
|
||||
|
||||
let nodes = profile.nodes;
|
||||
let idsToNodes = new Map<number, ProfileNode>();
|
||||
let idsToSegmentId = new Map<number, ProfileSegmentId>();
|
||||
let idsToSegmentId = new Map<number, ProfileSegmentId | null>();
|
||||
for (let node of nodes) {
|
||||
idsToNodes.set(node.id, node);
|
||||
}
|
||||
|
||||
function visit(node: ProfileNode, segmentId: ProfileSegmentId) {
|
||||
function visit(node: ProfileNode, segmentId: ProfileSegmentId | null) {
|
||||
if (!segmentId) {
|
||||
switch (node.callFrame.functionName) {
|
||||
case '(root)':
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const localExtensionManagementServerAuthority: string = 'vscode-local';
|
||||
|
||||
@@ -22,9 +24,11 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
|
||||
readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null;
|
||||
|
||||
constructor(
|
||||
localExtensionManagementService: IExtensionManagementService,
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService,
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService
|
||||
) {
|
||||
const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'));
|
||||
|
||||
this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") };
|
||||
const remoteAgentConnection = remoteAgentService.getConnection();
|
||||
if (remoteAgentConnection) {
|
||||
@@ -42,4 +46,6 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService);
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Barrier, runWhenIdle } from 'vs/base/common/async';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -17,33 +17,34 @@ import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IEx
|
||||
import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
|
||||
import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
||||
import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner';
|
||||
import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager';
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
|
||||
schema.properties.engines.properties.vscode.default = `^${pkg.version}`;
|
||||
|
||||
let productAllowProposedApi: Set<string> = null;
|
||||
let productAllowProposedApi: Set<string> | null = null;
|
||||
function allowProposedApiFromProduct(id: ExtensionIdentifier): boolean {
|
||||
// create set if needed
|
||||
if (productAllowProposedApi === null) {
|
||||
if (!productAllowProposedApi) {
|
||||
productAllowProposedApi = new Set<string>();
|
||||
if (isNonEmptyArray(product.extensionAllowedProposedApi)) {
|
||||
product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi.add(ExtensionIdentifier.toKey(id)));
|
||||
product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi!.add(ExtensionIdentifier.toKey(id)));
|
||||
}
|
||||
}
|
||||
return productAllowProposedApi.has(ExtensionIdentifier.toKey(id));
|
||||
@@ -61,7 +62,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
private readonly _extensionHostLogsLocation: URI;
|
||||
private _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _installedExtensionsReady: Barrier;
|
||||
private readonly _isDev: boolean;
|
||||
private readonly _extensionsMessages: Map<string, IMessage[]>;
|
||||
@@ -101,8 +102,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService
|
||||
) {
|
||||
super();
|
||||
this._extensionHostLogsLocation = URI.file(path.posix.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`));
|
||||
this._registry = null;
|
||||
this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`));
|
||||
this._registry = new ExtensionDescriptionRegistry([]);
|
||||
this._installedExtensionsReady = new Barrier();
|
||||
this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
|
||||
this._extensionsMessages = new Map<string, IMessage[]>();
|
||||
@@ -167,7 +168,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
|
||||
while (this._deltaExtensionsQueue.length > 0) {
|
||||
const item = this._deltaExtensionsQueue.shift();
|
||||
const item = this._deltaExtensionsQueue.shift()!;
|
||||
try {
|
||||
this._inHandleDeltaExtensions = true;
|
||||
await this._deltaExtensions(item.toAdd, item.toRemove);
|
||||
@@ -197,8 +198,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
|
||||
const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger());
|
||||
if (!extensionDescription || !this._usesOnlyDynamicExtensionPoints(extensionDescription)) {
|
||||
// uses non-dynamic extension point
|
||||
if (!extensionDescription) {
|
||||
// could not scan extension...
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -227,7 +228,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
|
||||
// Update the local registry
|
||||
this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
|
||||
const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
|
||||
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(', ')));
|
||||
}
|
||||
|
||||
// Update extension points
|
||||
this._rehandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove));
|
||||
@@ -267,28 +272,6 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
private _usesOnlyDynamicExtensionPoints(extension: IExtensionDescription): boolean {
|
||||
const extensionPoints = ExtensionsRegistry.getExtensionPointsMap();
|
||||
if (extension.contributes) {
|
||||
for (let extPointName in extension.contributes) {
|
||||
if (hasOwnProperty.call(extension.contributes, extPointName)) {
|
||||
const extPoint = extensionPoints[extPointName];
|
||||
if (extPoint) {
|
||||
if (!extPoint.isDynamic) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// This extension has a 3rd party (unknown) extension point
|
||||
// ===> require a reload for now...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public canAddExtension(extension: IExtensionDescription): boolean {
|
||||
if (this._windowService.getConfiguration().remoteAuthority) {
|
||||
return false;
|
||||
@@ -306,7 +289,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
return this._usesOnlyDynamicExtensionPoints(extension);
|
||||
return true;
|
||||
}
|
||||
|
||||
public canRemoveExtension(extension: IExtensionDescription): boolean {
|
||||
@@ -333,7 +316,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._usesOnlyDynamicExtensionPoints(extension);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise<void> {
|
||||
@@ -371,7 +354,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
|
||||
if (shouldActivate) {
|
||||
await Promise.all(
|
||||
this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason))
|
||||
this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason!))
|
||||
).then(() => { });
|
||||
}
|
||||
}
|
||||
@@ -453,7 +436,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
this._extensionHostProcessManagers.push(extHostProcessManager);
|
||||
}
|
||||
|
||||
private _onExtensionHostCrashed(code: number, signal: string): void {
|
||||
private _onExtensionHostCrashed(code: number, signal: string | null): void {
|
||||
console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
|
||||
this._stopExtensionHostProcess();
|
||||
|
||||
@@ -567,9 +550,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
for (const extension of extensions) {
|
||||
const extensionKey = ExtensionIdentifier.toKey(extension.identifier);
|
||||
result[extension.identifier.value] = {
|
||||
messages: this._extensionsMessages.get(extensionKey),
|
||||
messages: this._extensionsMessages.get(extensionKey) || [],
|
||||
activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey),
|
||||
runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey),
|
||||
runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -625,12 +608,15 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
const enabledExtensions = await this._getRuntimeExtensions(extensions);
|
||||
|
||||
this._handleExtensionPoints(enabledExtensions);
|
||||
extensionHost.start(enabledExtensions.map(extension => extension.identifier));
|
||||
extensionHost.start(enabledExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
|
||||
this._releaseBarrier();
|
||||
}
|
||||
|
||||
private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void {
|
||||
this._registry = new ExtensionDescriptionRegistry(allExtensions);
|
||||
const result = this._registry.deltaExtensions(allExtensions, []);
|
||||
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(', ')));
|
||||
}
|
||||
|
||||
let availableExtensions = this._registry.getAllExtensionDescriptions();
|
||||
let extensionPoints = ExtensionsRegistry.getExtensionPoints();
|
||||
@@ -681,7 +667,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
(enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args);
|
||||
|
||||
for (const extension of allExtensions) {
|
||||
const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI);
|
||||
const isExtensionUnderDevelopment = (
|
||||
this._environmentService.isExtensionDevelopment
|
||||
&& this._environmentService.extensionDevelopmentLocationURI
|
||||
&& isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI)
|
||||
);
|
||||
// Do not disable extensions under development
|
||||
if (!isExtensionUnderDevelopment) {
|
||||
if (disabledExtensions.some(disabled => areSameExtensions(disabled, { id: extension.identifier.value }))) {
|
||||
@@ -743,7 +733,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
if (!this._extensionsMessages.has(extensionKey)) {
|
||||
this._extensionsMessages.set(extensionKey, []);
|
||||
}
|
||||
this._extensionsMessages.get(extensionKey).push(msg);
|
||||
this._extensionsMessages.get(extensionKey)!.push(msg);
|
||||
|
||||
const extension = this._registry.getExtensionDescription(msg.extensionId);
|
||||
const strMsg = `[${msg.extensionId.value}]: ${msg.message}`;
|
||||
@@ -815,6 +805,16 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void> {
|
||||
const results = await Promise.all(
|
||||
this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent))
|
||||
);
|
||||
const activated = results.some(e => e);
|
||||
if (!activated) {
|
||||
throw new Error(`Unknown extension ${extensionId.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
public _onWillActivateExtension(extensionId: ExtensionIdentifier): void {
|
||||
this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId);
|
||||
}
|
||||
@@ -829,21 +829,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) {
|
||||
this._extensionHostExtensionRuntimeErrors.set(extensionKey, []);
|
||||
}
|
||||
this._extensionHostExtensionRuntimeErrors.get(extensionKey).push(err);
|
||||
this._onDidChangeExtensionsStatus.fire([extensionId]);
|
||||
}
|
||||
|
||||
public _addMessage(extensionId: ExtensionIdentifier, severity: Severity, message: string): void {
|
||||
const extensionKey = ExtensionIdentifier.toKey(extensionId);
|
||||
if (!this._extensionsMessages.has(extensionKey)) {
|
||||
this._extensionsMessages.set(extensionKey, []);
|
||||
}
|
||||
this._extensionsMessages.get(extensionKey).push({
|
||||
type: severity,
|
||||
message: message,
|
||||
extensionId: null,
|
||||
extensionPointId: null
|
||||
});
|
||||
this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err);
|
||||
this._onDidChangeExtensionsStatus.fire([extensionId]);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionService, ExtensionService);
|
||||
@@ -17,6 +17,7 @@ import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const FIVE_MINUTES = 5 * 60 * 1000;
|
||||
const THIRTY_SECONDS = 30 * 1000;
|
||||
@@ -90,9 +91,15 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
|
||||
}
|
||||
|
||||
if (!confirmed) {
|
||||
let uriString = uri.toString();
|
||||
|
||||
if (uriString.length > 40) {
|
||||
uriString = `${uriString.substring(0, 30)}...${uriString.substring(uriString.length - 5)}`;
|
||||
}
|
||||
|
||||
const result = await this.dialogService.confirm({
|
||||
message: localize('confirmUrl', "Allow an extension to open this URL?", extensionId),
|
||||
detail: `${extension.displayName || extension.name} (${extensionId}) wants to open a URL:\n\n${uri.toString()}`,
|
||||
detail: `${extension.displayName || extension.name} (${extensionId}) wants to open a URL:\n\n${uriString}`,
|
||||
primaryButton: localize('open', "&&Open"),
|
||||
type: 'question'
|
||||
});
|
||||
@@ -265,3 +272,5 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
|
||||
this.uriBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler);
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
|
||||
export class RuntimeExtensionsInput extends EditorInput {
|
||||
|
||||
static readonly ID = 'workbench.runtimeExtensions.input';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return RuntimeExtensionsInput.ID;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return nls.localize('extensionsInputName', "Running Extensions");
|
||||
}
|
||||
|
||||
matches(other: any): boolean {
|
||||
if (!(other instanceof RuntimeExtensionsInput)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
resolve(): Promise<any> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
supportsSplitEditor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
return URI.from({
|
||||
scheme: 'runtime-extensions',
|
||||
path: 'default'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class DeltaExtensionsResult {
|
||||
constructor(
|
||||
public readonly removedDueToLooping: IExtensionDescription[]
|
||||
) { }
|
||||
}
|
||||
|
||||
export class ExtensionDescriptionRegistry {
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
public readonly onDidChange = this._onDidChange.event;
|
||||
@@ -60,19 +65,120 @@ export class ExtensionDescriptionRegistry {
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
|
||||
public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]) {
|
||||
this._extensionDescriptions = this._extensionDescriptions.concat(toAdd);
|
||||
const toRemoveSet = new Set<string>();
|
||||
toRemove.forEach(extensionId => toRemoveSet.add(ExtensionIdentifier.toKey(extensionId)));
|
||||
this._extensionDescriptions = this._extensionDescriptions.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): DeltaExtensionsResult {
|
||||
if (toAdd.length > 0) {
|
||||
this._extensionDescriptions = this._extensionDescriptions.concat(toAdd);
|
||||
}
|
||||
|
||||
// Immediately remove looping extensions!
|
||||
const looping = ExtensionDescriptionRegistry._findLoopingExtensions(this._extensionDescriptions);
|
||||
toRemove = toRemove.concat(looping.map(ext => ext.identifier));
|
||||
|
||||
if (toRemove.length > 0) {
|
||||
const toRemoveSet = new Set<string>();
|
||||
toRemove.forEach(extensionId => toRemoveSet.add(ExtensionIdentifier.toKey(extensionId)));
|
||||
this._extensionDescriptions = this._extensionDescriptions.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
}
|
||||
|
||||
this._initialize();
|
||||
this._onDidChange.fire(undefined);
|
||||
return new DeltaExtensionsResult(looping);
|
||||
}
|
||||
|
||||
private static _findLoopingExtensions(extensionDescriptions: IExtensionDescription[]): IExtensionDescription[] {
|
||||
const G = new class {
|
||||
|
||||
private _arcs = new Map<string, string[]>();
|
||||
private _nodesSet = new Set<string>();
|
||||
private _nodesArr: string[] = [];
|
||||
|
||||
addNode(id: string): void {
|
||||
if (!this._nodesSet.has(id)) {
|
||||
this._nodesSet.add(id);
|
||||
this._nodesArr.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
addArc(from: string, to: string): void {
|
||||
this.addNode(from);
|
||||
this.addNode(to);
|
||||
if (this._arcs.has(from)) {
|
||||
this._arcs.get(from)!.push(to);
|
||||
} else {
|
||||
this._arcs.set(from, [to]);
|
||||
}
|
||||
}
|
||||
|
||||
getArcs(id: string): string[] {
|
||||
if (this._arcs.has(id)) {
|
||||
return this._arcs.get(id)!;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
hasOnlyGoodArcs(id: string, good: Set<string>): boolean {
|
||||
const dependencies = G.getArcs(id);
|
||||
for (let i = 0; i < dependencies.length; i++) {
|
||||
if (!good.has(dependencies[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
getNodes(): string[] {
|
||||
return this._nodesArr;
|
||||
}
|
||||
};
|
||||
|
||||
let descs = new Map<string, IExtensionDescription>();
|
||||
for (let extensionDescription of extensionDescriptions) {
|
||||
const extensionId = ExtensionIdentifier.toKey(extensionDescription.identifier);
|
||||
descs.set(extensionId, extensionDescription);
|
||||
if (extensionDescription.extensionDependencies) {
|
||||
for (let _depId of extensionDescription.extensionDependencies) {
|
||||
const depId = ExtensionIdentifier.toKey(_depId);
|
||||
G.addArc(extensionId, depId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize with all extensions with no dependencies.
|
||||
let good = new Set<string>();
|
||||
G.getNodes().filter(id => G.getArcs(id).length === 0).forEach(id => good.add(id));
|
||||
|
||||
// all other extensions will be processed below.
|
||||
let nodes = G.getNodes().filter(id => !good.has(id));
|
||||
|
||||
let madeProgress: boolean;
|
||||
do {
|
||||
madeProgress = false;
|
||||
|
||||
// find one extension which has only good deps
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const id = nodes[i];
|
||||
|
||||
if (G.hasOnlyGoodArcs(id, good)) {
|
||||
nodes.splice(i, 1);
|
||||
i--;
|
||||
good.add(id);
|
||||
madeProgress = true;
|
||||
}
|
||||
}
|
||||
} while (madeProgress);
|
||||
|
||||
// The remaining nodes are bad and have loops
|
||||
return nodes.map(id => descs.get(id)!);
|
||||
}
|
||||
|
||||
public containsActivationEvent(activationEvent: string): boolean {
|
||||
return this._activationMap.has(activationEvent);
|
||||
}
|
||||
|
||||
public containsExtension(extensionId: ExtensionIdentifier): boolean {
|
||||
return this._extensionsMap.has(ExtensionIdentifier.toKey(extensionId));
|
||||
}
|
||||
|
||||
public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] {
|
||||
const extensions = this._activationMap.get(activationEvent);
|
||||
return extensions ? extensions.slice(0) : [];
|
||||
@@ -82,8 +188,8 @@ export class ExtensionDescriptionRegistry {
|
||||
return this._extensionsArr.slice(0);
|
||||
}
|
||||
|
||||
public getExtensionDescription(extensionId: ExtensionIdentifier | string): IExtensionDescription | null {
|
||||
public getExtensionDescription(extensionId: ExtensionIdentifier | string): IExtensionDescription | undefined {
|
||||
const extension = this._extensionsMap.get(ExtensionIdentifier.toKey(extensionId));
|
||||
return extension ? extension : null;
|
||||
return extension ? extension : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
// we don't (yet) throw when extensions parse
|
||||
// uris that have no scheme
|
||||
@@ -49,7 +49,6 @@ export class ExtensionHostMain {
|
||||
private _isTerminating: boolean;
|
||||
private readonly _environment: IEnvironment;
|
||||
private readonly _extensionService: ExtHostExtensionService;
|
||||
private readonly _extHostConfiguration: ExtHostConfiguration;
|
||||
private readonly _extHostLogService: ExtHostLogService;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@@ -57,14 +56,14 @@ export class ExtensionHostMain {
|
||||
|
||||
constructor(protocol: IMessagePassingProtocol, initData: IInitData) {
|
||||
this._isTerminating = false;
|
||||
const uriTransformer: IURITransformer = null;
|
||||
const uriTransformer: IURITransformer | null = null;
|
||||
const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer);
|
||||
|
||||
// ensure URIs are transformed and revived
|
||||
initData = this.transform(initData, rpcProtocol);
|
||||
this._environment = initData.environment;
|
||||
|
||||
const allowExit = !!this._environment.extensionTestsPath; // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708)
|
||||
const allowExit = !!this._environment.extensionTestsLocationURI; // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708)
|
||||
patchProcess(allowExit);
|
||||
|
||||
this._patchPatchedConsole(rpcProtocol.getProxy(MainContext.MainThreadConsole));
|
||||
@@ -74,13 +73,13 @@ export class ExtensionHostMain {
|
||||
this.disposables.push(this._extHostLogService);
|
||||
|
||||
this._searchRequestIdProvider = new Counter();
|
||||
const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, initData.workspace, this._extHostLogService, this._searchRequestIdProvider);
|
||||
const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, this._extHostLogService, this._searchRequestIdProvider, initData.workspace);
|
||||
|
||||
this._extHostLogService.info('extension host started');
|
||||
this._extHostLogService.trace('initData', initData);
|
||||
|
||||
this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace);
|
||||
this._extensionService = new ExtHostExtensionService(nativeExit, initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService);
|
||||
const extHostConfiguraiton = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace);
|
||||
this._extensionService = new ExtHostExtensionService(nativeExit, initData, rpcProtocol, extHostWorkspace, extHostConfiguraiton, this._extHostLogService);
|
||||
|
||||
// error forwarding and stack trace scanning
|
||||
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
|
||||
@@ -88,7 +87,7 @@ export class ExtensionHostMain {
|
||||
this._extensionService.getExtensionPathIndex().then(map => {
|
||||
(<any>Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => {
|
||||
let stackTraceMessage = '';
|
||||
let extension: IExtensionDescription;
|
||||
let extension: IExtensionDescription | undefined;
|
||||
let fileName: string;
|
||||
for (const call of stackTrace) {
|
||||
stackTraceMessage += `\n\tat ${call.toString()}`;
|
||||
@@ -118,7 +117,7 @@ export class ExtensionHostMain {
|
||||
|
||||
private _patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void {
|
||||
// The console is already patched to use `process.send()`
|
||||
const nativeProcessSend = process.send;
|
||||
const nativeProcessSend = process.send!;
|
||||
process.send = (...args: any[]) => {
|
||||
if (args.length === 0 || !args[0] || args[0].type !== '__$console') {
|
||||
return nativeProcessSend.apply(process, args);
|
||||
@@ -154,7 +153,9 @@ export class ExtensionHostMain {
|
||||
initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot));
|
||||
initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome));
|
||||
initData.environment.extensionDevelopmentLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionDevelopmentLocationURI));
|
||||
initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI));
|
||||
initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome));
|
||||
initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome));
|
||||
initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation));
|
||||
initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace);
|
||||
return initData;
|
||||
|
||||
@@ -9,9 +9,9 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { Protocol } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import product from 'vs/platform/node/product';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { IInitData } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/node/extensionHostProtocol';
|
||||
import { exit, ExtensionHostMain } from 'vs/workbench/services/extensions/node/extensionHostMain';
|
||||
|
||||
// With Electron 2.x and node.js 8.x the "natives" module
|
||||
@@ -45,7 +45,7 @@ let onTerminate = function () {
|
||||
|
||||
function createExtHostProtocol(): Promise<IMessagePassingProtocol> {
|
||||
|
||||
const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST;
|
||||
const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!;
|
||||
|
||||
return new Promise<IMessagePassingProtocol>((resolve, reject) => {
|
||||
|
||||
@@ -107,9 +107,14 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
|
||||
setTimeout(() => {
|
||||
const idx = unhandledPromises.indexOf(promise);
|
||||
if (idx >= 0) {
|
||||
unhandledPromises.splice(idx, 1);
|
||||
console.warn('rejected promise not handled within 1 second');
|
||||
onUnexpectedError(reason);
|
||||
promise.catch(e => {
|
||||
unhandledPromises.splice(idx, 1);
|
||||
console.warn(`rejected promise not handled within 1 second: ${e}`);
|
||||
if (e.stack) {
|
||||
console.warn(`stack trace: ${e.stack}`);
|
||||
}
|
||||
onUnexpectedError(reason);
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const enum MessageType {
|
||||
Initialized,
|
||||
Ready,
|
||||
Terminate
|
||||
}
|
||||
|
||||
export function createMessageOfType(type: MessageType): Buffer {
|
||||
const result = Buffer.allocUnsafe(1);
|
||||
|
||||
switch (type) {
|
||||
case MessageType.Initialized: result.writeUInt8(1, 0); break;
|
||||
case MessageType.Ready: result.writeUInt8(2, 0); break;
|
||||
case MessageType.Terminate: result.writeUInt8(3, 0); break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isMessageOfType(message: Buffer, type: MessageType): boolean {
|
||||
if (message.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (message.readUInt8(0)) {
|
||||
case 1: return type === MessageType.Initialized;
|
||||
case 2: return type === MessageType.Ready;
|
||||
case 3: return type === MessageType.Terminate;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as semver from 'semver';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
@@ -14,8 +14,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, ExtensionIdentifierWithVersion, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
const MANIFEST_FILE = 'package.json';
|
||||
|
||||
@@ -451,7 +450,7 @@ export class ExtensionScannerInput {
|
||||
|
||||
constructor(
|
||||
public readonly ourVersion: string,
|
||||
public readonly commit: string | undefined,
|
||||
public readonly commit: string | null | undefined,
|
||||
public readonly locale: string | undefined,
|
||||
public readonly devMode: boolean,
|
||||
public readonly absoluteFolderPath: string,
|
||||
|
||||
14
src/vs/workbench/services/extensions/node/extensionsUtil.ts
Normal file
14
src/vs/workbench/services/extensions/node/extensionsUtil.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { isUIExtension as _isUIExtension } from 'vs/platform/extensions/node/extensionsUtil';
|
||||
|
||||
export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean {
|
||||
const uiExtensionPoints = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name);
|
||||
return _isUIExtension(manifest, uiExtensionPoints, configurationService);
|
||||
}
|
||||
@@ -5,10 +5,15 @@
|
||||
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as tls from 'tls';
|
||||
import * as nodeurl from 'url';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import { ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import { ProxyAgent } from 'vscode-proxy-agent';
|
||||
import { MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol';
|
||||
@@ -16,6 +21,7 @@ import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { promisify } from 'util';
|
||||
|
||||
interface ConnectionResult {
|
||||
proxy: string;
|
||||
@@ -25,34 +31,37 @@ interface ConnectionResult {
|
||||
}
|
||||
|
||||
export function connectProxyResolver(
|
||||
extHostWorkspace: ExtHostWorkspace,
|
||||
extHostWorkspace: IExtHostWorkspaceProvider,
|
||||
configProvider: ExtHostConfigProvider,
|
||||
extensionService: ExtHostExtensionService,
|
||||
extHostLogService: ExtHostLogService,
|
||||
mainThreadTelemetry: MainThreadTelemetryShape
|
||||
) {
|
||||
const agents = createProxyAgents(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry);
|
||||
const lookup = createPatchedModules(configProvider, agents);
|
||||
const resolveProxy = setupProxyResolution(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry);
|
||||
const lookup = createPatchedModules(configProvider, resolveProxy);
|
||||
return configureModuleLoading(extensionService, lookup);
|
||||
}
|
||||
|
||||
const maxCacheEntries = 5000; // Cache can grow twice that much due to 'oldCache'.
|
||||
|
||||
function createProxyAgents(
|
||||
extHostWorkspace: ExtHostWorkspace,
|
||||
function setupProxyResolution(
|
||||
extHostWorkspace: IExtHostWorkspaceProvider,
|
||||
configProvider: ExtHostConfigProvider,
|
||||
extHostLogService: ExtHostLogService,
|
||||
mainThreadTelemetry: MainThreadTelemetryShape
|
||||
) {
|
||||
const env = process.env;
|
||||
|
||||
let settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http')
|
||||
.get<string>('proxy'));
|
||||
configProvider.onDidChangeConfiguration(e => {
|
||||
settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http')
|
||||
.get<string>('proxy'));
|
||||
});
|
||||
const env = process.env;
|
||||
let envProxy = proxyFromConfigURL(env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY); // Not standardized.
|
||||
|
||||
let envNoProxy = noProxyFromEnv(env.no_proxy || env.NO_PROXY); // Not standardized.
|
||||
|
||||
let cacheRolls = 0;
|
||||
let oldCache = new Map<string, string>();
|
||||
let cache = new Map<string, string>();
|
||||
@@ -90,6 +99,7 @@ function createProxyAgents(
|
||||
let envCount = 0;
|
||||
let settingsCount = 0;
|
||||
let localhostCount = 0;
|
||||
let envNoProxyCount = 0;
|
||||
let results: ConnectionResult[] = [];
|
||||
function logEvent() {
|
||||
timeout = undefined;
|
||||
@@ -104,19 +114,32 @@ function createProxyAgents(
|
||||
"envCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"envNoProxyCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"results": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
}
|
||||
*/
|
||||
mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, results });
|
||||
count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = 0;
|
||||
mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, envNoProxyCount, results });
|
||||
count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = envNoProxyCount = 0;
|
||||
results = [];
|
||||
}
|
||||
|
||||
function resolveProxy(req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) {
|
||||
function resolveProxy(flags: { useProxySettings: boolean, useSystemCertificates: boolean }, req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) {
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(logEvent, 10 * 60 * 1000);
|
||||
}
|
||||
|
||||
useSystemCertificates(extHostLogService, flags.useSystemCertificates, opts, () => {
|
||||
useProxySettings(flags.useProxySettings, req, opts, url, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function useProxySettings(useProxySettings: boolean, req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) {
|
||||
|
||||
if (!useProxySettings) {
|
||||
callback('DIRECT');
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedUrl = nodeurl.parse(url); // Coming from Node's URL, sticking with that.
|
||||
|
||||
const hostname = parsedUrl.hostname;
|
||||
@@ -127,6 +150,13 @@ function createProxyAgents(
|
||||
return;
|
||||
}
|
||||
|
||||
if (envNoProxy(hostname, String(parsedUrl.port || (<any>opts.agent).defaultPort))) {
|
||||
envNoProxyCount++;
|
||||
callback('DIRECT');
|
||||
extHostLogService.trace('ProxyResolver#resolveProxy envNoProxy', url, 'DIRECT');
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingsProxy) {
|
||||
settingsCount++;
|
||||
callback(settingsProxy);
|
||||
@@ -168,11 +198,7 @@ function createProxyAgents(
|
||||
});
|
||||
}
|
||||
|
||||
const httpAgent: http.Agent = new ProxyAgent({ resolveProxy });
|
||||
(<any>httpAgent).defaultPort = 80;
|
||||
const httpsAgent: http.Agent = new ProxyAgent({ resolveProxy });
|
||||
(<any>httpsAgent).defaultPort = 443;
|
||||
return { http: httpAgent, https: httpsAgent };
|
||||
return resolveProxy;
|
||||
}
|
||||
|
||||
function collectResult(results: ConnectionResult[], resolveProxy: string, connection: string, req: http.ClientRequest) {
|
||||
@@ -189,7 +215,7 @@ function collectResult(results: ConnectionResult[], resolveProxy: string, connec
|
||||
});
|
||||
}
|
||||
|
||||
function findOrCreateResult(results: ConnectionResult[], proxy: string, connection: string, code: string): ConnectionResult | undefined {
|
||||
function findOrCreateResult(results: ConnectionResult[], proxy: string, connection: string, code: string): ConnectionResult {
|
||||
for (const result of results) {
|
||||
if (result.proxy === proxy && result.connection === connection && result.code === code) {
|
||||
return result;
|
||||
@@ -200,7 +226,7 @@ function findOrCreateResult(results: ConnectionResult[], proxy: string, connecti
|
||||
return result;
|
||||
}
|
||||
|
||||
function proxyFromConfigURL(configURL: string) {
|
||||
function proxyFromConfigURL(configURL: string | undefined) {
|
||||
const url = (configURL || '').trim();
|
||||
const i = url.indexOf('://');
|
||||
if (i === -1) {
|
||||
@@ -218,35 +244,70 @@ function proxyFromConfigURL(configURL: string) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function createPatchedModules(configProvider: ExtHostConfigProvider, agents: { http: http.Agent; https: http.Agent; }) {
|
||||
const setting = {
|
||||
function noProxyFromEnv(envValue?: string) {
|
||||
const value = (envValue || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
if (value === '*') {
|
||||
return () => true;
|
||||
}
|
||||
|
||||
const filters = value
|
||||
.split(',')
|
||||
.map(s => s.trim().split(':', 2))
|
||||
.map(([name, port]) => ({ name, port }))
|
||||
.filter(filter => !!filter.name)
|
||||
.map(({ name, port }) => {
|
||||
const domain = name[0] === '.' ? name : `.${name}`;
|
||||
return { domain, port };
|
||||
});
|
||||
if (!filters.length) {
|
||||
return () => false;
|
||||
}
|
||||
return (hostname: string, port: string) => filters.some(({ domain, port: filterPort }) => {
|
||||
return endsWith(`.${hostname.toLowerCase()}`, domain) && (!filterPort || port === filterPort);
|
||||
});
|
||||
}
|
||||
|
||||
function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProxy: ReturnType<typeof setupProxyResolution>) {
|
||||
const proxySetting = {
|
||||
config: configProvider.getConfiguration('http')
|
||||
.get<string>('proxySupport') || 'off'
|
||||
};
|
||||
configProvider.onDidChangeConfiguration(e => {
|
||||
setting.config = configProvider.getConfiguration('http')
|
||||
proxySetting.config = configProvider.getConfiguration('http')
|
||||
.get<string>('proxySupport') || 'off';
|
||||
});
|
||||
const certSetting = {
|
||||
config: !!configProvider.getConfiguration('http')
|
||||
.get<boolean>('systemCertificates')
|
||||
};
|
||||
configProvider.onDidChangeConfiguration(e => {
|
||||
certSetting.config = !!configProvider.getConfiguration('http')
|
||||
.get<string>('systemCertificates');
|
||||
});
|
||||
|
||||
return {
|
||||
http: {
|
||||
off: assign({}, http, patches(http, agents.http, agents.https, { config: 'off' }, true)),
|
||||
on: assign({}, http, patches(http, agents.http, agents.https, { config: 'on' }, true)),
|
||||
override: assign({}, http, patches(http, agents.http, agents.https, { config: 'override' }, true)),
|
||||
onRequest: assign({}, http, patches(http, agents.http, agents.https, setting, true)),
|
||||
default: assign(http, patches(http, agents.http, agents.https, setting, false)) // run last
|
||||
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
|
||||
},
|
||||
https: {
|
||||
off: assign({}, https, patches(https, agents.https, agents.http, { config: 'off' }, true)),
|
||||
on: assign({}, https, patches(https, agents.https, agents.http, { config: 'on' }, true)),
|
||||
override: assign({}, https, patches(https, agents.https, agents.http, { config: 'override' }, true)),
|
||||
onRequest: assign({}, https, patches(https, agents.https, agents.http, setting, true)),
|
||||
default: assign(https, patches(https, agents.https, agents.http, setting, false)) // run last
|
||||
}
|
||||
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
|
||||
},
|
||||
tls: assign(tls, tlsPatches(tls))
|
||||
};
|
||||
}
|
||||
|
||||
function patches(originals: typeof http | typeof https, agent: http.Agent, otherAgent: http.Agent, setting: { config: string; }, onRequest: boolean) {
|
||||
function patches(originals: typeof http | typeof https, resolveProxy: ReturnType<typeof setupProxyResolution>, proxySetting: { config: string }, certSetting: { config: boolean }, onRequest: boolean) {
|
||||
return {
|
||||
get: patch(originals.get),
|
||||
request: patch(originals.request)
|
||||
@@ -265,12 +326,15 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, other
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
const config = onRequest && ((<any>options)._vscodeProxySupport || /* LS */ (<any>options)._vscodeSystemProxy) || setting.config;
|
||||
if (config === 'off') {
|
||||
if (options.socketPath) {
|
||||
return original.apply(null, arguments as unknown as any[]);
|
||||
}
|
||||
|
||||
if (!options.socketPath && (config === 'override' || config === 'on' && !options.agent) && options.agent !== agent && options.agent !== otherAgent) {
|
||||
const config = onRequest && ((<any>options)._vscodeProxySupport || /* LS */ (<any>options)._vscodeSystemProxy) || proxySetting.config;
|
||||
const useProxySettings = (config === 'override' || config === 'on' && !options.agent) && !(options.agent instanceof ProxyAgent);
|
||||
const useSystemCertificates = certSetting.config && originals === https && !(options as https.RequestOptions).ca;
|
||||
|
||||
if (useProxySettings || useSystemCertificates) {
|
||||
if (url) {
|
||||
const parsed = typeof url === 'string' ? new nodeurl.URL(url) : url;
|
||||
const urlOptions = {
|
||||
@@ -286,7 +350,11 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, other
|
||||
} else {
|
||||
options = { ...options };
|
||||
}
|
||||
options.agent = agent;
|
||||
options.agent = new ProxyAgent({
|
||||
resolveProxy: resolveProxy.bind(undefined, { useProxySettings, useSystemCertificates }),
|
||||
defaultPort: originals === https ? 443 : 80,
|
||||
originalAgent: options.agent
|
||||
});
|
||||
return original(options, callback);
|
||||
}
|
||||
|
||||
@@ -296,12 +364,35 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, other
|
||||
}
|
||||
}
|
||||
|
||||
function tlsPatches(originals: typeof tls) {
|
||||
return {
|
||||
createSecureContext: patch(originals.createSecureContext)
|
||||
};
|
||||
|
||||
function patch(original: typeof tls.createSecureContext): typeof tls.createSecureContext {
|
||||
return function (details: tls.SecureContextOptions): ReturnType<typeof tls.createSecureContext> {
|
||||
const context = original.apply(null, arguments as unknown as any[]);
|
||||
const certs = (details as any)._vscodeAdditionalCaCerts;
|
||||
if (certs) {
|
||||
for (const cert of certs) {
|
||||
context.context.addCACert(cert);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function configureModuleLoading(extensionService: ExtHostExtensionService, lookup: ReturnType<typeof createPatchedModules>): Promise<void> {
|
||||
return extensionService.getExtensionPathIndex()
|
||||
.then(extensionPaths => {
|
||||
const node_module = <any>require.__$__nodeRequire('module');
|
||||
const original = node_module._load;
|
||||
node_module._load = function load(request: string, parent: any, isMain: any) {
|
||||
if (request === 'tls') {
|
||||
return lookup.tls;
|
||||
}
|
||||
|
||||
if (request !== 'http' && request !== 'https') {
|
||||
return original.apply(this, arguments);
|
||||
}
|
||||
@@ -314,4 +405,120 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku
|
||||
return modules.default;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function useSystemCertificates(extHostLogService: ExtHostLogService, useSystemCertificates: boolean, opts: http.RequestOptions, callback: () => void) {
|
||||
if (useSystemCertificates) {
|
||||
getCaCertificates(extHostLogService)
|
||||
.then(caCertificates => {
|
||||
if (caCertificates) {
|
||||
if (caCertificates.append) {
|
||||
(opts as any)._vscodeAdditionalCaCerts = caCertificates.certs;
|
||||
} else {
|
||||
(opts as https.RequestOptions).ca = caCertificates.certs;
|
||||
}
|
||||
}
|
||||
callback();
|
||||
})
|
||||
.catch(err => {
|
||||
extHostLogService.error('ProxyResolver#useSystemCertificates', toErrorMessage(err));
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
let _caCertificates: ReturnType<typeof readCaCertificates> | Promise<undefined>;
|
||||
async function getCaCertificates(extHostLogService: ExtHostLogService) {
|
||||
if (!_caCertificates) {
|
||||
_caCertificates = readCaCertificates()
|
||||
.then(res => res && res.certs.length ? res : undefined)
|
||||
.catch(err => {
|
||||
extHostLogService.error('ProxyResolver#getCertificates', toErrorMessage(err));
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return _caCertificates;
|
||||
}
|
||||
|
||||
async function readCaCertificates() {
|
||||
if (process.platform === 'win32') {
|
||||
return readWindowsCaCertificates();
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
return readMacCaCertificates();
|
||||
}
|
||||
if (process.platform === 'linux') {
|
||||
return readLinuxCaCertificates();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readWindowsCaCertificates() {
|
||||
const winCA = require.__$__nodeRequire<any>('win-ca-lib');
|
||||
|
||||
let ders = [];
|
||||
const store = winCA();
|
||||
try {
|
||||
let der;
|
||||
while (der = store.next()) {
|
||||
ders.push(der);
|
||||
}
|
||||
} finally {
|
||||
store.done();
|
||||
}
|
||||
|
||||
const seen = {};
|
||||
const certs = ders.map(derToPem)
|
||||
.filter(pem => !seen[pem] && (seen[pem] = true));
|
||||
return {
|
||||
certs,
|
||||
append: true
|
||||
};
|
||||
}
|
||||
|
||||
async function readMacCaCertificates() {
|
||||
const stdout = (await promisify(cp.execFile)('/usr/bin/security', ['find-certificate', '-a', '-p'], { encoding: 'utf8' })).stdout;
|
||||
const seen = {};
|
||||
const certs = stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g)
|
||||
.filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true));
|
||||
return {
|
||||
certs,
|
||||
append: true
|
||||
};
|
||||
}
|
||||
|
||||
const linuxCaCertificatePaths = [
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
'/etc/ssl/certs/ca-bundle.crt',
|
||||
];
|
||||
|
||||
async function readLinuxCaCertificates() {
|
||||
for (const certPath of linuxCaCertificatePaths) {
|
||||
try {
|
||||
const content = await promisify(fs.readFile)(certPath, { encoding: 'utf8' });
|
||||
const seen = {};
|
||||
const certs = content.split(/(?=-----BEGIN CERTIFICATE-----)/g)
|
||||
.filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true));
|
||||
return {
|
||||
certs,
|
||||
append: false
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function derToPem(blob) {
|
||||
const lines = ['-----BEGIN CERTIFICATE-----'];
|
||||
const der = blob.toString('base64');
|
||||
for (let i = 0; i < der.length; i += 64) {
|
||||
lines.push(der.substr(i, 64));
|
||||
}
|
||||
lines.push('-----END CERTIFICATE-----', '');
|
||||
return lines.join(os.EOL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user