Path: blob/main/src/vs/base/parts/sandbox/electron-browser/preload.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45/* eslint-disable no-restricted-globals */67(function () {89const { ipcRenderer, webFrame, contextBridge, webUtils } = require('electron');1011type ISandboxConfiguration = import('../common/sandboxTypes.js').ISandboxConfiguration;1213//#region Utilities1415function validateIPC(channel: string): true | never {16if (!channel || !channel.startsWith('vscode:')) {17throw new Error(`Unsupported event IPC channel '${channel}'`);18}1920return true;21}2223function parseArgv(key: string): string | undefined {24for (const arg of process.argv) {25if (arg.indexOf(`--${key}=`) === 0) {26return arg.split('=')[1];27}28}2930return undefined;31}3233//#endregion3435//#region Resolve Configuration3637let configuration: ISandboxConfiguration | undefined = undefined;3839const resolveConfiguration: Promise<ISandboxConfiguration> = (async () => {40const windowConfigIpcChannel = parseArgv('vscode-window-config');41if (!windowConfigIpcChannel) {42throw new Error('Preload: did not find expected vscode-window-config in renderer process arguments list.');43}4445try {46validateIPC(windowConfigIpcChannel);4748// Resolve configuration from electron-main49const resolvedConfiguration: ISandboxConfiguration = configuration = await ipcRenderer.invoke(windowConfigIpcChannel);5051// Apply `userEnv` directly52Object.assign(process.env, resolvedConfiguration.userEnv);5354// Apply zoom level early before even building the55// window DOM elements to avoid UI flicker. We always56// have to set the zoom level from within the window57// because Chrome has it's own way of remembering zoom58// settings per origin (if vscode-file:// is used) and59// we want to ensure that the user configuration wins.60webFrame.setZoomLevel(resolvedConfiguration.zoomLevel ?? 0);6162return resolvedConfiguration;63} catch (error) {64throw new Error(`Preload: unable to fetch vscode-window-config: ${error}`);65}66})();6768//#endregion6970//#region Resolve Shell Environment7172/**73* If VSCode is not run from a terminal, we should resolve additional74* shell specific environment from the OS shell to ensure we are seeing75* all development related environment variables. We do this from the76* main process because it may involve spawning a shell.77*/78const resolveShellEnv: Promise<typeof process.env> = (async () => {7980// Resolve `userEnv` from configuration and81// `shellEnv` from the main side82const [userEnv, shellEnv] = await Promise.all([83(async () => (await resolveConfiguration).userEnv)(),84ipcRenderer.invoke('vscode:fetchShellEnv')85]);8687return { ...process.env, ...shellEnv, ...userEnv };88})();8990//#endregion9192//#region Globals Definition9394// #######################################################################95// ### ###96// ### !!! DO NOT USE GET/SET PROPERTIES ANYWHERE HERE !!! ###97// ### !!! UNLESS THE ACCESS IS WITHOUT SIDE EFFECTS !!! ###98// ### (https://github.com/electron/electron/issues/25516) ###99// ### ###100// #######################################################################101102const globals = {103104/**105* A minimal set of methods exposed from Electron's `ipcRenderer`106* to support communication to main process.107*/108109ipcRenderer: {110111send(channel: string, ...args: any[]): void {112if (validateIPC(channel)) {113ipcRenderer.send(channel, ...args);114}115},116117invoke(channel: string, ...args: any[]): Promise<any> {118validateIPC(channel);119120return ipcRenderer.invoke(channel, ...args);121},122123on(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {124validateIPC(channel);125126ipcRenderer.on(channel, listener);127128return this;129},130131once(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {132validateIPC(channel);133134ipcRenderer.once(channel, listener);135136return this;137},138139removeListener(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {140validateIPC(channel);141142ipcRenderer.removeListener(channel, listener);143144return this;145}146},147148ipcMessagePort: {149150acquire(responseChannel: string, nonce: string) {151if (validateIPC(responseChannel)) {152const responseListener = (e: Electron.IpcRendererEvent, responseNonce: string) => {153// validate that the nonce from the response is the same154// as when requested. and if so, use `postMessage` to155// send the `MessagePort` safely over, even when context156// isolation is enabled157if (nonce === responseNonce) {158ipcRenderer.off(responseChannel, responseListener);159window.postMessage(nonce, '*', e.ports);160}161};162163// handle reply from main164ipcRenderer.on(responseChannel, responseListener);165}166}167},168169/**170* Support for subset of methods of Electron's `webFrame` type.171*/172webFrame: {173174setZoomLevel(level: number): void {175if (typeof level === 'number') {176webFrame.setZoomLevel(level);177}178}179},180181/**182* Support for subset of Electron's `webUtils` type.183*/184webUtils: {185186getPathForFile(file: File): string {187return webUtils.getPathForFile(file);188}189},190191/**192* Support for a subset of access to node.js global `process`.193*194* Note: when `sandbox` is enabled, the only properties available195* are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox196*/197process: {198get platform() { return process.platform; },199get arch() { return process.arch; },200get env() { return { ...process.env }; },201get versions() { return process.versions; },202get type() { return 'renderer'; },203get execPath() { return process.execPath; },204205cwd(): string {206return process.env['VSCODE_CWD'] || process.execPath.substr(0, process.execPath.lastIndexOf(process.platform === 'win32' ? '\\' : '/'));207},208209shellEnv(): Promise<typeof process.env> {210return resolveShellEnv;211},212213getProcessMemoryInfo(): Promise<Electron.ProcessMemoryInfo> {214return process.getProcessMemoryInfo();215},216217on(type: string, callback: (...args: any[]) => void): void {218process.on(type, callback);219}220},221222/**223* Some information about the context we are running in.224*/225context: {226227/**228* A configuration object made accessible from the main side229* to configure the sandbox browser window.230*231* Note: intentionally not using a getter here because the232* actual value will be set after `resolveConfiguration`233* has finished.234*/235configuration(): ISandboxConfiguration | undefined {236return configuration;237},238239/**240* Allows to await the resolution of the configuration object.241*/242async resolveConfiguration(): Promise<ISandboxConfiguration> {243return resolveConfiguration;244}245}246};247248// Use `contextBridge` APIs to expose globals to VSCode249// only if context isolation is enabled, otherwise just250// add to the DOM global.251if (process.contextIsolated) {252try {253contextBridge.exposeInMainWorld('vscode', globals);254} catch (error) {255console.error(error);256}257} else {258(window as any).vscode = globals;259}260}());261262263