mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-28 17:23:19 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -9,7 +9,7 @@ import { WindowsManager } from 'vs/code/electron-main/windows';
|
||||
import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
|
||||
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
|
||||
import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { getShellEnvironment } from 'vs/code/node/shellEnv';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
|
||||
@@ -36,12 +36,10 @@ import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import pkg from 'vs/platform/product/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 { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
|
||||
import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
@@ -52,7 +50,7 @@ import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateServ
|
||||
import { IIssueService } from 'vs/platform/issue/common/issue';
|
||||
import { IssueChannel } from 'vs/platform/issue/node/issueIpc';
|
||||
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
|
||||
import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc';
|
||||
import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
|
||||
import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
|
||||
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
|
||||
@@ -63,7 +61,6 @@ import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
|
||||
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 { storeBackgroundColor } from 'vs/code/electron-main/theme';
|
||||
import { homedir } from 'os';
|
||||
import { join, sep } from 'vs/base/common/path';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -83,17 +80,19 @@ import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAg
|
||||
import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { statSync } from 'fs';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
|
||||
export class CodeApplication extends Disposable {
|
||||
|
||||
private static readonly MACHINE_ID_KEY = 'telemetry.machineId';
|
||||
private static readonly TRUE_MACHINE_ID_KEY = 'telemetry.trueMachineId';
|
||||
|
||||
private windowsMainService: IWindowsMainService;
|
||||
|
||||
private electronIpcServer: ElectronIPCServer;
|
||||
|
||||
private sharedProcess: SharedProcess;
|
||||
private sharedProcessClient: Promise<Client>;
|
||||
private windowsMainService: IWindowsMainService | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly mainIpcServer: Server,
|
||||
@@ -102,14 +101,12 @@ export class CodeApplication extends Disposable {
|
||||
@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
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@ISignService private readonly signService: ISignService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(mainIpcServer);
|
||||
this._register(configurationService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
@@ -120,12 +117,12 @@ export class CodeApplication extends Disposable {
|
||||
process.on('uncaughtException', err => this.onUnexpectedError(err));
|
||||
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
|
||||
|
||||
// Contextmenu via IPC support
|
||||
registerContextMenuListener();
|
||||
|
||||
// Dispose on shutdown
|
||||
this.lifecycleService.onWillShutdown(() => this.dispose());
|
||||
|
||||
// Contextmenu via IPC support
|
||||
registerContextMenuListener();
|
||||
|
||||
app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
|
||||
@@ -142,8 +139,38 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
|
||||
// Security related measures (https://electronjs.org/docs/tutorial/security)
|
||||
// DO NOT CHANGE without consulting the documentation
|
||||
app.on('web-contents-created', (event: Electron.Event, contents) => {
|
||||
//
|
||||
// !!! DO NOT CHANGE without consulting the documentation !!!
|
||||
//
|
||||
// app.on('remote-get-guest-web-contents', event => event.preventDefault()); // TODO@Ben TODO@Matt revisit this need for <webview>
|
||||
app.on('remote-require', (event, sender, module) => {
|
||||
this.logService.trace('App#on(remote-require): prevented');
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
app.on('remote-get-global', (event, sender, module) => {
|
||||
this.logService.trace(`App#on(remote-get-global): prevented on ${module}`);
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
app.on('remote-get-builtin', (event, sender, module) => {
|
||||
this.logService.trace(`App#on(remote-get-builtin): prevented on ${module}`);
|
||||
|
||||
if (module !== 'clipboard') {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
app.on('remote-get-current-window', event => {
|
||||
this.logService.trace(`App#on(remote-get-current-window): prevented`);
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
app.on('remote-get-current-web-contents', event => {
|
||||
this.logService.trace(`App#on(remote-get-current-web-contents): prevented`);
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
app.on('web-contents-created', (_event: Electron.Event, contents) => {
|
||||
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
|
||||
|
||||
const isValidWebviewSource = (source: string): boolean => {
|
||||
@@ -198,7 +225,7 @@ export class CodeApplication extends Disposable {
|
||||
event.preventDefault();
|
||||
|
||||
// Keep in array because more might come!
|
||||
macOpenFileURIs.push(getURIToOpenFromPathSync(path));
|
||||
macOpenFileURIs.push(this.getURIToOpenFromPathSync(path));
|
||||
|
||||
// Clear previous handler if any
|
||||
if (runningTimeout !== null) {
|
||||
@@ -215,6 +242,7 @@ export class CodeApplication extends Disposable {
|
||||
urisToOpen: macOpenFileURIs,
|
||||
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
|
||||
});
|
||||
|
||||
macOpenFileURIs = [];
|
||||
runningTimeout = null;
|
||||
}
|
||||
@@ -222,7 +250,9 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
|
||||
app.on('new-window-for-tab', () => {
|
||||
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:exit', (event: Event, code: number) => {
|
||||
@@ -232,37 +262,26 @@ export class CodeApplication extends Disposable {
|
||||
this.lifecycleService.kill(code);
|
||||
});
|
||||
|
||||
ipc.on('vscode:fetchShellEnv', (event: Event) => {
|
||||
ipc.on('vscode:fetchShellEnv', async (event: Event) => {
|
||||
const webContents = event.sender;
|
||||
getShellEnvironment(this.logService).then(shellEnv => {
|
||||
|
||||
try {
|
||||
const shellEnv = await getShellEnvironment(this.logService, this.environmentService);
|
||||
if (!webContents.isDestroyed()) {
|
||||
webContents.send('vscode:acceptShellEnv', shellEnv);
|
||||
}
|
||||
}, err => {
|
||||
} catch (error) {
|
||||
if (!webContents.isDestroyed()) {
|
||||
webContents.send('vscode:acceptShellEnv', {});
|
||||
}
|
||||
|
||||
this.logService.error('Error fetching shell env', err);
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: object; }) => {
|
||||
if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
|
||||
this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
|
||||
|
||||
// Handle specific events on main side
|
||||
this.onBroadcast(broadcast.channel, broadcast.payload);
|
||||
|
||||
// Send to all windows (except sender window)
|
||||
this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]);
|
||||
this.logService.error('Error fetching shell env', error);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => {
|
||||
if (this.windowsMainService) {
|
||||
// Send to all windows (except sender window)
|
||||
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]);
|
||||
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]); // Send to all windows (except sender window)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -271,11 +290,25 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload());
|
||||
|
||||
powerMonitor.on('resume', () => { // After waking up from sleep
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:osResume', undefined);
|
||||
}
|
||||
});
|
||||
// Some listeners after window opened
|
||||
(async () => {
|
||||
await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
|
||||
|
||||
// After waking up from sleep (after window opened)
|
||||
powerMonitor.on('resume', () => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:osResume', undefined);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard layout changes (after window opened)
|
||||
const nativeKeymap = await import('native-keymap');
|
||||
nativeKeymap.onDidChangeKeyboardLayout(() => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
private onUnexpectedError(err: Error): void {
|
||||
@@ -299,15 +332,7 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private onBroadcast(event: string, payload: object): void {
|
||||
|
||||
// Theme changes
|
||||
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
|
||||
storeBackgroundColor(this.stateService, JSON.parse(payload));
|
||||
}
|
||||
}
|
||||
|
||||
startup(): Promise<void> {
|
||||
async startup(): Promise<void> {
|
||||
this.logService.debug('Starting VS Code');
|
||||
this.logService.debug(`from: ${this.environmentService.appRoot}`);
|
||||
this.logService.debug('args:', this.environmentService.args);
|
||||
@@ -335,64 +360,142 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
|
||||
// Create Electron IPC Server
|
||||
this.electronIpcServer = new ElectronIPCServer();
|
||||
|
||||
const startupWithMachineId = (machineId: string) => {
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId}`);
|
||||
|
||||
// Spawn shared process
|
||||
this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
|
||||
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
|
||||
// Services
|
||||
return this.initServices(machineId).then(appInstantiationService => {
|
||||
|
||||
// Create driver
|
||||
if (this.environmentService.driverHandle) {
|
||||
serveDriver(this.electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService).then(server => {
|
||||
this.logService.info('Driver started at:', this.environmentService.driverHandle);
|
||||
this._register(server);
|
||||
});
|
||||
}
|
||||
|
||||
// Setup Auth Handler
|
||||
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
|
||||
this._register(authHandler);
|
||||
|
||||
// Open Windows
|
||||
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
|
||||
|
||||
// Post Open Windows Tasks
|
||||
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
|
||||
|
||||
// Tracing: Stop tracing after windows are ready if enabled
|
||||
if (this.environmentService.args.trace) {
|
||||
this.stopTracingEventually(windows);
|
||||
}
|
||||
});
|
||||
};
|
||||
const electronIpcServer = new ElectronIPCServer();
|
||||
|
||||
// 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));
|
||||
const { machineId, trueMachineId } = await this.resolveMachineId();
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId} (trueMachineId: ${trueMachineId})`);
|
||||
|
||||
// Spawn shared process after the first window has opened and 3s have passed
|
||||
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
|
||||
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
|
||||
this._register(new RunOnceScheduler(async () => {
|
||||
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
|
||||
|
||||
sharedProcess.spawn(userEnv);
|
||||
}, 3000)).schedule();
|
||||
});
|
||||
|
||||
// Services
|
||||
const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient);
|
||||
|
||||
// Create driver
|
||||
if (this.environmentService.driverHandle) {
|
||||
const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService);
|
||||
|
||||
this.logService.info('Driver started at:', this.environmentService.driverHandle);
|
||||
this._register(server);
|
||||
}
|
||||
|
||||
// Setup Auth Handler
|
||||
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
|
||||
this._register(authHandler);
|
||||
|
||||
// Open Windows
|
||||
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
|
||||
|
||||
// Post Open Windows Tasks
|
||||
this.afterWindowOpen();
|
||||
|
||||
// Tracing: Stop tracing after windows are ready if enabled
|
||||
if (this.environmentService.args.trace) {
|
||||
this.stopTracingEventually(windows);
|
||||
}
|
||||
}
|
||||
|
||||
private resolveMachineId(): string | Promise<string> {
|
||||
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
|
||||
if (machineId) {
|
||||
return machineId;
|
||||
private async resolveMachineId(): Promise<{ machineId: string, trueMachineId?: string }> {
|
||||
|
||||
// We cache the machineId for faster lookups on startup
|
||||
// and resolve it only once initially if not cached
|
||||
let machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
|
||||
if (!machineId) {
|
||||
machineId = await getMachineId();
|
||||
|
||||
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
|
||||
}
|
||||
|
||||
return getMachineId().then(machineId => {
|
||||
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
|
||||
// Check if machineId is hashed iBridge Device
|
||||
let trueMachineId: string | undefined;
|
||||
if (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead') {
|
||||
trueMachineId = this.stateService.getItem<string>(CodeApplication.TRUE_MACHINE_ID_KEY);
|
||||
if (!trueMachineId) {
|
||||
trueMachineId = await getMachineId();
|
||||
|
||||
return machineId;
|
||||
});
|
||||
this.stateService.setItem(CodeApplication.TRUE_MACHINE_ID_KEY, trueMachineId);
|
||||
}
|
||||
}
|
||||
|
||||
return { machineId, trueMachineId };
|
||||
}
|
||||
|
||||
private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessClient: Promise<Client<string>>): Promise<IInstantiationService> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
// Files
|
||||
const fileService = this._register(new FileService(this.logService));
|
||||
services.set(IFileService, fileService);
|
||||
|
||||
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(this.logService));
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
|
||||
break;
|
||||
|
||||
case 'linux':
|
||||
if (process.env.SNAP && process.env.SNAP_REVISION) {
|
||||
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
|
||||
} else {
|
||||
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'darwin':
|
||||
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
|
||||
break;
|
||||
}
|
||||
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv]));
|
||||
services.set(IWindowsService, new SyncDescriptor(WindowsService, [sharedProcess]));
|
||||
services.set(ILaunchService, new SyncDescriptor(LaunchService));
|
||||
|
||||
const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics')));
|
||||
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel]));
|
||||
|
||||
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
|
||||
services.set(IMenubarService, new SyncDescriptor(MenubarService));
|
||||
|
||||
const storageMainService = new StorageMainService(this.logService, this.environmentService);
|
||||
services.set(IStorageMainService, storageMainService);
|
||||
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
|
||||
|
||||
const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService);
|
||||
services.set(IBackupMainService, 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) {
|
||||
const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender')));
|
||||
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
|
||||
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
|
||||
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
|
||||
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId };
|
||||
|
||||
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
|
||||
} else {
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
// Init services that require it
|
||||
await backupMainService.initialize();
|
||||
|
||||
return this.instantiationService.createChild(services);
|
||||
}
|
||||
|
||||
private stopTracingEventually(windows: ICodeWindow[]): void {
|
||||
@@ -408,12 +511,14 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
|
||||
if (!timeout) {
|
||||
this.windowsMainService.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")]
|
||||
}, this.windowsMainService.getLastActiveWindow());
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.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")]
|
||||
}, this.windowsMainService.getLastActiveWindow());
|
||||
}
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
}
|
||||
@@ -430,72 +535,7 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
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, [process.env.SNAP, process.env.SNAP_REVISION]));
|
||||
} else {
|
||||
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
|
||||
}
|
||||
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId]));
|
||||
services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess]));
|
||||
services.set(ILaunchService, new SyncDescriptor(LaunchService));
|
||||
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) {
|
||||
const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender')));
|
||||
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
|
||||
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
|
||||
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
|
||||
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
|
||||
|
||||
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
|
||||
} else {
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
const appInstantiationService = this.instantiationService.createChild(services);
|
||||
|
||||
// Init services that require it
|
||||
return appInstantiationService.invokeFunction(accessor => Promise.all([
|
||||
this.initStorageService(accessor),
|
||||
this.initBackupService(accessor)
|
||||
])).then(() => appInstantiationService);
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>): ICodeWindow[] {
|
||||
|
||||
// Register more Main IPC services
|
||||
const launchService = accessor.get(ILaunchService);
|
||||
@@ -505,48 +545,48 @@ export class CodeApplication extends Disposable {
|
||||
// Register more Electron IPC services
|
||||
const updateService = accessor.get(IUpdateService);
|
||||
const updateChannel = new UpdateChannel(updateService);
|
||||
this.electronIpcServer.registerChannel('update', updateChannel);
|
||||
electronIpcServer.registerChannel('update', updateChannel);
|
||||
|
||||
const issueService = accessor.get(IIssueService);
|
||||
const issueChannel = new IssueChannel(issueService);
|
||||
this.electronIpcServer.registerChannel('issue', issueChannel);
|
||||
electronIpcServer.registerChannel('issue', issueChannel);
|
||||
|
||||
const workspacesService = accessor.get(IWorkspacesMainService);
|
||||
const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService);
|
||||
this.electronIpcServer.registerChannel('workspaces', workspacesChannel);
|
||||
const workspacesChannel = new WorkspacesChannel(workspacesService);
|
||||
electronIpcServer.registerChannel('workspaces', workspacesChannel);
|
||||
|
||||
const windowsService = accessor.get(IWindowsService);
|
||||
const windowsChannel = new WindowsChannel(windowsService);
|
||||
this.electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
|
||||
electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
|
||||
|
||||
const menubarService = accessor.get(IMenubarService);
|
||||
const menubarChannel = new MenubarChannel(menubarService);
|
||||
this.electronIpcServer.registerChannel('menubar', menubarChannel);
|
||||
electronIpcServer.registerChannel('menubar', menubarChannel);
|
||||
|
||||
const urlService = accessor.get(IURLService);
|
||||
const urlChannel = new URLServiceChannel(urlService);
|
||||
this.electronIpcServer.registerChannel('url', urlChannel);
|
||||
electronIpcServer.registerChannel('url', urlChannel);
|
||||
|
||||
const storageMainService = accessor.get(IStorageMainService);
|
||||
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService));
|
||||
this.electronIpcServer.registerChannel('storage', storageChannel);
|
||||
electronIpcServer.registerChannel('storage', storageChannel);
|
||||
|
||||
// Log level management
|
||||
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
|
||||
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
|
||||
this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
|
||||
electronIpcServer.registerChannel('loglevel', logLevelChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
|
||||
|
||||
// Lifecycle
|
||||
(this.lifecycleService as LifecycleService).ready();
|
||||
// Signal phase: ready (services set)
|
||||
this.lifecycleService.phase = LifecycleMainPhase.Ready;
|
||||
|
||||
// Propagate to clients
|
||||
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
|
||||
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
|
||||
|
||||
// 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));
|
||||
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter);
|
||||
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', activeWindowRouter);
|
||||
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
|
||||
|
||||
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
|
||||
@@ -555,15 +595,17 @@ export class CodeApplication extends Disposable {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
|
||||
urlService.registerHandler({
|
||||
handleURL(uri: URI): Promise<boolean> {
|
||||
async 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 });
|
||||
|
||||
return window.ready().then(() => urlService.open(uri));
|
||||
await window.ready();
|
||||
|
||||
return urlService.open(uri);
|
||||
}
|
||||
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -574,11 +616,9 @@ export class CodeApplication extends Disposable {
|
||||
// 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);
|
||||
const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService);
|
||||
this._register(urlListener);
|
||||
|
||||
this.windowsMainService.ready(this.userEnv);
|
||||
|
||||
// Open our first window
|
||||
const macOpenFiles: string[] = (<any>global).macOpenFiles;
|
||||
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
|
||||
@@ -588,9 +628,9 @@ export class CodeApplication extends Disposable {
|
||||
const noRecentEntry = args['skip-add-to-recently-opened'] === true;
|
||||
const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined;
|
||||
|
||||
// new window if "-n" was used without paths
|
||||
if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
|
||||
// new window if "-n" was used without paths
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context,
|
||||
cli: args,
|
||||
forceNewWindow: true,
|
||||
@@ -601,12 +641,12 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
// mac: open-file event received on startup
|
||||
if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
|
||||
// mac: open-file event received on startup
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context: OpenContext.DOCK,
|
||||
cli: args,
|
||||
urisToOpen: macOpenFiles.map(getURIToOpenFromPathSync),
|
||||
urisToOpen: macOpenFiles.map(file => this.getURIToOpenFromPathSync(file)),
|
||||
noRecentEntry,
|
||||
waitMarkerFileURI,
|
||||
initialStartup: true
|
||||
@@ -614,7 +654,7 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
|
||||
// default: read paths from cli
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context,
|
||||
cli: args,
|
||||
forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']),
|
||||
@@ -625,61 +665,30 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private afterWindowOpen(accessor: ServicesAccessor): void {
|
||||
const windowsMainService = accessor.get(IWindowsMainService);
|
||||
const historyMainService = accessor.get(IHistoryMainService);
|
||||
|
||||
if (isWindows) {
|
||||
|
||||
// Setup Windows mutex
|
||||
try {
|
||||
const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex;
|
||||
const windowsMutex = new Mutex(product.win32MutexName);
|
||||
this._register(toDisposable(() => windowsMutex.release()));
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
windowsMainService.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-mutex!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
private getURIToOpenFromPathSync(path: string): IURIToOpen {
|
||||
try {
|
||||
const fileStat = statSync(path);
|
||||
if (fileStat.isDirectory()) {
|
||||
return { folderUri: URI.file(path) };
|
||||
}
|
||||
|
||||
// Ensure Windows foreground love module
|
||||
try {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
require.__$__nodeRequire('windows-foreground-love');
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
windowsMainService.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-foreground-love!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
if (hasWorkspaceFileExtension(path)) {
|
||||
return { workspaceUri: URI.file(path) };
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore errors
|
||||
}
|
||||
|
||||
return { fileUri: URI.file(path) };
|
||||
}
|
||||
|
||||
private afterWindowOpen(): void {
|
||||
|
||||
// Signal phase: after window open
|
||||
this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen;
|
||||
|
||||
// Remote Authorities
|
||||
this.handleRemoteAuthorities();
|
||||
|
||||
// Keyboard layout changes
|
||||
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
});
|
||||
|
||||
// Jump List
|
||||
historyMainService.updateWindowsJumpList();
|
||||
historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
|
||||
|
||||
// Start shared process after a while
|
||||
const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment(this.logService).then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
|
||||
sharedProcessSpawn.schedule();
|
||||
}
|
||||
|
||||
private handleRemoteAuthorities(): void {
|
||||
@@ -692,18 +701,21 @@ export class CodeApplication extends Disposable {
|
||||
private readonly _connection: Promise<ManagementPersistentConnection>;
|
||||
private readonly _disposeRunner: RunOnceScheduler;
|
||||
|
||||
constructor(authority: string, host: string, port: number) {
|
||||
constructor(authority: string, host: string, port: number, signService: ISignService) {
|
||||
this._authority = authority;
|
||||
|
||||
const options: IConnectionOptions = {
|
||||
isBuilt: isBuilt,
|
||||
isBuilt,
|
||||
commit: product.commit,
|
||||
webSocketFactory: nodeWebSocketFactory,
|
||||
addressProvider: {
|
||||
getAddress: () => {
|
||||
return Promise.resolve({ host, port });
|
||||
}
|
||||
}
|
||||
},
|
||||
signService
|
||||
};
|
||||
|
||||
this._connection = connectRemoteAgentManagement(options, authority, `main`);
|
||||
this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000);
|
||||
}
|
||||
@@ -711,14 +723,13 @@ export class CodeApplication extends Disposable {
|
||||
dispose(): void {
|
||||
this._disposeRunner.dispose();
|
||||
connectionPool.delete(this._authority);
|
||||
this._connection.then((connection) => {
|
||||
connection.dispose();
|
||||
});
|
||||
this._connection.then(connection => connection.dispose());
|
||||
}
|
||||
|
||||
async getClient(): Promise<Client<RemoteAgentConnectionContext>> {
|
||||
this._disposeRunner.schedule();
|
||||
const connection = await this._connection;
|
||||
|
||||
return connection.client;
|
||||
}
|
||||
}
|
||||
@@ -726,7 +737,9 @@ export class CodeApplication extends Disposable {
|
||||
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => {
|
||||
this.logService.info('Received resolved authority', data.authority);
|
||||
|
||||
resolvedAuthorities.set(data.authority, data);
|
||||
|
||||
// Make sure to close and remove any existing connections
|
||||
if (connectionPool.has(data.authority)) {
|
||||
connectionPool.get(data.authority)!.dispose();
|
||||
@@ -735,15 +748,19 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
|
||||
this.logService.info('Resolving authority', authority);
|
||||
|
||||
if (authority.indexOf('+') >= 0) {
|
||||
if (resolvedAuthorities.has(authority)) {
|
||||
return withUndefinedAsNull(resolvedAuthorities.get(authority));
|
||||
}
|
||||
|
||||
this.logService.info('Didnot find resolved authority for', authority);
|
||||
|
||||
return null;
|
||||
} else {
|
||||
const [host, strPort] = authority.split(':');
|
||||
const port = parseInt(strPort, 10);
|
||||
|
||||
return { authority, host, port };
|
||||
}
|
||||
};
|
||||
@@ -752,6 +769,7 @@ export class CodeApplication extends Disposable {
|
||||
if (request.method !== 'GET') {
|
||||
return callback(undefined);
|
||||
}
|
||||
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
let activeConnection: ActiveConnection | undefined;
|
||||
@@ -763,9 +781,11 @@ export class CodeApplication extends Disposable {
|
||||
callback(undefined);
|
||||
return;
|
||||
}
|
||||
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
|
||||
|
||||
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port, this.signService);
|
||||
connectionPool.set(uri.authority, activeConnection);
|
||||
}
|
||||
|
||||
try {
|
||||
const rawClient = await activeConnection!.getClient();
|
||||
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
|
||||
@@ -784,16 +804,3 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getURIToOpenFromPathSync(path: string): IURIToOpen {
|
||||
try {
|
||||
const fileStat = statSync(path);
|
||||
if (fileStat.isDirectory()) {
|
||||
return { folderUri: URI.file(path) };
|
||||
} else if (hasWorkspaceFileExtension(path)) {
|
||||
return { workspaceUri: URI.file(path) };
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
return { fileUri: URI.file(path) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user