Archived
1
0

Update to VS Code 1.52.1

This commit is contained in:
Asher
2021-02-09 16:08:37 +00:00
1351 changed files with 56560 additions and 38990 deletions

View File

@ -33,7 +33,14 @@
self.require = {
baseUrl: `${window.location.origin}/static/out`,
recordStats: true,
createTrustedScriptURL: value => value,
trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', {
createScriptURL(value) {
if(value.startsWith(window.location.origin)) {
return value;
}
throw new Error(`Invalid script url: ${value}`)
}
}),
paths: {
'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`,
'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`,

View File

@ -32,7 +32,14 @@
self.require = {
baseUrl: `${window.location.origin}/static/out`,
recordStats: true,
createTrustedScriptURL: value => value,
trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', {
createScriptURL(value) {
if(value.startsWith(window.location.origin)) {
return value;
}
throw new Error(`Invalid script url: ${value}`)
}
}),
paths: {
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`,

View File

@ -17,7 +17,11 @@ import { isStandalone } from 'vs/base/browser/browser';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
import product from 'vs/platform/product/common/product';
<<<<<<< HEAD
import { encodePath } from 'vs/server/node/util';
=======
import { parseLogLevel } from 'vs/platform/log/common/log';
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
function doCreateUri(path: string, queryValues: Map<string, string>): URI {
let query: string | undefined = undefined;
@ -438,7 +442,50 @@ class WindowIndicator implements IWindowIndicator {
// Find workspace to open and payload
let foundWorkspace = false;
let workspace: IWorkspace;
<<<<<<< HEAD
let payload = config.workspaceProvider?.payload || Object.create(null);
=======
let payload = Object.create(null);
let logLevel: string | undefined = undefined;
const query = new URL(document.location.href).searchParams;
query.forEach((value, key) => {
switch (key) {
// Folder
case WorkspaceProvider.QUERY_PARAM_FOLDER:
workspace = { folderUri: URI.parse(value) };
foundWorkspace = true;
break;
// Workspace
case WorkspaceProvider.QUERY_PARAM_WORKSPACE:
workspace = { workspaceUri: URI.parse(value) };
foundWorkspace = true;
break;
// Empty
case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW:
workspace = undefined;
foundWorkspace = true;
break;
// Payload
case WorkspaceProvider.QUERY_PARAM_PAYLOAD:
try {
payload = JSON.parse(value);
} catch (error) {
console.error(error); // possible invalid JSON
}
break;
// Log level
case 'logLevel':
logLevel = value;
break;
}
});
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
// If no workspace is provided through the URL, check for config attribute from server
if (!foundWorkspace) {
@ -496,6 +543,7 @@ class WindowIndicator implements IWindowIndicator {
// Finally create workbench
create(document.body, {
...config,
logLevel: logLevel ? parseLogLevel(logLevel) : undefined,
settingsSyncOptions,
windowIndicator,
productQualityChangeHandler,

View File

@ -51,7 +51,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel, StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
@ -63,7 +63,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen
import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService';
import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService';
import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
@ -72,6 +71,7 @@ import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker';
import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender';
import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions';
import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@ -151,7 +151,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IStorageService, storageService);
disposables.add(toDisposable(() => storageService.flush()));
services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService));
services.set(IEnvironmentService, environmentService);
services.set(INativeEnvironmentService, environmentService);
@ -188,7 +187,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
appender: telemetryAppender,
commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath),
sendErrorTelemetry: true,
piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot]
piiPaths: [appRoot, extensionsPath]
};
telemetryService = new TelemetryService(config, configurationService);
@ -199,10 +198,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
}
server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(telemetryAppender));
const storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService);
const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService);
server.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel);
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
@ -214,6 +209,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService));
services.set(IExtensionsStorageSyncService, new SyncDescriptor(ExtensionsStorageSyncService));
services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService));

View File

@ -9,15 +9,12 @@
'use strict';
(function () {
const bootstrapWindow = bootstrapWindowLib();
// Add a perf entry right from the top
const perf = perfLib();
const perf = bootstrapWindow.perfLib();
perf.mark('renderer/started');
// Load environment in parallel to workbench loading to avoid waterfall
const bootstrapWindow = bootstrapWindowLib();
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved();
// Load workbench main JS, CSS and NLS all in parallel. This is an
// optimization to prevent a waterfall of loading to happen, because
// we know for a fact that workbench.desktop.main will depend on
@ -31,12 +28,6 @@
// Mark start of workbench
perf.mark('didLoadWorkbenchMain');
performance.mark('workbench-start');
// Wait for process environment being fully resolved
await whenEnvResolved;
perf.mark('main/startup');
// @ts-ignore
return require('vs/workbench/electron-browser/desktop.main').main(configuration);
@ -58,23 +49,11 @@
//region Helpers
function perfLib() {
globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || [];
return {
/**
* @param {string} name
*/
mark(name) {
globalThis.MonacoPerformanceMarks.push(name, Date.now());
}
};
}
/**
* @returns {{
* load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown,
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals')
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'),
* perfLib: () => { mark: (name: string) => void }
* }}
*/
function bootstrapWindowLib() {
@ -187,5 +166,5 @@
}
//#endregion
}());

View File

@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { app, ipcMain, systemPreferences, contentTracing, protocol, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { OpenContext } from 'vs/platform/windows/node/window';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
import { resolveShellEnv } from 'vs/code/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc';
import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main';
@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IStateService } from 'vs/platform/state/node/state';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IURLService } from 'vs/platform/url/common/url';
import { IOpenURLOptions, IURLService } from 'vs/platform/url/common/url';
import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc';
import { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
@ -35,6 +35,7 @@ import { getDelayedChannel, StaticRouter, createChannelReceiver, createChannelSe
import product from 'vs/platform/product/common/product';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { ProxyAuthHandler2 } from 'vs/code/electron-main/auth2';
import { FileProtocolHandler } from 'vs/code/electron-main/protocol';
import { Disposable } from 'vs/base/common/lifecycle';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { URI } from 'vs/base/common/uri';
@ -52,7 +53,7 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { sep, posix } from 'vs/base/common/path';
import { sep, posix, join, isAbsolute } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
@ -72,7 +73,6 @@ import { INativeHostMainService, NativeHostMainService } from 'vs/platform/nativ
import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService';
import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
import { coalesce } from 'vs/base/common/arrays';
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService';
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
@ -82,6 +82,14 @@ import { generateUuid } from 'vs/base/common/uuid';
import { VSBuffer } from 'vs/base/common/buffer';
import { EncryptionMainService, IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService';
import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker';
import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { DisplayMainService, IDisplayMainService } from 'vs/platform/display/electron-main/displayMainService';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { isEqualOrParent } from 'vs/base/common/extpath';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService';
export class CodeApplication extends Disposable {
private windowsMainService: IWindowsMainService | undefined;
@ -96,7 +104,8 @@ export class CodeApplication extends Disposable {
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStateService private readonly stateService: IStateService
@IStateService private readonly stateService: IStateService,
@IFileService private readonly fileService: IFileService
) {
super();
@ -118,9 +127,7 @@ export class CodeApplication extends Disposable {
// Accessibility change event
app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
}
this.windowsMainService?.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
});
// macOS dock activate
@ -128,8 +135,8 @@ export class CodeApplication extends Disposable {
this.logService.trace('app#activate');
// Mac only event: open new window when we get activated
if (!hasVisibleWindows && this.windowsMainService) {
this.windowsMainService.openEmptyWindow({ context: OpenContext.DOCK });
if (!hasVisibleWindows) {
this.windowsMainService?.openEmptyWindow({ context: OpenContext.DOCK });
}
});
@ -214,9 +221,7 @@ export class CodeApplication extends Disposable {
contents.on('new-window', (event, url) => {
event.preventDefault(); // prevent code that wants to open links
if (this.nativeHostMainService) {
this.nativeHostMainService.openExternal(undefined, url);
}
this.nativeHostMainService?.openExternal(undefined, url);
});
session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => {
@ -247,62 +252,119 @@ export class CodeApplication extends Disposable {
// Handle paths delayed in case more are coming!
runningTimeout = setTimeout(() => {
if (this.windowsMainService) {
this.windowsMainService.open({
context: OpenContext.DOCK /* can also be opening from finder while app is running */,
cli: this.environmentService.args,
urisToOpen: macOpenFileURIs,
gotoLineMode: false,
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
});
this.windowsMainService?.open({
context: OpenContext.DOCK /* can also be opening from finder while app is running */,
cli: this.environmentService.args,
urisToOpen: macOpenFileURIs,
gotoLineMode: false,
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
});
macOpenFileURIs = [];
runningTimeout = null;
}
macOpenFileURIs = [];
runningTimeout = null;
}, 100);
});
app.on('new-window-for-tab', () => {
if (this.windowsMainService) {
this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
}
this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
});
ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => {
//#region Bootstrap IPC Handlers
ipcMain.on('vscode:fetchShellEnv', async event => {
const webContents = event.sender;
const window = this.windowsMainService?.getWindowByWebContents(event.sender);
try {
const shellEnv = await getShellEnvironment(this.logService, this.environmentService);
let replied = false;
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', shellEnv);
function acceptShellEnv(env: NodeJS.ProcessEnv): void {
clearTimeout(shellEnvSlowWarningHandle);
clearTimeout(shellEnvTimeoutErrorHandle);
if (!replied) {
replied = true;
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', env);
}
}
} catch (error) {
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', {});
}
this.logService.error('Error fetching shell env', error);
}
// Handle slow shell environment resolve calls:
// - a warning after 3s but continue to resolve
// - an error after 10s and stop trying to resolve
const cts = new CancellationTokenSource();
const shellEnvSlowWarningHandle = setTimeout(() => window?.sendWhenReady('vscode:showShellEnvSlowWarning', cts.token), 3000);
const shellEnvTimeoutErrorHandle = setTimeout(function () {
cts.dispose(true);
window?.sendWhenReady('vscode:showShellEnvTimeoutError', CancellationToken.None);
acceptShellEnv({});
}, 10000);
// Prefer to use the args and env from the target window
// when resolving the shell env. It is possible that
// a first window was opened from the UI but a second
// from the CLI and that has implications for wether to
// resolve the shell environment or not.
let args: NativeParsedArgs;
let env: NodeJS.ProcessEnv;
if (window?.config) {
args = window.config;
env = { ...process.env, ...window.config.userEnv };
} else {
args = this.environmentService.args;
env = process.env;
}
// Resolve shell env
const shellEnv = await resolveShellEnv(this.logService, args, env);
acceptShellEnv(shellEnv);
});
ipc.on('vscode:toggleDevTools', (event: IpcMainEvent) => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', (event: IpcMainEvent) => event.sender.openDevTools());
ipcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => {
const uri = this.validateNlsPath([path]);
if (!uri || typeof data !== 'string') {
return Promise.reject('Invalid operation (vscode:writeNlsFile)');
}
ipc.on('vscode:reloadWindow', (event: IpcMainEvent) => event.sender.reload());
return this.fileService.writeFile(uri, VSBuffer.fromString(data));
});
// Some listeners after window opened
(async () => {
await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen);
ipcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => {
const uri = this.validateNlsPath(paths);
if (!uri) {
return Promise.reject('Invalid operation (vscode:readNlsFile)');
}
// Keyboard layout changes (after window opened)
const nativeKeymap = await import('native-keymap');
nativeKeymap.onDidChangeKeyboardLayout(() => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged');
return (await this.fileService.readFile(uri)).value.toString();
});
ipcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools());
ipcMain.on('vscode:openDevTools', event => event.sender.openDevTools());
ipcMain.on('vscode:reloadWindow', event => event.sender.reload());
//#endregion
}
private validateNlsPath(pathSegments: unknown[]): URI | undefined {
let path: string | undefined = undefined;
for (const pathSegment of pathSegments) {
if (typeof pathSegment === 'string') {
if (typeof path !== 'string') {
path = pathSegment;
} else {
path = join(path, pathSegment);
}
});
})();
}
}
if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) {
return undefined;
}
return URI.file(path);
}
private onUnexpectedError(err: Error): void {
@ -315,9 +377,7 @@ export class CodeApplication extends Disposable {
};
// handle on client side
if (this.windowsMainService) {
this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError));
}
this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError));
}
this.logService.error(`[uncaught exception in main]: ${err}`);
@ -354,6 +414,9 @@ export class CodeApplication extends Disposable {
this.logService.error(error);
}
// Setup Protocol Handler
const fileProtocolHandler = this._register(this.instantiationService.createInstance(FileProtocolHandler));
// Create Electron IPC Server
const electronIpcServer = new ElectronIPCServer();
@ -376,7 +439,7 @@ export class CodeApplication extends Disposable {
});
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService));
sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env));
}, 3000)).schedule();
});
@ -391,15 +454,15 @@ export class CodeApplication extends Disposable {
this._register(server);
}
// Setup Auth Handler
if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') !== true) {
// Setup Auth Handler (TODO@ben remove old auth handler eventually)
if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') === false) {
this._register(new ProxyAuthHandler());
} else {
this._register(appInstantiationService.createInstance(ProxyAuthHandler2));
}
// Open Windows
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient, fileProtocolHandler));
// Post Open Windows Tasks
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
@ -453,10 +516,13 @@ export class CodeApplication extends Disposable {
services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId]));
services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService));
services.set(IDisplayMainService, new SyncDescriptor(DisplayMainService));
services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService));
services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService));
services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService));
services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService));
services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService));
const storageMainService = new StorageMainService(this.logService, this.environmentService);
services.set(IStorageMainService, storageMainService);
@ -474,7 +540,7 @@ export class CodeApplication extends Disposable {
const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender')));
const appender = new TelemetryAppenderClient(channel);
const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath);
const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot];
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true };
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
@ -502,14 +568,12 @@ export class CodeApplication extends Disposable {
const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath);
if (!timeout) {
if (this.dialogMainService) {
this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "OK")]
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
}
this.dialogMainService?.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "OK")]
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
}
@ -525,7 +589,7 @@ export class CodeApplication extends Disposable {
});
}
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>): ICodeWindow[] {
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>, fileProtocolHandler: FileProtocolHandler): ICodeWindow[] {
// Register more Main IPC services
const launchMainService = accessor.get(ILaunchMainService);
@ -545,6 +609,14 @@ export class CodeApplication extends Disposable {
const encryptionChannel = createChannelReceiver(encryptionMainService);
electronIpcServer.registerChannel('encryption', encryptionChannel);
const keyboardLayoutMainService = accessor.get(IKeyboardLayoutMainService);
const keyboardLayoutChannel = createChannelReceiver(keyboardLayoutMainService);
electronIpcServer.registerChannel('keyboardLayout', keyboardLayoutChannel);
const displayMainService = accessor.get(IDisplayMainService);
const displayChannel = createChannelReceiver(displayMainService);
electronIpcServer.registerChannel('display', displayChannel);
const nativeHostMainService = this.nativeHostMainService = accessor.get(INativeHostMainService);
const nativeHostChannel = createChannelReceiver(this.nativeHostMainService);
electronIpcServer.registerChannel('nativeHost', nativeHostChannel);
@ -566,6 +638,10 @@ export class CodeApplication extends Disposable {
const urlChannel = createChannelReceiver(urlService);
electronIpcServer.registerChannel('url', urlChannel);
const extensionUrlTrustService = accessor.get(IExtensionUrlTrustService);
const extensionUrlTrustChannel = createChannelReceiver(extensionUrlTrustService);
electronIpcServer.registerChannel('extensionUrlTrust', extensionUrlTrustChannel);
const webviewManagerService = accessor.get(IWebviewManagerService);
const webviewChannel = createChannelReceiver(webviewManagerService);
electronIpcServer.registerChannel('webview', webviewChannel);
@ -579,8 +655,10 @@ export class CodeApplication extends Disposable {
electronIpcServer.registerChannel('logger', loggerChannel);
sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));
// ExtensionHost Debug broadcast service
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
fileProtocolHandler.injectWindowsMainService(windowsMainService);
// ExtensionHost Debug broadcast service
electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ElectronExtensionHostDebugBroadcastChannel(windowsMainService));
// Signal phase: ready (services set)
@ -591,29 +669,32 @@ export class CodeApplication extends Disposable {
// Check for initial URLs to handle from protocol link invocations
const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = [];
const pendingProtocolLinksToHandle = coalesce([
const pendingProtocolLinksToHandle = [
// Windows/Linux: protocol handler invokes CLI with --open-url
...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [],
// macOS: open-url events
...((<any>global).getOpenUrls() || []) as string[]
].map(pendingUrlToHandle => {
].map(url => {
try {
return URI.parse(pendingUrlToHandle);
} catch (error) {
return undefined;
return { uri: URI.parse(url), url };
} catch {
return null;
}
})).filter(pendingUriToHandle => {
// if URI should be blocked, filter it out
if (this.shouldBlockURI(pendingUriToHandle)) {
}).filter((obj): obj is { uri: URI, url: string } => {
if (!obj) {
return false;
}
// filter out any protocol link that wants to open as window so that
// If URI should be blocked, filter it out
if (this.shouldBlockURI(obj.uri)) {
return false;
}
// Filter out any protocol link that wants to open as window so that
// we open the right set of windows on startup and not restore the
// previous workspace too.
const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle);
const windowOpenable = this.getWindowOpenableFromProtocolLink(obj.uri);
if (windowOpenable) {
pendingWindowOpenablesFromProtocolLinks.push(windowOpenable);
@ -627,8 +708,9 @@ export class CodeApplication extends Disposable {
const app = this;
const environmentService = this.environmentService;
urlService.registerHandler({
async handleURL(uri: URI): Promise<boolean> {
// if URI should be blocked, behave as if it's handled
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
// If URI should be blocked, behave as if it's handled
if (app.shouldBlockURI(uri)) {
return true;
}
@ -658,7 +740,7 @@ export class CodeApplication extends Disposable {
await window.ready();
return urlService.open(uri);
return urlService.open(uri, options);
}
return false;
@ -666,11 +748,11 @@ export class CodeApplication extends Disposable {
});
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager({
const activeWindowManager = this._register(new ActiveWindowManager({
onDidOpenWindow: nativeHostMainService.onDidOpenWindow,
onDidFocusWindow: nativeHostMainService.onDidFocusWindow,
getActiveWindowId: () => nativeHostMainService.getActiveWindowId(-1)
});
}));
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter);
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter);
@ -682,7 +764,7 @@ export class CodeApplication extends Disposable {
// Open our first window
const args = this.environmentService.args;
const macOpenFiles: string[] = (<any>global).macOpenFiles;
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP;
const hasCliArgs = args._.length;
const hasFolderURIs = !!args['folder-uri'];
const hasFileURIs = !!args['file-uri'];
@ -828,12 +910,14 @@ export class CodeApplication extends Disposable {
updateService.initialize();
}
// Start to fetch shell environment (if needed) after window has opened
resolveShellEnv(this.logService, this.environmentService.args, process.env);
// If enable-crash-reporter argv is undefined then this is a fresh start,
// based on telemetry.enableCrashreporter settings, generate a UUID which
// will be used as crash reporter id and also update the json file.
try {
const fileService = accessor.get(IFileService);
const argvContent = await fileService.readFile(this.environmentService.argvResource);
const argvContent = await this.fileService.readFile(this.environmentService.argvResource);
const argvString = argvContent.value.toString();
const argvJSON = JSON.parse(stripComments(argvString));
if (argvJSON['enable-crash-reporter'] === undefined) {
@ -850,14 +934,11 @@ export class CodeApplication extends Disposable {
'}'
];
const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n'));
await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString));
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString));
}
} catch (error) {
this.logService.error(error);
}
// Start to fetch shell environment after window has opened
getShellEnvironment(this.logService, this.environmentService);
}
private handleRemoteAuthorities(): void {

View File

@ -13,6 +13,7 @@ import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeH
import { IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService';
import { generateUuid } from 'vs/base/common/uuid';
import product from 'vs/platform/product/common/product';
import { CancellationToken } from 'vs/base/common/cancellation';
interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDetails {
firstAuthAttempt?: boolean; // https://github.com/electron/electron/blob/84a42a050e7d45225e69df5bd2d2bf9f1037ea41/shell/browser/login_handler.cc#L70
@ -192,7 +193,7 @@ export class ProxyAuthHandler2 extends Disposable {
password: this.sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored
replyChannel: `vscode:proxyAuthResponse:${generateUuid()}`
};
window.sendWhenReady('vscode:openProxyAuthenticationDialog', payload);
window.sendWhenReady('vscode:openProxyAuthenticationDialog', CancellationToken.None, payload);
this.state = ProxyAuthState.LoginDialogShown;
// Handle reply

View File

@ -200,7 +200,7 @@ class CodeMain {
VSCODE_IPC_HOOK: environmentService.mainIPCHandle
};
['VSCODE_NLS_CONFIG', 'VSCODE_LOGS', 'VSCODE_PORTABLE'].forEach(key => {
['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => {
const value = process.env[key];
if (typeof value === 'string') {
instanceEnvironment[key] = value;
@ -343,15 +343,7 @@ class CodeMain {
private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void {
if (error.code === 'EACCES' || error.code === 'EPERM') {
const directories = [environmentService.userDataPath];
if (environmentService.extensionsPath) {
directories.push(environmentService.extensionsPath);
}
if (XDG_RUNTIME_DIR) {
directories.push(XDG_RUNTIME_DIR);
}
const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]);
this.showStartupWarningDialog(
localize('startupDataDirError', "Unable to write program user data."),

View File

@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { session } from 'electron';
import { ILogService } from 'vs/platform/log/common/log';
import { TernarySearchTree } from 'vs/base/common/map';
import { isLinux } from 'vs/base/common/platform';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void };
export class FileProtocolHandler extends Disposable {
private readonly validRoots = TernarySearchTree.forUris<boolean>(() => !isLinux);
constructor(
@INativeEnvironmentService environmentService: INativeEnvironmentService,
@ILogService private readonly logService: ILogService
) {
super();
const { defaultSession } = session;
// Define an initial set of roots we allow loading from
// - appRoot : all files installed as part of the app
// - extensions : all files shipped from extensions
this.validRoots.set(URI.file(environmentService.appRoot), true);
this.validRoots.set(URI.file(environmentService.extensionsPath), true);
// Register vscode-file:// handler
defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback));
// Block any file:// access (sandbox only)
if (environmentService.args.__sandbox) {
defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback));
}
// Cleanup
this._register(toDisposable(() => {
defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource);
if (environmentService.args.__sandbox) {
defaultSession.protocol.uninterceptProtocol(Schemas.file);
}
}));
}
injectWindowsMainService(windowsMainService: IWindowsMainService): void {
this._register(windowsMainService.onWindowReady(window => {
if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) {
const disposables = new DisposableStore();
disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose()));
// Allow access to extension development path
if (window.config.extensionDevelopmentPath) {
for (const extensionDevelopmentPath of window.config.extensionDevelopmentPath) {
disposables.add(this.addValidRoot(URI.file(extensionDevelopmentPath)));
}
}
// Allow access to extension tests path
if (window.config.extensionTestsPath) {
disposables.add(this.addValidRoot(URI.file(window.config.extensionTestsPath)));
}
}
}));
}
private addValidRoot(root: URI): IDisposable {
if (!this.validRoots.get(root)) {
this.validRoots.set(root, true);
return toDisposable(() => this.validRoots.delete(root));
}
return Disposable.None;
}
private async handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) {
const uri = URI.parse(request.url);
this.logService.error(`Refused to load resource ${uri.fsPath} from ${Schemas.file}: protocol`);
callback({ error: -3 /* ABORTED */ });
}
private async handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) {
const uri = URI.parse(request.url);
// Restore the `vscode-file` URI to a `file` URI so that we can
// ensure the root is valid and properly tell Chrome where the
// resource is at.
const fileUri = FileAccess.asFileUri(uri);
if (this.validRoots.findSubstr(fileUri)) {
return callback({
path: fileUri.fsPath
});
}
this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath}}`);
callback({ error: -3 /* ABORTED */ });
}
}

