-
Notifications
You must be signed in to change notification settings - Fork 4
Description
VS Code: Process Architecture
The work of enabling process sandbox is started at early 2020, and plan to finish at the beginning of 2023.
Process Architecture before enabling the sandbox
-
Renderer process is a major client of Node.js APIs - for example to read and write files.
-
Most processes are Node.js child processes (in green) forked from the renderer process.
-
Most of the IPC is implemented via Node.js sockets.
-
The
Shared Process
is a hidden Electron window with Node.js enabled. All other windows can communicate with to perform complex tasks such as extension installation. It is a work process.const worker = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) await worker.loadFile('worker.html')
- The disadvantage is that Node.js socket communication is not possible in sandboxed renderers.
-
The
extension host
is a process that runs all the installed extensions isolated from the renderer process. There is on extension host per opened window.
Process Architecture after enabling the sandbox
This is the process architecture in late 2022.
-
Remove all Node.js dependencies from the renderer
- Code that was previously running in one process to be split and run across multiple processes.
- Replace global objects such as Node.js
Buffer
with compatiable variants such asUint8Array
.
-
Expose a
vscode
global variable for communication using preload scripts// src/vs/base/parts/sandbox/electron-sandbox/preload.js if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('vscode', globals); } catch (error) { console.error(error); } } else { // @ts-ignore window.vscode = globals; }
-
Moving child processes out of the renderer
- Converted the
extension host process
to be autility process
that is created from the main process. - Converted the
shared process
to be autility process
- Notice: At the time the diagram was created, shared process was a hidden window, now it is a utility process. So, I don’t confirm this part in the diagram is correct. I will review it after reading source code of this part.
- Integrated terminals and file watching moved to be child processes of the shared process.
- Converted the
-
Fast IPC via message port
-
Renderer processes directly communicate with shared process and extension host process using
MessagePort
. -
The Sequence of passing
MessagePort
across shared process and renderer process- The shared process creates the message ports P1 and P2 and keeps P1.
- P2 is sent via Electron IPC to the main process.
- The main process forwards P2 to the requesting renderer process.
- P2 ends up in the preload script of that renderer process.
- The preload script forwards P2 into the renderer main script.
- The main script receives P2 and can use it to send messages directly.
-
Other efforts
Change the origin of the renderer
-
Abandon the
file
protocol.// vs/platform/protocol/electron-main/protocolMainService.ts // Block any file:// access defaultSession.protocol.interceptFileProtocol('file', (request, callback) => this.handleFileRequest(request, callback)); ... private handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { const uri = URI.parse(request.url); this.logService.error(`Refused to load resource ${uri.fsPath} from ${Schemas.file}: protocol (original URL: ${request.url})`); return callback({ error: -3 /* ABORTED */ }); }
-
Register the
vscode-file
protocol configured to behave like HTTPS.// src/main.js const { protocol } = require('electron'); protocol.registerSchemesAsPrivileged([ { scheme: 'vscode-webview', privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, allowServiceWorkers: true, } }, { scheme: 'vscode-file', privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true } } ]); // vs/platform/protocol/electron-main/protocolMainService.ts // Register vscode-file:// handler defaultSession.protocol.registerFileProtocol('vscode-file', (request, callback) => this.handleResourceRequest(request, callback));
Replace the Electron webview
element with iframe
Enable renderer process reuse
- Traditionally the renderer process would terminate and restart every time a navigation occurs to another URL.
- Sandboxed renderer processes are kept alive, even when navigating URLs.