in src/vs/code/electron-main/app.ts [105:305]
private registerListeners(): void {
// We handle uncaught exceptions here to prevent electron from opening a dialog to the user
setUnexpectedErrorHandler(err => this.onUnexpectedError(err));
process.on('uncaughtException', err => this.onUnexpectedError(err));
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
// Dispose on shutdown
this.lifecycleMainService.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);
}
});
app.on('activate', (event: Event, hasVisibleWindows: boolean) => {
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 });
}
});
//#region Security related measures (https://electronjs.org/docs/tutorial/security)
//
// !!! DO NOT CHANGE without consulting the documentation !!!
//
app.on('remote-get-guest-web-contents', event => {
this.logService.trace('App#on(remote-get-guest-web-contents): prevented');
event.preventDefault();
});
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 => {
if (this.environmentService.args.driver) {
return; // the driver needs access to web contents
}
this.logService.trace(`App#on(remote-get-current-web-contents): prevented`);
event.preventDefault();
});
app.on('web-contents-created', (_event: Event, contents) => {
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
const isValidWebviewSource = (source: string | undefined): boolean => {
if (!source) {
return false;
}
const uri = URI.parse(source);
if (uri.scheme === Schemas.vscodeWebview) {
return uri.path === '/index.html' || uri.path === '/electron-browser/index.html';
}
const srcUri = uri.fsPath.toLowerCase();
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
return srcUri.startsWith(rootUri + sep);
};
// Ensure defaults
delete webPreferences.preload;
webPreferences.nodeIntegration = false;
// Verify URLs being loaded
// https://github.com/electron/electron/issues/21553
if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) {
return;
}
delete (webPreferences as { preloadURL: string | undefined }).preloadURL; // https://github.com/electron/electron/issues/21553
// Otherwise prevent loading
this.logService.error('webContents#web-contents-created: Prevented webview attach');
event.preventDefault();
});
contents.on('will-navigate', event => {
this.logService.error('webContents#will-navigate: Prevented webcontent navigation');
event.preventDefault();
});
contents.on('new-window', (event: Event, url: string) => {
event.preventDefault(); // prevent code that wants to open links
shell.openExternal(url);
});
session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => {
return callback(false);
});
session.defaultSession.setPermissionCheckHandler((webContents, permission /* 'media' */) => {
return false;
});
});
//#endregion
let macOpenFileURIs: IWindowOpenable[] = [];
let runningTimeout: NodeJS.Timeout | null = null;
app.on('open-file', (event: Event, path: string) => {
this.logService.trace('App#open-file: ', path);
event.preventDefault();
// Keep in array because more might come!
macOpenFileURIs.push(this.getWindowOpenableFromPathSync(path));
// Clear previous handler if any
if (runningTimeout !== null) {
clearTimeout(runningTimeout);
runningTimeout = null;
}
// 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 */
});
macOpenFileURIs = [];
runningTimeout = null;
}
}, 100);
});
app.on('new-window-for-tab', () => {
if (this.windowsMainService) {
this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
}
});
ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => {
const webContents = event.sender;
try {
const shellEnv = await getShellEnvironment(this.logService, this.environmentService);
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', shellEnv);
}
} catch (error) {
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', {});
}
this.logService.error('Error fetching shell env', error);
}
});
ipc.on('vscode:toggleDevTools', (event: IpcMainEvent) => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', (event: IpcMainEvent) => event.sender.openDevTools());
ipc.on('vscode:reloadWindow', (event: IpcMainEvent) => event.sender.reload());
// Some listeners after window opened
(async () => {
await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen);
// Keyboard layout changes (after window opened)
const nativeKeymap = await import('native-keymap');
nativeKeymap.onDidChangeKeyboardLayout(() => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
}
});
})();
}