View File

@ -60,8 +60,9 @@ export class SharedProcess implements ISharedProcess {
windowId: this.window.id
};
const windowUrl = FileAccess
.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)
const windowUrl = (this.environmentService.sandbox ?
FileAccess._asCodeFileUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) :
FileAccess.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require))
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` });
this.window.loadURL(windowUrl.toString(true));

View File

@ -3,9 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as objects from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import * as perf from 'vs/base/common/performance';
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron';
@ -23,7 +24,6 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import * as perf from 'vs/base/common/performance';
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { RunOnceScheduler } from 'vs/base/common/async';
@ -33,8 +33,10 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IStorageMainService } from 'vs/platform/storage/node/storageMainService';
import { IFileService } from 'vs/platform/files/common/files';
import { ByteSize, IFileService } from 'vs/platform/files/common/files';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface IWindowCreationOptions {
state: IWindowState;
@ -84,7 +86,7 @@ const enum ReadyState {
export class CodeWindow extends Disposable implements ICodeWindow {
private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32
private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32
private readonly _onLoad = this._register(new Emitter<void>());
readonly onLoad = this._onLoad.event;
@ -110,8 +112,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[];
private pendingLoadConfig?: INativeWindowConfiguration;
private marketplaceHeadersPromise: Promise<object>;
private readonly touchBarGroups: TouchBarSegmentedControl[];
@ -150,7 +150,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below)
const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen);
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
const options: BrowserWindowConstructorOptions = {
width: this.windowState.width,
@ -211,7 +211,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs
}
const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom';
const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom';
if (useCustomTitleStyle) {
options.titleBarStyle = 'hidden';
this.hiddenTitleBarStyle = true;
@ -285,6 +285,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.registerListeners();
}
private pendingLoadConfig: INativeWindowConfiguration | undefined;
private currentConfig: INativeWindowConfiguration | undefined;
get config(): INativeWindowConfiguration | undefined { return this.currentConfig; }
@ -296,11 +298,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; }
get isExtensionDevelopmentHost(): boolean { return !!(this.config && this.config.extensionDevelopmentPath); }
get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); }
get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); }
get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); }
get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; }
get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; }
setRepresentedFilename(filename: string): void {
if (isMacintosh) {
@ -468,9 +470,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return; // disposed
}
// Notify renderers about displays changed
this.sendWhenReady('vscode:displayChanged');
// Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround
// we need to detect when display metrics change or displays are added/removed and toggle the
// fullscreen manually.
@ -520,11 +519,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Window Fullscreen
this._win.on('enter-full-screen', () => {
this.sendWhenReady('vscode:enterFullScreen');
this.sendWhenReady('vscode:enterFullScreen', CancellationToken.None);
});
this._win.on('leave-full-screen', () => {
this.sendWhenReady('vscode:leaveFullScreen');
this.sendWhenReady('vscode:leaveFullScreen', CancellationToken.None);
});
// Window Failed to load
@ -680,6 +679,16 @@ export class CodeWindow extends Disposable implements ICodeWindow {
load(config: INativeWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void {
// If this window was loaded before from the command line
// (as indicated by VSCODE_CLI environment), make sure to
// preserve that user environment in subsequent loads,
// unless the new configuration context was also a CLI
// (for https://github.com/microsoft/vscode/issues/108571)
const currentUserEnv = (this.currentConfig ?? this.pendingLoadConfig)?.userEnv;
if (currentUserEnv && isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(config.userEnv)) {
config.userEnv = { ...currentUserEnv, ...config.userEnv }; // still allow to override certain environment as passed in
}
// If this is the first time the window is loaded, we associate the paths
// directly with the window because we assume the loading will just work
if (this._readyState === ReadyState.NONE) {
@ -738,10 +747,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this._onLoad.fire();
}
reload(configurationIn?: INativeWindowConfiguration, cli?: NativeParsedArgs): void {
reload(cli?: NativeParsedArgs): void {
// If config is not provided, copy our current one
const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig);
// Copy our current config for reuse
const configuration = Object.assign({}, this.currentConfig);
// Delete some properties we do not want during reload
delete configuration.filesToOpenOrCreate;
@ -773,7 +782,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
windowConfiguration.logLevel = this.logService.getLevel();
// Set zoomlevel
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
const zoomLevel = windowConfig?.zoomLevel;
if (typeof zoomLevel === 'number') {
windowConfiguration.zoomLevel = zoomLevel;
@ -799,6 +808,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Parts splash
windowConfiguration.partsSplashPath = path.join(this.environmentService.userDataPath, 'rapid_render.json');
// OS Info
windowConfiguration.os = {
release: os.release()
};
// Config (combination of process.argv and window configuration)
const environment = parseArgs(process.argv, OPTIONS);
const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown };
@ -814,10 +828,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// large depending on user configuration, so we can only remove it in that case.
let configUrl = this.doGetUrl(config);
if (configUrl.length > CodeWindow.MAX_URL_LENGTH) {
delete config.userEnv;
this.logService.warn('Application URL exceeds maximum of 2MB and was shortened.');
configUrl = this.doGetUrl(config);
configUrl = this.doGetUrl({ ...config, userEnv: undefined });
if (configUrl.length > CodeWindow.MAX_URL_LENGTH) {
this.logService.error('Application URL exceeds maximum of 2MB and cannot be loaded.');
@ -835,10 +848,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
workbench = 'vs/code/electron-browser/workbench/workbench.html';
}
return FileAccess
.asBrowserUri(workbench, require)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true);
return (this.environmentService.sandbox ?
FileAccess._asCodeFileUri(workbench, require) :
FileAccess.asBrowserUri(workbench, require))
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }).toString(true);
}
serializeWindowState(): IWindowState {
@ -1088,7 +1101,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
// Events
this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen');
this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen', CancellationToken.None);
// Respect configured menu bar visibility or default to toggle if not set
if (this.currentMenuBarVisibility) {
@ -1116,7 +1129,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
private useNativeFullScreen(): boolean {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') {
return true; // default
}
@ -1133,7 +1146,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
private getMenuBarVisibility(): MenuBarVisibility {
let menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService, !!this.config?.extensionDevelopmentPath);
let menuBarVisibility = getMenuBarVisibility(this.configurationService);
if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) {
menuBarVisibility = 'default';
}
@ -1229,11 +1242,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
}
sendWhenReady(channel: string, ...args: any[]): void {
sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void {
if (this.isReady) {
this.send(channel, ...args);
} else {
this.ready().then(() => this.send(channel, ...args));
this.ready().then(() => {
if (!token.isCancellationRequested) {
this.send(channel, ...args);
}
});
}
}
@ -1283,7 +1300,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
mode: 'buttons',
segmentStyle: 'automatic',
change: (selectedIndex) => {
this.sendWhenReady('vscode:runAction', { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' });
this.sendWhenReady('vscode:runAction', CancellationToken.None, { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' });
}
});

View File

@ -150,6 +150,7 @@ export class IssueReporter extends Disposable {
applyZoom(configuration.data.zoomLevel);
this.applyStyles(configuration.data.styles);
this.handleExtensionData(configuration.data.enabledExtensions);
this.updateExperimentsInfo(configuration.data.experiments);
}
render(): void {
@ -285,7 +286,7 @@ export class IssueReporter extends Disposable {
this.render();
});
(['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => {
(['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments'] as const).forEach(elementId => {
this.addEventListener(elementId, 'click', (event: Event) => {
event.stopPropagation();
this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] });
@ -693,8 +694,7 @@ export class IssueReporter extends Disposable {
const processBlock = document.querySelector('.block-process');
const workspaceBlock = document.querySelector('.block-workspace');
const extensionsBlock = document.querySelector('.block-extensions');
const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions');
const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults');
const experimentsBlock = document.querySelector('.block-experiments');
const problemSource = this.getElementById('problem-source')!;
const descriptionTitle = this.getElementById('issue-description-label')!;
@ -707,8 +707,7 @@ export class IssueReporter extends Disposable {
hide(processBlock);
hide(workspaceBlock);
hide(extensionsBlock);
hide(searchedExtensionsBlock);
hide(settingsSearchResultsBlock);
hide(experimentsBlock);
hide(problemSource);
hide(extensionSelector);
@ -716,6 +715,7 @@ export class IssueReporter extends Disposable {
show(blockContainer);
show(systemBlock);
show(problemSource);
show(experimentsBlock);
if (fileOnExtension) {
show(extensionSelector);
@ -730,6 +730,7 @@ export class IssueReporter extends Disposable {
show(processBlock);
show(workspaceBlock);
show(problemSource);
show(experimentsBlock);
if (fileOnExtension) {
show(extensionSelector);
@ -1084,6 +1085,14 @@ export class IssueReporter extends Disposable {
}
}
private updateExperimentsInfo(experimentInfo: string | undefined) {
this.issueReporterModel.update({ experimentInfo });
const target = document.querySelector<HTMLElement>('.block-experiments .block-info');
if (target) {
target.textContent = experimentInfo ? experimentInfo : localize('noCurrentExperiments', "No current experiments.");
}
}
private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement {
return $('table', undefined,
$('tr', undefined,

View File

@ -19,8 +19,7 @@ export interface IssueReporterData {
includeWorkspaceInfo: boolean;
includeProcessInfo: boolean;
includeExtensions: boolean;
includeSearchedExtensions: boolean;
includeSettingsSearchDetails: boolean;
includeExperiments: boolean;
numberOfThemeExtesions?: number;
allExtensions: IssueReporterExtensionData[];
@ -31,6 +30,7 @@ export interface IssueReporterData {
actualSearchResults?: ISettingSearchResult[];
query?: string;
filterResultCount?: number;
experimentInfo?: string;
}
export class IssueReporterModel {
@ -43,8 +43,7 @@ export class IssueReporterModel {
includeWorkspaceInfo: true,
includeProcessInfo: true,
includeExtensions: true,
includeSearchedExtensions: true,
includeSettingsSearchDetails: true,
includeExperiments: true,
allExtensions: []
};
@ -133,6 +132,12 @@ ${this.getInfos()}
}
}
if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) {
if (this._data.includeExperiments && this._data.experimentInfo) {
info += this.generateExperimentsInfoMd();
}
}
return info;
}
@ -150,7 +155,7 @@ ${this.getInfos()}
|GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('<br>')}|
|Load (avg)|${this._data.systemInfo.load}|
|Memory (System)|${this._data.systemInfo.memory}|
|Process Argv|${this._data.systemInfo.processArgs}|
|Process Argv|${this._data.systemInfo.processArgs.replace(/\\/g, '\\\\')}|
|Screen Reader|${this._data.systemInfo.screenReader}|
|VM|${this._data.systemInfo.vmHint}|`;
@ -203,6 +208,18 @@ ${this._data.processInfo}
${this._data.workspaceInfo};
\`\`\`
</details>
`;
}
private generateExperimentsInfoMd(): string {
return `<details>
<summary>A/B Experiments</summary>
\`\`\`
${this._data.experimentInfo}
\`\`\`
</details>
`;
}

