Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/parts/sandbox/electron-browser/preload.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
/* eslint-disable no-restricted-globals */
7
8
(function () {
9
10
const { ipcRenderer, webFrame, contextBridge, webUtils } = require('electron');
11
12
type ISandboxConfiguration = import('../common/sandboxTypes.js').ISandboxConfiguration;
13
14
//#region Utilities
15
16
function validateIPC(channel: string): true | never {
17
if (!channel || !channel.startsWith('vscode:')) {
18
throw new Error(`Unsupported event IPC channel '${channel}'`);
19
}
20
21
return true;
22
}
23
24
function parseArgv(key: string): string | undefined {
25
for (const arg of process.argv) {
26
if (arg.indexOf(`--${key}=`) === 0) {
27
return arg.split('=')[1];
28
}
29
}
30
31
return undefined;
32
}
33
34
//#endregion
35
36
//#region Resolve Configuration
37
38
let configuration: ISandboxConfiguration | undefined = undefined;
39
40
const resolveConfiguration: Promise<ISandboxConfiguration> = (async () => {
41
const windowConfigIpcChannel = parseArgv('vscode-window-config');
42
if (!windowConfigIpcChannel) {
43
throw new Error('Preload: did not find expected vscode-window-config in renderer process arguments list.');
44
}
45
46
try {
47
validateIPC(windowConfigIpcChannel);
48
49
// Resolve configuration from electron-main
50
const resolvedConfiguration: ISandboxConfiguration = configuration = await ipcRenderer.invoke(windowConfigIpcChannel);
51
52
// Apply `userEnv` directly
53
Object.assign(process.env, resolvedConfiguration.userEnv);
54
55
// Apply zoom level early before even building the
56
// window DOM elements to avoid UI flicker. We always
57
// have to set the zoom level from within the window
58
// because Chrome has it's own way of remembering zoom
59
// settings per origin (if vscode-file:// is used) and
60
// we want to ensure that the user configuration wins.
61
webFrame.setZoomLevel(resolvedConfiguration.zoomLevel ?? 0);
62
63
return resolvedConfiguration;
64
} catch (error) {
65
throw new Error(`Preload: unable to fetch vscode-window-config: ${error}`);
66
}
67
})();
68
69
//#endregion
70
71
//#region Resolve Shell Environment
72
73
/**
74
* If VSCode is not run from a terminal, we should resolve additional
75
* shell specific environment from the OS shell to ensure we are seeing
76
* all development related environment variables. We do this from the
77
* main process because it may involve spawning a shell.
78
*/
79
const resolveShellEnv: Promise<typeof process.env> = (async () => {
80
81
// Resolve `userEnv` from configuration and
82
// `shellEnv` from the main side
83
const [userEnv, shellEnv] = await Promise.all([
84
(async () => (await resolveConfiguration).userEnv)(),
85
ipcRenderer.invoke('vscode:fetchShellEnv')
86
]);
87
88
return { ...process.env, ...shellEnv, ...userEnv };
89
})();
90
91
//#endregion
92
93
//#region Globals Definition
94
95
// #######################################################################
96
// ### ###
97
// ### !!! DO NOT USE GET/SET PROPERTIES ANYWHERE HERE !!! ###
98
// ### !!! UNLESS THE ACCESS IS WITHOUT SIDE EFFECTS !!! ###
99
// ### (https://github.com/electron/electron/issues/25516) ###
100
// ### ###
101
// #######################################################################
102
103
const globals = {
104
105
/**
106
* A minimal set of methods exposed from Electron's `ipcRenderer`
107
* to support communication to main process.
108
*/
109
110
ipcRenderer: {
111
112
send(channel: string, ...args: any[]): void {
113
if (validateIPC(channel)) {
114
ipcRenderer.send(channel, ...args);
115
}
116
},
117
118
invoke(channel: string, ...args: any[]): Promise<any> {
119
validateIPC(channel);
120
121
return ipcRenderer.invoke(channel, ...args);
122
},
123
124
on(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {
125
validateIPC(channel);
126
127
ipcRenderer.on(channel, listener);
128
129
return this;
130
},
131
132
once(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {
133
validateIPC(channel);
134
135
ipcRenderer.once(channel, listener);
136
137
return this;
138
},
139
140
removeListener(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {
141
validateIPC(channel);
142
143
ipcRenderer.removeListener(channel, listener);
144
145
return this;
146
}
147
},
148
149
ipcMessagePort: {
150
151
acquire(responseChannel: string, nonce: string) {
152
if (validateIPC(responseChannel)) {
153
const responseListener = (e: Electron.IpcRendererEvent, responseNonce: string) => {
154
// validate that the nonce from the response is the same
155
// as when requested. and if so, use `postMessage` to
156
// send the `MessagePort` safely over, even when context
157
// isolation is enabled
158
if (nonce === responseNonce) {
159
ipcRenderer.off(responseChannel, responseListener);
160
window.postMessage(nonce, '*', e.ports);
161
}
162
};
163
164
// handle reply from main
165
ipcRenderer.on(responseChannel, responseListener);
166
}
167
}
168
},
169
170
/**
171
* Support for subset of methods of Electron's `webFrame` type.
172
*/
173
webFrame: {
174
175
setZoomLevel(level: number): void {
176
if (typeof level === 'number') {
177
webFrame.setZoomLevel(level);
178
}
179
}
180
},
181
182
/**
183
* Support for subset of Electron's `webUtils` type.
184
*/
185
webUtils: {
186
187
getPathForFile(file: File): string {
188
return webUtils.getPathForFile(file);
189
}
190
},
191
192
/**
193
* Support for a subset of access to node.js global `process`.
194
*
195
* Note: when `sandbox` is enabled, the only properties available
196
* are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox
197
*/
198
process: {
199
get platform() { return process.platform; },
200
get arch() { return process.arch; },
201
get env() { return { ...process.env }; },
202
get versions() { return process.versions; },
203
get type() { return 'renderer'; },
204
get execPath() { return process.execPath; },
205
206
cwd(): string {
207
return process.env['VSCODE_CWD'] || process.execPath.substr(0, process.execPath.lastIndexOf(process.platform === 'win32' ? '\\' : '/'));
208
},
209
210
shellEnv(): Promise<typeof process.env> {
211
return resolveShellEnv;
212
},
213
214
getProcessMemoryInfo(): Promise<Electron.ProcessMemoryInfo> {
215
return process.getProcessMemoryInfo();
216
},
217
218
on(type: string, callback: (...args: any[]) => void): void {
219
process.on(type, callback);
220
}
221
},
222
223
/**
224
* Some information about the context we are running in.
225
*/
226
context: {
227
228
/**
229
* A configuration object made accessible from the main side
230
* to configure the sandbox browser window.
231
*
232
* Note: intentionally not using a getter here because the
233
* actual value will be set after `resolveConfiguration`
234
* has finished.
235
*/
236
configuration(): ISandboxConfiguration | undefined {
237
return configuration;
238
},
239
240
/**
241
* Allows to await the resolution of the configuration object.
242
*/
243
async resolveConfiguration(): Promise<ISandboxConfiguration> {
244
return resolveConfiguration;
245
}
246
}
247
};
248
249
// Use `contextBridge` APIs to expose globals to VSCode
250
// only if context isolation is enabled, otherwise just
251
// add to the DOM global.
252
if (process.contextIsolated) {
253
try {
254
contextBridge.exposeInMainWorld('vscode', globals);
255
} catch (error) {
256
console.error(error);
257
}
258
} else {
259
(window as any).vscode = globals;
260
}
261
}());
262
263