contextBridge
History
| Version(s) | Changes |
|---|---|
None |
|
在隔离上下文之间创建一个安全的、双向的、同步的桥梁
进程: 渲染器
🌐 Process: Renderer
下面给出了从独立的预加载脚本向渲染器公开 API 的示例:
🌐 An example of exposing an API to a renderer from an isolated preload script is given below:
// Preload (Isolated World)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Renderer (Main World)
window.electron.doThing()
词汇表
🌐 Glossary
主世界
🌐 Main World
“主世界”是你的主要渲染器代码运行的 JavaScript 上下文。默认情况下,你在渲染器中加载的页面会在这个世界中执行代码。
🌐 The "Main World" is the JavaScript context that your main renderer code runs in. By default, the page you load in your renderer executes code in this world.
孤立的世界
🌐 Isolated World
当在你的 webPreferences 中启用 contextIsolation 时(自 Electron 12.0.0 起这是默认行为),你的 preload 脚本会在“隔离环境”中运行。你可以在安全性 文档中了解更多关于上下文隔离及其影响的信息。
🌐 When contextIsolation is enabled in your webPreferences (this is the default behavior since Electron 12.0.0), your preload scripts run in an
"Isolated World". You can read more about context isolation and what it affects in the
security docs.
方法
🌐 Methods
contextBridge 模块具有以下方法:
🌐 The contextBridge module has the following methods:
contextBridge.exposeInMainWorld(apiKey, api)
apiKeystring - 用于将 API 注入window的密钥。该 API 将可以通过window[apiKey]访问。apiany - 你的 API,关于此 API 可以做什么以及它如何工作的更多信息如下所示。
contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)
worldIdInteger - 要将 API 注入的世界的 ID。0是默认世界,999是 Electron 的contextIsolation功能使用的世界。使用 999 会将对象暴露给预加载上下文。我们建议在创建隔离世界时使用 1000 以上的值。apiKeystring - 用于将 API 注入window的密钥。该 API 将可以通过window[apiKey]访问。apiany - 你的 API,关于此 API 可以做什么以及它如何工作的更多信息如下所示。
contextBridge.executeInMainWorld(executionScript) 实验性
🌐 contextBridge.executeInMainWorld(executionScript) Experimental
executionScript对象func(...args: any[]) => any - 一个用于执行的 JavaScript 函数。该函数将被序列化,这意味着任何绑定的参数和执行上下文都将丢失。argsany[](optional)- 要传递给提供函数的参数数组。这些参数将根据支持类型表在不同环境间复制。
返回 any - 从主世界执行函数后得到的结果值的副本。
参见表格 了解值在不同世界之间是如何复制的。
🌐 Returns any - A copy of the resulting value from executing the function in the main world.
Refer to the table on how values are copied between worlds.
用法
🌐 Usage
应用接口
🌐 API
提供给 exposeInMainWorld 的 api 必须是 Function、string、number、Array、boolean,或者是一个键为字符串、值为 Function、string、number、Array、boolean 或另一个满足相同条件的嵌套对象的对象。
🌐 The api provided to exposeInMainWorld must be a Function, string, number, Array, boolean, or an object
whose keys are strings and values are a Function, string, number, Array, boolean, or another nested object that meets the same conditions.
Function 值会被代理到其他上下文,所有其他值都会被复制并冻结。通过 API 发送的任何数据/原始类型都会变为不可变,并且桥接两端的更新不会影响另一端的更新。
复杂 API 的示例如下所示:
🌐 An example of a complex API is shown below:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing'),
myPromises: [Promise.resolve(), Promise.reject(new Error('whoops'))],
anAsyncFunction: async () => 123,
data: {
myFlags: ['a', 'b', 'c'],
bootTime: 1234
},
nestedAPI: {
evenDeeper: {
youCanDoThisAsMuchAsYouWant: {
fn: () => ({
returnData: 123
})
}
}
}
}
)
下面给出了 exposeInIsolatedWorld 的一个示例:
🌐 An example of exposeInIsolatedWorld is shown below:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInIsolatedWorld(
1004,
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Renderer (In isolated world id1004)
window.electron.doThing()
API 函数
🌐 API Functions
通过 contextBridge 绑定的 Function 值会通过 Electron 进行代理,以确保上下文保持隔离。这会导致我们在下面概述的一些关键限制。
参数/错误/返回类型支持
🌐 Parameter / Error / Return Type support
由于在通过桥发送时参数、错误和返回值是复制的,因此只有某些类型可以使用。 从高层次来看,如果你想使用的类型可以被序列化和反序列化为相同的对象,那么它是可行的。下面包含了一张类型支持的表,以便完整说明:
🌐 Because parameters, errors and return values are copied when they are sent over the bridge, there are only certain types that can be used. At a high level, if the type you want to use can be serialized and deserialized into the same object it will work. A table of type support has been included below for completeness:
| 类型 | 复杂性 | 参数支持 | 返回值支持 | 限制 |
|---|---|---|---|---|
string | 简单 | ✅ | ✅ | 不适用 |
number | 简单 | ✅ | ✅ | 不适用 |
boolean | 简单 | ✅ | ✅ | 不适用 |
Object | 复杂 | ✅ | ✅ | 此表中的键必须仅使用“简单”类型支持。值必须在此表中支持。原型修改将被丢弃。发送自定义类时会复制值,但不会复制原型。 |
Array | 复杂 | ✅ | ✅ | 与 Object 类型相同的限制 |
Error | 复杂 | ✅ | ✅ | 抛出的错误也会被复制,这可能导致错误的消息和堆栈跟踪由于在不同的上下文中抛出而略有变化,并且 Error 对象上的任何自定义属性 将会丢失 |
Promise | 复杂 | ✅ | ✅ | 不适用 |
Function | 复杂 | ✅ | ✅ | 原型修改会被丢弃。发送类或构造函数将不起作用。 |
| 可克隆类型 | 简单 | ✅ | ✅ | 参见关于可克隆类型的链接文档 |
Element | 复杂 | ✅ | ✅ | 原型修改会被丢弃。发送自定义元素将不起作用。 |
Blob | 复杂 | ✅ | ✅ | 不适用 |
VideoFrame | 复杂 | ✅ | ✅ | 不适用 |
Symbol | 不适用 | ❌ | ❌ | 符号不能跨上下文复制,因此被丢弃 |
如果你关心的类型不在上表中,则可能不支持。
🌐 If the type you care about is not in the above table, it is probably not supported.
公开 ipcRenderer
🌐 Exposing ipcRenderer
尝试将整个 ipcRenderer 模块作为对象通过 contextBridge 发送会导致桥接接收端得到一个空对象。完整地通过 ipcRenderer 发送可能会让任何代码发送任意消息,这是一种安全隐患。要通过 ipcRenderer 进行交互,可以提供如下安全封装器:
🌐 Attempting to send the entire ipcRenderer module as an object over the contextBridge will result in
an empty object on the receiving side of the bridge. Sending over ipcRenderer in full can let any
code send any message, which is a security footgun. To interact through ipcRenderer, provide a safe wrapper
like below:
// Preload (Isolated World)
contextBridge.exposeInMainWorld('electron', {
onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args))
})
// Renderer (Main World)
window.electron.onMyEventName(data => { /* ... */ })
公开节点全局符号
🌐 Exposing Node Global Symbols
contextBridge 可以被预加载脚本使用,从而让你的渲染进程访问 Node API。
上面描述的支持类型表同样适用于你通过 contextBridge 暴露的 Node API。
请注意,许多 Node API 会访问本地系统资源。
对哪些全局对象和 API 暴露给不受信任的远程内容要非常谨慎。
🌐 The contextBridge can be used by the preload script to give your renderer access to Node APIs.
The table of supported types described above also applies to Node APIs that you expose through contextBridge.
Please note that many Node APIs grant access to local system resources.
Be very cautious about which globals and APIs you expose to untrusted remote content.
const { contextBridge } = require('electron')
const crypto = require('node:crypto')
contextBridge.exposeInMainWorld('nodeCrypto', {
sha256sum (data) {
const hash = crypto.createHash('sha256')
hash.update(data)
return hash.digest('hex')
}
})