View File

@ -111,25 +111,15 @@ export default (): string => `
<!-- To be dynamically filled -->
</div>
</div>
<div class="block block-searchedExtensions">
<input class="sendData" type="checkbox" id="includeSearchedExtensions" checked/>
<label class="caption" for="includeSearchedExtensions">${escape(localize({
key: 'sendSearchedExtensions',
comment: ['{0} is either "show" or "hide" and is a button to toggle the visibility of the searched extensions']
}, "Send searched extensions ({0})")).replace('{0}', `<a href="#" class="showInfo">${escape(localize('show', "show"))}</a>`)}</label>
<div class="block-info hidden">
<div class="block block-experiments">
<input class="sendData" type="checkbox" id="includeExperiments" checked/>
<label class="caption" for="includeExperiments">${escape(localize({
key: 'sendExperiments',
comment: ['{0} is either "show" or "hide" and is a button to toggle the visibility of the current experiment information']
}, "Include A/B experiment info ({0})")).replace('{0}', `<a href="#" class="showInfo">${escape(localize('show', "show"))}</a>`)}</label>
<pre class="block-info hidden">
<!-- To be dynamically filled -->
</div>
</div>
<div class="block block-settingsSearchResults">
<input class="sendData" type="checkbox" id="includeSettingsSearchDetails" checked/>
<label class="caption" for="includeSettingsSearchDetails">${escape(localize({
key: 'sendSettingsSearchDetails',
comment: ['{0} is either "show" or "hide" and is a button to toggle the visibility of the search details']
}, "Send settings search details ({0})")).replace('{0}', `<a href="#" class="showInfo">${escape(localize('show', "show"))}</a>`)}</label>
<div class="block-info hidden">
<!-- To be dynamically filled -->
</div>
</pre>
</div>
</div>
</div>`;

