Archived
1
0
This repository has been archived on 2024-09-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
code-server/lib/vscode/src/vs/code/electron-main/sharedProcess.ts
Joe Previte eae5d8c807 chore(vscode): update to 1.53.2
These conflicts will be resolved in the following commits. We do it this way so
that PR review is possible.
2021-02-25 11:27:27 -07:00

245 lines
8.1 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrowserWindow, ipcMain, Event, MessagePortMain } from 'electron';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { FileAccess } from 'vs/base/common/network';
import { browserCodeLoadingCacheStrategy } from 'vs/base/common/platform';
import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedProcess/node/sharedProcess';
import { Disposable } from 'vs/base/common/lifecycle';
import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp';
import { assertIsDefined } from 'vs/base/common/types';
export class SharedProcess extends Disposable implements ISharedProcess {
private readonly whenSpawnedBarrier = new Barrier();
private window: BrowserWindow | undefined = undefined;
private windowCloseListener: ((event: Event) => void) | undefined = undefined;
constructor(
private readonly machineId: string,
private userEnv: NodeJS.ProcessEnv,
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService,
@IThemeMainService private readonly themeMainService: IThemeMainService
) {
super();
this.registerListeners();
}
private registerListeners(): void {
// Lifecycle
this._register(this.lifecycleMainService.onWillShutdown(() => this.onWillShutdown()));
// Shared process connections from workbench windows
ipcMain.on('vscode:createSharedProcessMessageChannel', async (e, nonce: string) => {
this.logService.trace('SharedProcess: on vscode:createSharedProcessMessageChannel');
// await the shared process to be overall ready
// we do not just wait for IPC ready because the
// workbench window will communicate directly
await this.whenReady();
// connect to the shared process window
const port = await this.connect();
// Check back if the requesting window meanwhile closed
// Since shared process is delayed on startup there is
// a chance that the window close before the shared process
// was ready for a connection.
if (e.sender.isDestroyed()) {
return port.close();
}
// send the port back to the requesting window
e.sender.postMessage('vscode:createSharedProcessMessageChannelResult', nonce, [port]);
});
}
private onWillShutdown(): void {
const window = this.window;
if (!window) {
return; // possibly too early before created
}
// Signal exit to shared process when shutting down
if (!window.isDestroyed() && !window.webContents.isDestroyed()) {
window.webContents.send('vscode:electron-main->shared-process=exit');
}
// Shut the shared process down when we are quitting
//
// Note: because we veto the window close, we must first remove our veto.
// Otherwise the application would never quit because the shared process
// window is refusing to close!
//
if (this.windowCloseListener) {
window.removeListener('close', this.windowCloseListener);
this.windowCloseListener = undefined;
}
// Electron seems to crash on Windows without this setTimeout :|
setTimeout(() => {
try {
window.close();
} catch (err) {
// ignore, as electron is already shutting down
}
this.window = undefined;
}, 0);
}
private _whenReady: Promise<void> | undefined = undefined;
whenReady(): Promise<void> {
if (!this._whenReady) {
// Overall signal that the shared process window was loaded and
// all services within have been created.
this._whenReady = new Promise<void>(resolve => ipcMain.once('vscode:shared-process->electron-main=init-done', () => {
this.logService.trace('SharedProcess: Overall ready');
resolve();
}));
}
return this._whenReady;
}
private _whenIpcReady: Promise<void> | undefined = undefined;
private get whenIpcReady() {
if (!this._whenIpcReady) {
this._whenIpcReady = (async () => {
// Always wait for `spawn()`
await this.whenSpawnedBarrier.wait();
// Create window for shared process
this.createWindow();
// Listeners
this.registerWindowListeners();
// Wait for window indicating that IPC connections are accepted
await new Promise<void>(resolve => ipcMain.once('vscode:shared-process->electron-main=ipc-ready', () => {
this.logService.trace('SharedProcess: IPC ready');
resolve();
}));
})();
}
return this._whenIpcReady;
}
private createWindow(): void {
// shared process is a hidden window by default
this.window = new BrowserWindow({
show: false,
backgroundColor: this.themeMainService.getBackgroundColor(),
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: browserCodeLoadingCacheStrategy,
nodeIntegration: true,
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
nativeWindowOpen: true,
images: false,
webgl: false,
disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer
}
});
const config: ISharedProcessConfiguration = {
machineId: this.machineId,
windowId: this.window.id,
appRoot: this.environmentService.appRoot,
nodeCachedDataDir: this.environmentService.nodeCachedDataDir,
backupWorkspacesPath: this.environmentService.backupWorkspacesPath,
userEnv: this.userEnv,
sharedIPCHandle: this.environmentService.sharedIPCHandle,
args: this.environmentService.args,
logLevel: this.logService.getLevel()
};
// Load with config
this.window.loadURL(FileAccess
.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true)
);
}
private registerWindowListeners(): void {
if (!this.window) {
return;
}
// Prevent the window from closing
this.windowCloseListener = (e: Event) => {
this.logService.trace('SharedProcess#close prevented');
// We never allow to close the shared process unless we get explicitly disposed()
e.preventDefault();
// Still hide the window though if visible
if (this.window?.isVisible()) {
this.window.hide();
}
};
this.window.on('close', this.windowCloseListener);
// Crashes & Unrsponsive & Failed to load
this.window.webContents.on('render-process-gone', (event, details) => this.logService.error(`SharedProcess: crashed (detail: ${details?.reason})`));
this.window.on('unresponsive', () => this.logService.error('SharedProcess: detected unresponsive window'));
this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.logService.warn('SharedProcess: failed to load window, ', errorDescription));
}
spawn(userEnv: NodeJS.ProcessEnv): void {
this.userEnv = { ...this.userEnv, ...userEnv };
// Release barrier
this.whenSpawnedBarrier.open();
}
async connect(): Promise<MessagePortMain> {
// Wait for shared process being ready to accept connection
await this.whenIpcReady;
// Connect and return message port
const window = assertIsDefined(this.window);
return connectMessagePort(window);
}
async toggle(): Promise<void> {
// wait for window to be created
await this.whenIpcReady;
if (!this.window) {
return; // possibly disposed already
}
if (this.window.isVisible()) {
this.window.webContents.closeDevTools();
this.window.hide();
} else {
this.window.show();
this.window.webContents.openDevTools();
}
}
}