Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol } from 'electron';
import * as platform from 'vs/base/common/platform';
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
@@ -38,7 +38,6 @@ import pkg from 'vs/platform/node/package';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { isUndefinedOrNull } from 'vs/base/common/types';
@@ -61,11 +60,10 @@ import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/p
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
import { ILabelService, RegisterFormatterEvent } from 'vs/platform/label/common/label';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/theme';
import { storeBackgroundColor } from 'vs/code/electron-main/theme';
import { nativeSep, join } from 'vs/base/common/paths';
import { homedir } from 'os';
import { localize } from 'vs/nls';
@@ -76,9 +74,12 @@ import { SnapUpdateService } from 'vs/platform/update/electron-main/updateServic
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
import { generateUuid } from 'vs/base/common/uuid';
// {{SQL CARBON EDIT}}
import { CodeMenu } from 'sql/workbench/electron-browser/menus';
// {{SQL CARBON EDIT}} - End
import { startsWith } from 'vs/base/common/strings';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
import { URLService } from 'vs/platform/url/common/urlService';
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
export class CodeApplication extends Disposable {
@@ -89,19 +90,17 @@ export class CodeApplication extends Disposable {
private electronIpcServer: ElectronIPCServer;
private sharedProcess: SharedProcess;
private sharedProcessClient: TPromise<Client>;
private sharedProcessClient: Promise<Client>;
constructor(
private mainIpcServer: Server,
private userEnv: platform.IProcessEnvironment,
@IInstantiationService private instantiationService: IInstantiationService,
@ILogService private logService: ILogService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IConfigurationService private configurationService: ConfigurationService,
@IStateService private stateService: IStateService,
@IHistoryMainService private historyMainService: IHistoryMainService,
@ILabelService private labelService: ILabelService
private userEnv: IProcessEnvironment,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IConfigurationService private readonly configurationService: ConfigurationService,
@IStateService private readonly stateService: IStateService
) {
super();
@@ -144,12 +143,27 @@ export class CodeApplication extends Disposable {
app.on('web-contents-created', (event: any, contents) => {
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
const isValidWebviewSource = (source: string): boolean => {
if (!source) {
return false;
}
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
return true;
}
const srcUri = URI.parse(source).fsPath.toLowerCase();
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
return startsWith(srcUri, rootUri + nativeSep);
};
// Ensure defaults
delete webPreferences.preload;
webPreferences.nodeIntegration = false;
// Verify URLs being loaded
if (this.isValidWebviewSource(params.src) && this.isValidWebviewSource(webPreferences.preloadURL)) {
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
return;
}
@@ -174,85 +188,6 @@ export class CodeApplication extends Disposable {
});
});
const connectionPool: Map<string, ActiveConnection> = new Map<string, ActiveConnection>();
class ActiveConnection {
private _authority: string;
private _client: TPromise<Client<RemoteAgentConnectionContext>>;
private _disposeRunner: RunOnceScheduler;
constructor(authority: string, host: string, port: number) {
this._authority = authority;
this._client = connectRemoteAgentManagement(authority, host, port, `main`);
this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000);
}
private _dispose(): void {
this._disposeRunner.dispose();
connectionPool.delete(this._authority);
this._client.then((connection) => {
connection.dispose();
});
}
public getClient(): TPromise<Client<RemoteAgentConnectionContext>> {
this._disposeRunner.schedule();
return this._client;
}
}
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
resolvedAuthorities.set(data.authority, data);
});
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
if (authority.indexOf('+') >= 0) {
if (resolvedAuthorities.has(authority)) {
return resolvedAuthorities.get(authority);
}
return null;
} else {
const [host, strPort] = authority.split(':');
const port = parseInt(strPort, 10);
return { authority, host, port, syncExtensions: false };
}
};
protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
if (request.method !== 'GET') {
return callback(null);
}
const uri = URI.parse(request.url);
let activeConnection: ActiveConnection = null;
if (connectionPool.has(uri.authority)) {
activeConnection = connectionPool.get(uri.authority);
} else {
let resolvedAuthority = resolveAuthority(uri.authority);
if (!resolvedAuthority) {
callback(null);
return;
}
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
connectionPool.set(uri.authority, activeConnection);
}
try {
const rawClient = await activeConnection.getClient();
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
callback(Buffer.from(fileContents));
} else {
callback(null);
}
} catch (err) {
errors.onUnexpectedError(err);
callback(null);
}
});
let macOpenFileURIs: URI[] = [];
let runningTimeout: any = null;
app.on('open-file', (event: Event, path: string) => {
@@ -321,44 +256,18 @@ export class CodeApplication extends Disposable {
}
});
ipc.on('vscode:labelRegisterFormatter', (event: any, data: RegisterFormatterEvent) => {
this.labelService.registerFormatter(data.selector, data.formatter);
});
ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools());
ipc.on('vscode:toggleDevTools', (event: Event) => {
event.sender.toggleDevTools();
});
ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload());
ipc.on('vscode:openDevTools', (event: Event) => {
event.sender.openDevTools();
});
ipc.on('vscode:reloadWindow', (event: Event) => {
event.sender.reload();
});
// Keyboard layout changes
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
powerMonitor.on('resume', () => { // After waking up from sleep
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
this.windowsMainService.sendToAll('vscode:osResume', undefined);
}
});
}
private isValidWebviewSource(source: string): boolean {
if (!source) {
return false;
}
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
return true;
}
const srcUri: any = URI.parse(source).fsPath.toLowerCase();
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
return srcUri.startsWith(rootUri + nativeSep);
}
private onUnexpectedError(err: Error): void {
if (err) {
@@ -384,14 +293,11 @@ export class CodeApplication extends Disposable {
// Theme changes
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
let data = JSON.parse(payload);
this.stateService.setItem(THEME_STORAGE_KEY, data.baseTheme);
this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background);
storeBackgroundColor(this.stateService, JSON.parse(payload));
}
}
startup(): TPromise<void> {
startup(): Promise<void> {
this.logService.debug('Starting VS Code');
this.logService.debug(`from: ${this.environmentService.appRoot}`);
this.logService.debug('args:', this.environmentService.args);
@@ -400,7 +306,7 @@ export class CodeApplication extends Disposable {
// This will help Windows to associate the running program with
// any shortcut that is pinned to the taskbar and prevent showing
// two icons in the taskbar for the same app.
if (platform.isWindows && product.win32AppUserModelId) {
if (isWindows && product.win32AppUserModelId) {
app.setAppUserModelId(product.win32AppUserModelId);
}
@@ -411,7 +317,7 @@ export class CodeApplication extends Disposable {
// Explicitly opt out of the patch here before creating any windows.
// See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085
try {
if (platform.isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
if (isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any);
}
} catch (error) {
@@ -421,9 +327,7 @@ export class CodeApplication extends Disposable {
// Create Electron IPC Server
this.electronIpcServer = new ElectronIPCServer();
// Resolve unique machine ID
this.logService.trace('Resolving machine identifier...');
return this.resolveMachineId().then(machineId => {
const startupWithMachineId = (machineId: string) => {
this.logService.trace(`Resolved machine identifier: ${machineId}`);
// Spawn shared process
@@ -456,6 +360,28 @@ export class CodeApplication extends Disposable {
this.stopTracingEventually(windows);
}
});
};
// Resolve unique machine ID
this.logService.trace('Resolving machine identifier...');
const resolvedMachineId = this.resolveMachineId();
if (typeof resolvedMachineId === 'string') {
return startupWithMachineId(resolvedMachineId);
} else {
return resolvedMachineId.then(machineId => startupWithMachineId(machineId));
}
}
private resolveMachineId(): string | Promise<string> {
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
if (machineId) {
return machineId;
}
return getMachineId().then(machineId => {
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
return machineId;
});
}
@@ -488,35 +414,20 @@ export class CodeApplication extends Disposable {
const timeoutHandle = setTimeout(() => stopRecording(true), 30000);
// Wait for all windows to get ready and stop tracing then
TPromise.join(windows.map(window => window.ready())).then(() => {
Promise.all(windows.map(window => window.ready())).then(() => {
clearTimeout(timeoutHandle);
stopRecording(false);
});
}
private resolveMachineId(): TPromise<string> {
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
if (machineId) {
return TPromise.wrap(machineId);
}
return getMachineId().then(machineId => {
// Remember in global storage
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
return machineId;
});
}
private initServices(machineId: string): Thenable<IInstantiationService> {
private initServices(machineId: string): Promise<IInstantiationService> {
const services = new ServiceCollection();
if (process.platform === 'win32') {
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
} else if (process.platform === 'linux') {
if (process.env.SNAP && process.env.SNAP_REVISION) {
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService));
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
} else {
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
}
@@ -530,6 +441,10 @@ export class CodeApplication extends Disposable {
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
services.set(IMenubarService, new SyncDescriptor(MenubarService));
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
services.set(IURLService, new SyncDescriptor(URLService));
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
// Telemetry
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
@@ -546,17 +461,20 @@ export class CodeApplication extends Disposable {
const appInstantiationService = this.instantiationService.createChild(services);
return appInstantiationService.invokeFunction(accessor => this.initStorageService(accessor)).then(() => appInstantiationService);
return appInstantiationService.invokeFunction(accessor => Promise.all([
this.initStorageService(accessor),
this.initBackupService(accessor)
])).then(() => appInstantiationService);
}
private initStorageService(accessor: ServicesAccessor): Thenable<void> {
private initStorageService(accessor: ServicesAccessor): Promise<void> {
const storageMainService = accessor.get(IStorageMainService) as StorageMainService;
// Ensure to close storage on shutdown
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
// Initialize storage service
return storageMainService.initialize().then(void 0, error => {
return storageMainService.initialize().then(undefined, error => {
errors.onUnexpectedError(error);
this.logService.error(error);
}).then(() => {
@@ -586,6 +504,12 @@ export class CodeApplication extends Disposable {
});
}
private initBackupService(accessor: ServicesAccessor): Promise<void> {
const backupMainService = accessor.get(IBackupMainService) as BackupMainService;
return backupMainService.initialize();
}
private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
const appInstantiationService = accessor.get(IInstantiationService);
@@ -635,8 +559,6 @@ export class CodeApplication extends Disposable {
// Propagate to clients
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
const args = this.environmentService.args;
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
@@ -645,11 +567,11 @@ export class CodeApplication extends Disposable {
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
// if there is none
if (platform.isMacintosh) {
if (isMacintosh) {
const environmentService = accessor.get(IEnvironmentService);
urlService.registerHandler({
handleURL(uri: URI): TPromise<boolean> {
handleURL(uri: URI): Promise<boolean> {
if (windowsMainService.getWindowCount() === 0) {
const cli = { ...environmentService.args, goto: true };
const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });
@@ -657,7 +579,7 @@ export class CodeApplication extends Disposable {
return window.ready().then(() => urlService.open(uri));
}
return TPromise.as(false);
return Promise.resolve(false);
}
});
}
@@ -666,6 +588,7 @@ export class CodeApplication extends Disposable {
urlService.registerHandler(multiplexURLHandler);
// Watch Electron URLs and forward them to the UrlService
const args = this.environmentService.args;
const urls = args['open-url'] ? args._urls : [];
const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
this._register(urlListener);
@@ -692,9 +615,10 @@ export class CodeApplication extends Disposable {
private afterWindowOpen(accessor: ServicesAccessor): void {
const windowsMainService = accessor.get(IWindowsMainService);
const historyMainService = accessor.get(IHistoryMainService);
let windowsMutex: Mutex | null = null;
if (platform.isWindows) {
if (isWindows) {
// Setup Windows mutex
try {
@@ -716,7 +640,7 @@ export class CodeApplication extends Disposable {
// Ensure Windows foreground love module
try {
// tslint:disable-next-line:no-unused-expression
<any>require.__$__nodeRequire('windows-foreground-love');
require.__$__nodeRequire('windows-foreground-love');
} catch (e) {
if (!this.environmentService.isBuilt) {
windowsMainService.showMessageBox({
@@ -730,21 +654,105 @@ export class CodeApplication extends Disposable {
}
}
// {{SQL CARBON EDIT}} - Use static menu for now
// Install Menu
const instantiationService = accessor.get(IInstantiationService);
const configurationService = accessor.get(IConfigurationService);
if (platform.isMacintosh || configurationService.getValue<string>('window.titleBarStyle') !== 'custom') {
instantiationService.createInstance(CodeMenu);
}
// {{SQL CARBON EDIT}} - End
// Remote Authorities
this.handleRemoteAuthorities();
// Keyboard layout changes
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
});
// Jump List
this.historyMainService.updateWindowsJumpList();
this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList());
historyMainService.updateWindowsJumpList();
historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
// Start shared process after a while
const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment().then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
sharedProcessSpawn.schedule();
}
private handleRemoteAuthorities(): void {
const connectionPool: Map<string, ActiveConnection> = new Map<string, ActiveConnection>();
const isBuilt = this.environmentService.isBuilt;
class ActiveConnection {
private _authority: string;
private _client: Promise<Client<RemoteAgentConnectionContext>>;
private _disposeRunner: RunOnceScheduler;
constructor(authority: string, host: string, port: number) {
this._authority = authority;
this._client = connectRemoteAgentManagement(authority, host, port, `main`, isBuilt);
this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000);
}
private _dispose(): void {
this._disposeRunner.dispose();
connectionPool.delete(this._authority);
this._client.then((connection) => {
connection.dispose();
});
}
public getClient(): Promise<Client<RemoteAgentConnectionContext>> {
this._disposeRunner.schedule();
return this._client;
}
}
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
resolvedAuthorities.set(data.authority, data);
});
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
if (authority.indexOf('+') >= 0) {
if (resolvedAuthorities.has(authority)) {
return resolvedAuthorities.get(authority);
}
return null;
} else {
const [host, strPort] = authority.split(':');
const port = parseInt(strPort, 10);
return { authority, host, port, syncExtensions: false };
}
};
protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
if (request.method !== 'GET') {
return callback(null);
}
const uri = URI.parse(request.url);
let activeConnection: ActiveConnection = null;
if (connectionPool.has(uri.authority)) {
activeConnection = connectionPool.get(uri.authority);
} else {
let resolvedAuthority = resolveAuthority(uri.authority);
if (!resolvedAuthority) {
callback(null);
return;
}
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
connectionPool.set(uri.authority, activeConnection);
}
try {
const rawClient = await activeConnection.getClient();
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
callback(Buffer.from(fileContents));
} else {
callback(null);
}
} catch (err) {
errors.onUnexpectedError(err);
callback(null);
}
});
}
}