View File

@ -18,8 +18,7 @@ suite('IssueReporter', () => {
includeWorkspaceInfo: true,
includeProcessInfo: true,
includeExtensions: true,
includeSearchedExtensions: true,
includeSettingsSearchDetails: true,
includeExperiments: true,
issueType: 0
});
});
@ -78,6 +77,58 @@ OS version: undefined
|Screen Reader|no|
|VM|0%|
</details>Extensions: none
<!-- generated by issue reporter -->`);
});
test('serializes experiment info when data is provided', () => {
const issueReporterModel = new IssueReporterModel({
issueType: 0,
systemInfo: {
os: 'Darwin',
cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)',
memory: '16.00GB',
vmHint: '0%',
processArgs: '',
screenReader: 'no',
remoteData: [],
gpuStatus: {
'2d_canvas': 'enabled',
'checker_imaging': 'disabled_off'
}
},
experimentInfo: 'vsliv695:30137379\nvsins829:30139715'
});
assert.equal(issueReporterModel.serialize(),
`
Issue Type: <b>Bug</b>
undefined
VS Code version: undefined
OS version: undefined
<details>
<summary>System Info</summary>
|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)|
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off|
|Load (avg)|undefined|
|Memory (System)|16.00GB|
|Process Argv||
|Screen Reader|no|
|VM|0%|
</details>Extensions: none<details>
<summary>A/B Experiments</summary>
\`\`\`
vsliv695:30137379
vsins829:30139715
\`\`\`
</details>
<!-- generated by issue reporter -->`);
});
@ -191,6 +242,45 @@ Remote OS version: Linux x64 4.18.0
<!-- generated by issue reporter -->`);
});
test('escapes backslashes in processArgs', () => {
const issueReporterModel = new IssueReporterModel({
issueType: 0,
systemInfo: {
os: 'Darwin',
cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)',
memory: '16.00GB',
vmHint: '0%',
processArgs: '\\\\HOST\\path',
screenReader: 'no',
remoteData: [],
gpuStatus: {}
}
});
assert.equal(issueReporterModel.serialize(),
`
Issue Type: <b>Bug</b>
undefined
VS Code version: undefined
OS version: undefined
<details>
<summary>System Info</summary>
|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)|
|GPU Status||
|Load (avg)|undefined|
|Memory (System)|16.00GB|
|Process Argv|\\\\\\\\HOST\\\\path|
|Screen Reader|no|
|VM|0%|
</details>Extensions: none
<!-- generated by issue reporter -->`);
});
test('should normalize GitHub urls', () => {
[
'https://github.com/repo',

View File

@ -19,6 +19,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
import { ByteSize } from 'vs/platform/files/common/files';
const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/;
const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/;
@ -67,18 +68,19 @@ class ProcessExplorer {
private getProcessList(rootProcess: ProcessItem, isLocal: boolean, totalMem: number): FormattedProcessItem[] {
const processes: FormattedProcessItem[] = [];
const handledProcesses = new Set<number>();
if (rootProcess) {
this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem);
this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem, handledProcesses);
}
return processes;
}
private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number): void {
private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number, handledProcesses: Set<number>): void {
const isRoot = (indent === 0);
const MB = 1024 * 1024;
handledProcesses.add(item.pid);
let name = item.name;
if (isRoot) {
@ -95,7 +97,7 @@ class ProcessExplorer {
const memory = this.data.platform === 'win32' ? item.mem : (totalMem * (item.mem / 100));
processes.push({
cpu: item.load,
memory: (memory / MB),
memory: (memory / ByteSize.MB),
pid: item.pid.toFixed(0),
name,
formattedName,
@ -105,9 +107,11 @@ class ProcessExplorer {
// Recurse into children if any
if (Array.isArray(item.children)) {
item.children.forEach(child => {
if (child) {
this.getProcessItem(processes, child, indent + 1, isLocal, totalMem);
if (!child || handledProcesses.has(child.pid)) {
return; // prevent loops
}
this.getProcessItem(processes, child, indent + 1, isLocal, totalMem, handledProcesses);
});
}
}

View File

@ -9,15 +9,12 @@
'use strict';
(function () {
const bootstrapWindow = bootstrapWindowLib();
// Add a perf entry right from the top
const perf = perfLib();
const perf = bootstrapWindow.perfLib();
perf.mark('renderer/started');
// Load environment in parallel to workbench loading to avoid waterfall
const bootstrapWindow = bootstrapWindowLib();
const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved();
// Load workbench main JS, CSS and NLS all in parallel. This is an
// optimization to prevent a waterfall of loading to happen, because
// we know for a fact that workbench.desktop.sandbox.main will depend on
@ -31,12 +28,6 @@
// Mark start of workbench
perf.mark('didLoadWorkbenchMain');
performance.mark('workbench-start');
// Wait for process environment being fully resolved
await whenEnvResolved;
perf.mark('main/startup');
// @ts-ignore
return require('vs/workbench/electron-sandbox/desktop.main').main(configuration);
@ -58,23 +49,11 @@
//region Helpers
function perfLib() {
globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || [];
return {
/**
* @param {string} name
*/
mark(name) {
globalThis.MonacoPerformanceMarks.push(name, Date.now());
}
};
}
/**
* @returns {{
* load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown,
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals')
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'),
* perfLib: () => { mark: (name: string) => void }
* }}
*/
function bootstrapWindowLib() {

View File

@ -123,10 +123,6 @@ export async function main(argv: string[]): Promise<any> {
'ELECTRON_NO_ATTACH_CONSOLE': '1'
};
if (args['force-user-env']) {
env['VSCODE_FORCE_USER_ENV'] = '1';
}
delete env['ELECTRON_RUN_AS_NODE'];
const processCallbacks: ((child: ChildProcess) => Promise<void>)[] = [];
@ -157,41 +153,39 @@ export async function main(argv: string[]): Promise<any> {
// stdin. We do this because there is no reliable way to find out if data is piped to stdin. Just
// checking for stdin being connected to a TTY is not enough (https://github.com/microsoft/vscode/issues/40351)
if (args._.length === 0) {
if (hasReadStdinArg) {
stdinFilePath = getStdinFilePath();
if (hasReadStdinArg) {
stdinFilePath = getStdinFilePath();
// returns a file path where stdin input is written into (write in progress).
try {
readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written
// returns a file path where stdin input is written into (write in progress).
try {
readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written
// Make sure to open tmp file
addArg(argv, stdinFilePath);
// Make sure to open tmp file
addArg(argv, stdinFilePath);
// Enable --wait to get all data and ignore adding this to history
addArg(argv, '--wait');
addArg(argv, '--skip-add-to-recently-opened');
args.wait = true;
// Enable --wait to get all data and ignore adding this to history
addArg(argv, '--wait');
addArg(argv, '--skip-add-to-recently-opened');
args.wait = true;
console.log(`Reading from stdin via: ${stdinFilePath}`);
} catch (e) {
console.log(`Failed to create file to read via stdin: ${e.toString()}`);
stdinFilePath = undefined;
}
} else {
// If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message
// if we detect that data flows into via stdin after a certain timeout.
processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => {
if (dataReceived) {
if (isWindows) {
console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`);
} else {
console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`);
}
}
}));
console.log(`Reading from stdin via: ${stdinFilePath}`);
} catch (e) {
console.log(`Failed to create file to read via stdin: ${e.toString()}`);
stdinFilePath = undefined;
}
} else {
// If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message
// if we detect that data flows into via stdin after a certain timeout.
processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => {
if (dataReceived) {
if (isWindows) {
console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`);
} else {
console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`);
}
}
}));
}
}

View File

@ -31,7 +31,7 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs';
import { getBaseLabel } from 'vs/base/common/labels';
import { IStateService } from 'vs/platform/state/node/state';
import { StateService } from 'vs/platform/state/node/stateService';
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
import { ILogService, getLogLevel, LogLevel, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { URI } from 'vs/base/common/uri';
@ -78,7 +78,7 @@ export class Main {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
) { }
async run(argv: NativeParsedArgs): Promise<void> {
@ -93,7 +93,7 @@ export class Main {
} else if (argv['locate-extension']) {
await this.locateExtension(argv['locate-extension']);
} else if (argv['telemetry']) {
console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath ? this.environmentService.extensionsPath : undefined));
console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath));
}
}
@ -126,13 +126,28 @@ export class Main {
extensions.forEach(e => console.log(getId(e.manifest, showVersions)));
}
private async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise<void> {
async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise<void> {
const failed: string[] = [];
const installedExtensionsManifests: IExtensionManifest[] = [];
if (extensions.length) {
console.log(localize('installingExtensions', "Installing extensions..."));
}
const installed = await this.extensionManagementService.getInstalled(ExtensionType.User);
const checkIfNotInstalled = (id: string, version?: string): boolean => {
const installedExtension = installed.find(i => areSameExtensions(i.identifier, { id }));
if (installedExtension) {
if (!version && !force) {
console.log(localize('alreadyInstalled-checkAndUpdate', "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: '{2}@1.2.3'.", id, installedExtension.manifest.version, id));
return false;
}
if (version && installedExtension.manifest.version === version) {
console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", `${id}@${version}`));
return false;
}
}
return true;
};
const vsixs: string[] = [];
const installExtensionInfos: InstallExtensionInfo[] = [];
for (const extension of extensions) {
@ -140,12 +155,16 @@ export class Main {
vsixs.push(extension);
} else {
const [id, version] = getIdAndVersion(extension);
installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } });
if (checkIfNotInstalled(id, version)) {
installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } });
}
}
}
for (const extension of builtinExtensionIds) {
const [id, version] = getIdAndVersion(extension);
installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } });
if (checkIfNotInstalled(id, version)) {
installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } });
}
}
if (vsixs.length) {
@ -162,28 +181,29 @@ export class Main {
}));
}
const [galleryExtensions, installed] = await Promise.all([
this.getGalleryExtensions(installExtensionInfos),
this.extensionManagementService.getInstalled(ExtensionType.User)
]);
if (installExtensionInfos.length) {
await Promise.all(installExtensionInfos.map(async extensionInfo => {
const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase());
if (gallery) {
try {
const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force);
if (manifest) {
installedExtensionsManifests.push(manifest);
const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos);
await Promise.all(installExtensionInfos.map(async extensionInfo => {
const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase());
if (gallery) {
try {
const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force);
if (manifest) {
installedExtensionsManifests.push(manifest);
}
} catch (err) {
console.error(err.message || err.stack || err);
failed.push(extensionInfo.id);
}
} catch (err) {
console.error(err.message || err.stack || err);
} else {
console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`);
failed.push(extensionInfo.id);
}
} else {
console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`);
failed.push(extensionInfo.id);
}
}));
}));
}
if (installedExtensionsManifests.some(manifest => isLanguagePackExtension(manifest))) {
await this.updateLocalizationsCache();
@ -244,10 +264,6 @@ export class Main {
console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id));
return null;
}
if (!version && !force) {
console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, galleryExtension.version));
return null;
}
console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, galleryExtension.version));
}
@ -315,7 +331,7 @@ export class Main {
return;
}
console.log(localize('uninstalling', "Uninstalling {0}...", id));
await this.extensionManagementService.uninstall(extensionToUninstall, true);
await this.extensionManagementService.uninstall(extensionToUninstall);
uninstalledExtensions.push(extensionToUninstall);
console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id));
}
@ -353,7 +369,13 @@ export async function main(argv: NativeParsedArgs): Promise<void> {
const disposables = new DisposableStore();
const environmentService = new NativeEnvironmentService(argv);
const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService));
const logLevel = getLogLevel(environmentService);
const loggers: ILogService[] = [];
loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel));
if (logLevel === LogLevel.Trace) {
loggers.push(new ConsoleLogService(logLevel));
}
const logService = new MultiplexLogService(loggers);
process.once('exit', () => logService.dispose());
logService.info('main', argv);
@ -403,7 +425,7 @@ export async function main(argv: NativeParsedArgs): Promise<void> {
appender: combinedAppender(...appenders),
sendErrorTelemetry: false,
commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath),
piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot]
piiPaths: [appRoot, extensionsPath]
};
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));

View File

@ -7,9 +7,57 @@ import { spawn } from 'child_process';
import { generateUuid } from 'vs/base/common/uuid';
import { isWindows } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { toErrorMessage } from 'vs/base/common/errorMessage';
function getUnixShellEnvironment(logService: ILogService): Promise<typeof process.env> {
/**
* We need to get the environment from a user's shell.
* This should only be done when Code itself is not launched
* from within a shell.
*/
export async function resolveShellEnv(logService: ILogService, args: NativeParsedArgs, env: NodeJS.ProcessEnv): Promise<typeof process.env> {
// Skip if --force-disable-user-env
if (args['force-disable-user-env']) {
logService.trace('resolveShellEnv(): skipped (--force-disable-user-env)');
return {};
}
// Skip on windows
else if (isWindows) {
logService.trace('resolveShellEnv(): skipped (Windows)');
return {};
}
// Skip if running from CLI already
else if (isLaunchedFromCli(env) && !args['force-user-env']) {
logService.trace('resolveShellEnv(): skipped (VSCODE_CLI is set)');
return {};
}
// Otherwise resolve (macOS, Linux)
else {
if (isLaunchedFromCli(env)) {
logService.trace('resolveShellEnv(): running (--force-user-env)');
} else {
logService.trace('resolveShellEnv(): running (macOS/Linux)');
}
if (!unixShellEnvPromise) {
unixShellEnvPromise = doResolveUnixShellEnv(logService);
}
return unixShellEnvPromise;
}
}
let unixShellEnvPromise: Promise<typeof process.env> | undefined = undefined;
async function doResolveUnixShellEnv(logService: ILogService): Promise<typeof process.env> {
const promise = new Promise<typeof process.env>((resolve, reject) => {
const runAsNode = process.env['ELECTRON_RUN_AS_NODE'];
logService.trace('getUnixShellEnvironment#runAsNode', runAsNode);
@ -78,33 +126,11 @@ function getUnixShellEnvironment(logService: ILogService): Promise<typeof proces
});
});
// swallow errors
return promise.catch(() => ({}));
}
try {
return await promise;
} catch (error) {
logService.error('getUnixShellEnvironment#error', toErrorMessage(error));
let shellEnvPromise: Promise<typeof process.env> | undefined = undefined;
/**
* We need to get the environment from a user's shell.
* This should only be done when Code itself is not launched
* from within a shell.
*/
export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise<typeof process.env> {
if (!shellEnvPromise) {
if (environmentService.args['disable-user-env-probe']) {
logService.trace('getShellEnvironment: disable-user-env-probe set, skipping');
shellEnvPromise = Promise.resolve({});
} else if (isWindows) {
logService.trace('getShellEnvironment: running on Windows, skipping');
shellEnvPromise = Promise.resolve({});
} else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') {
logService.trace('getShellEnvironment: running on CLI, skipping');
shellEnvPromise = Promise.resolve({});
} else {
logService.trace('getShellEnvironment: running on Unix');
shellEnvPromise = getUnixShellEnvironment(logService);
}
return {}; // ignore any errors
}
return shellEnvPromise;
}

View File

@ -1,40 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { isWindows } from 'vs/base/common/platform';
suite('Windows Native Helpers', () => {
if (!isWindows) {
return;
}
test('windows-mutex', async () => {
const mutex = await import('windows-mutex');
assert.ok(mutex && typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.');
assert.ok(typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.');
});
test('windows-foreground-love', async () => {
const foregroundLove = await import('windows-foreground-love');
assert.ok(foregroundLove && typeof foregroundLove.allowSetForegroundWindow === 'function', 'Unable to load windows-foreground-love dependency.');
});
test('windows-process-tree', async () => {
const processTree = await import('windows-process-tree');
assert.ok(processTree && typeof processTree.getProcessTree === 'function', 'Unable to load windows-process-tree dependency.');
});
test('vscode-windows-ca-certs', async () => {
// @ts-ignore Windows only
const windowsCerts = await import('vscode-windows-ca-certs');
assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.');
});
test('vscode-windows-registry', async () => {
const windowsRegistry = await import('vscode-windows-registry');
assert.ok(windowsRegistry && typeof windowsRegistry.GetStringRegKey === 'function', 'Unable to load vscode-windows-registry dependency.');
});
});

View File

@ -1,292 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import { restoreWindowsState, getWindowsStateStoreData } from 'vs/platform/windows/electron-main/windowsStateStorage';
import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsMainService';
function getUIState(): IWindowUIState {
return {
x: 0,
y: 10,
width: 100,
height: 200,
mode: 0
};
}
function toWorkspace(uri: URI): IWorkspaceIdentifier {
return {
id: '1234',
configPath: uri
};
}
function assertEqualURI(u1: URI | undefined, u2: URI | undefined, message?: string): void {
assert.equal(u1 && u1.toString(), u2 && u2.toString(), message);
}
function assertEqualWorkspace(w1: IWorkspaceIdentifier | undefined, w2: IWorkspaceIdentifier | undefined, message?: string): void {
if (!w1 || !w2) {
assert.equal(w1, w2, message);
return;
}
assert.equal(w1.id, w2.id, message);
assertEqualURI(w1.configPath, w2.configPath, message);
}
function assertEqualWindowState(expected: IWindowState | undefined, actual: IWindowState | undefined, message?: string) {
if (!expected || !actual) {
assert.deepEqual(expected, actual, message);
return;
}
assert.equal(expected.backupPath, actual.backupPath, message);
assertEqualURI(expected.folderUri, actual.folderUri, message);
assert.equal(expected.remoteAuthority, actual.remoteAuthority, message);
assertEqualWorkspace(expected.workspace, actual.workspace, message);
assert.deepEqual(expected.uiState, actual.uiState, message);
}
function assertEqualWindowsState(expected: IWindowsState, actual: IWindowsState, message?: string) {
assertEqualWindowState(expected.lastPluginDevelopmentHostWindow, actual.lastPluginDevelopmentHostWindow, message);
assertEqualWindowState(expected.lastActiveWindow, actual.lastActiveWindow, message);
assert.equal(expected.openedWindows.length, actual.openedWindows.length, message);
for (let i = 0; i < expected.openedWindows.length; i++) {
assertEqualWindowState(expected.openedWindows[i], actual.openedWindows[i], message);
}
}
function assertRestoring(state: IWindowsState, message?: string) {
const stored = getWindowsStateStoreData(state);
const restored = restoreWindowsState(stored);
assertEqualWindowsState(state, restored, message);
}
const testBackupPath1 = path.join(os.tmpdir(), 'windowStateTest', 'backupFolder1');
const testBackupPath2 = path.join(os.tmpdir(), 'windowStateTest', 'backupFolder2');
const testWSPath = URI.file(path.join(os.tmpdir(), 'windowStateTest', 'test.code-workspace'));
const testFolderURI = URI.file(path.join(os.tmpdir(), 'windowStateTest', 'testFolder'));
const testRemoteFolderURI = URI.parse('foo://bar/c/d');
suite('Windows State Storing', () => {
test('storing and restoring', () => {
let windowState: IWindowsState;
windowState = {
openedWindows: []
};
assertRestoring(windowState, 'no windows');
windowState = {
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState() }]
};
assertRestoring(windowState, 'empty workspace');
windowState = {
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), workspace: toWorkspace(testWSPath) }]
};
assertRestoring(windowState, 'workspace');
windowState = {
openedWindows: [{ backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI }]
};
assertRestoring(windowState, 'folder');
windowState = {
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), folderUri: testFolderURI }, { backupPath: testBackupPath1, uiState: getUIState(), folderUri: testRemoteFolderURI, remoteAuthority: 'bar' }]
};
assertRestoring(windowState, 'multiple windows');
windowState = {
lastActiveWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
openedWindows: []
};
assertRestoring(windowState, 'lastActiveWindow');
windowState = {
lastPluginDevelopmentHostWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
openedWindows: []
};
assertRestoring(windowState, 'lastPluginDevelopmentHostWindow');
});
test('open 1_31', () => {
const v1_31_workspace = `{
"openedWindows": [],
"lastActiveWindow": {
"workspace": {
"id": "a41787288b5e9cc1a61ba2dd84cd0d80",
"configPath": "/home/user/workspaces/code-and-docs.code-workspace"
},
"backupPath": "/home/user/.config/Code - Insiders/Backups/a41787288b5e9cc1a61ba2dd84cd0d80",
"uiState": {
"mode": 0,
"x": 0,
"y": 27,
"width": 2560,
"height": 1364
}
}
}`;
let windowsState = restoreWindowsState(JSON.parse(v1_31_workspace));
let expected: IWindowsState = {
openedWindows: [],
lastActiveWindow: {
backupPath: '/home/user/.config/Code - Insiders/Backups/a41787288b5e9cc1a61ba2dd84cd0d80',
uiState: { mode: WindowMode.Maximized, x: 0, y: 27, width: 2560, height: 1364 },
workspace: { id: 'a41787288b5e9cc1a61ba2dd84cd0d80', configPath: URI.file('/home/user/workspaces/code-and-docs.code-workspace') }
}
};
assertEqualWindowsState(expected, windowsState, 'v1_31_workspace');
const v1_31_folder = `{
"openedWindows": [],
"lastPluginDevelopmentHostWindow": {
"folderUri": {
"$mid": 1,
"fsPath": "/home/user/workspaces/testing/customdata",
"external": "file:///home/user/workspaces/testing/customdata",
"path": "/home/user/workspaces/testing/customdata",
"scheme": "file"
},
"uiState": {
"mode": 1,
"x": 593,
"y": 617,
"width": 1625,
"height": 595
}
}
}`;
windowsState = restoreWindowsState(JSON.parse(v1_31_folder));
expected = {
openedWindows: [],
lastPluginDevelopmentHostWindow: {
uiState: { mode: WindowMode.Normal, x: 593, y: 617, width: 1625, height: 595 },
folderUri: URI.parse('file:///home/user/workspaces/testing/customdata')
}
};
assertEqualWindowsState(expected, windowsState, 'v1_31_folder');
const v1_31_empty_window = ` {
"openedWindows": [
],
"lastActiveWindow": {
"backupPath": "C:\\\\Users\\\\Mike\\\\AppData\\\\Roaming\\\\Code\\\\Backups\\\\1549538599815",
"uiState": {
"mode": 0,
"x": -8,
"y": -8,
"width": 2576,
"height": 1344
}
}
}`;
windowsState = restoreWindowsState(JSON.parse(v1_31_empty_window));
expected = {
openedWindows: [],
lastActiveWindow: {
backupPath: 'C:\\Users\\Mike\\AppData\\Roaming\\Code\\Backups\\1549538599815',
uiState: { mode: WindowMode.Maximized, x: -8, y: -8, width: 2576, height: 1344 }
}
};
assertEqualWindowsState(expected, windowsState, 'v1_31_empty_window');
});
test('open 1_32', () => {
const v1_32_workspace = `{
"openedWindows": [],
"lastActiveWindow": {
"workspaceIdentifier": {
"id": "53b714b46ef1a2d4346568b4f591028c",
"configURIPath": "file:///home/user/workspaces/testing/custom.code-workspace"
},
"backupPath": "/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c",
"uiState": {
"mode": 0,
"x": 0,
"y": 27,
"width": 2560,
"height": 1364
}
}
}`;
let windowsState = restoreWindowsState(JSON.parse(v1_32_workspace));
let expected: IWindowsState = {
openedWindows: [],
lastActiveWindow: {
backupPath: '/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c',
uiState: { mode: WindowMode.Maximized, x: 0, y: 27, width: 2560, height: 1364 },
workspace: { id: '53b714b46ef1a2d4346568b4f591028c', configPath: URI.parse('file:///home/user/workspaces/testing/custom.code-workspace') }
}
};
assertEqualWindowsState(expected, windowsState, 'v1_32_workspace');
const v1_32_folder = `{
"openedWindows": [],
"lastActiveWindow": {
"folder": "file:///home/user/workspaces/testing/folding",
"backupPath": "/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5",
"uiState": {
"mode": 1,
"x": 625,
"y": 263,
"width": 1718,
"height": 953
}
}
}`;
windowsState = restoreWindowsState(JSON.parse(v1_32_folder));
expected = {
openedWindows: [],
lastActiveWindow: {
backupPath: '/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5',
uiState: { mode: WindowMode.Normal, x: 625, y: 263, width: 1718, height: 953 },
folderUri: URI.parse('file:///home/user/workspaces/testing/folding')
}
};
assertEqualWindowsState(expected, windowsState, 'v1_32_folder');
const v1_32_empty_window = ` {
"openedWindows": [
],
"lastActiveWindow": {
"backupPath": "/home/user/.config/code-oss-dev/Backups/1549539668998",
"uiState": {
"mode": 1,
"x": 768,
"y": 336,
"width": 1024,
"height": 768
}
}
}`;
windowsState = restoreWindowsState(JSON.parse(v1_32_empty_window));
expected = {
openedWindows: [],
lastActiveWindow: {
backupPath: '/home/user/.config/code-oss-dev/Backups/1549539668998',
uiState: { mode: WindowMode.Normal, x: 768, y: 336, width: 1024, height: 768 }
}
};
assertEqualWindowsState(expected, windowsState, 'v1_32_empty_window');
});
});

View File

@ -1,67 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { formatOptions, Option } from 'vs/platform/environment/node/argv';
import { addArg } from 'vs/platform/environment/node/argvHelper';
suite('formatOptions', () => {
function o(description: string): Option<any> {
return {
description, type: 'string'
};
}
test('Text should display small columns correctly', () => {
assert.deepEqual(
formatOptions({
'add': o('bar')
}, 80),
[' --add bar']
);
assert.deepEqual(
formatOptions({
'add': o('bar'),
'wait': o('ba'),
'trace': o('b')
}, 80),
[
' --add bar',
' --wait ba',
' --trace b'
]);
});
test('Text should wrap', () => {
assert.deepEqual(
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 40),
[
' --add bar bar bar bar bar bar bar bar',
' bar'
]);
});
test('Text should revert to the condensed view when the terminal is too narrow', () => {
assert.deepEqual(
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 30),
[
' --add',
' bar bar bar bar bar bar bar bar bar '
]);
});
test('addArg', () => {
assert.deepEqual(addArg([], 'foo'), ['foo']);
assert.deepEqual(addArg([], 'foo', 'bar'), ['foo', 'bar']);
assert.deepEqual(addArg(['foo'], 'bar'), ['foo', 'bar']);
assert.deepEqual(addArg(['--wait'], 'bar'), ['--wait', 'bar']);
assert.deepEqual(addArg(['--wait', '--', '--foo'], 'bar'), ['--wait', 'bar', '--', '--foo']);
assert.deepEqual(addArg(['--', '--foo'], 'bar'), ['bar', '--', '--foo']);
});
});