contextBridge
History
Version(s) | Changes |
---|---|
None |
|
Создает безопасный, двунаправленный, синхронный мост через изолированные контексты
Процесс: Графический
Пример предоставления API-интерфейса средству визуализации из изолированного сценария предварительной загрузки приведен ниже:
// Предварительная загрузка (Isolated World/Изолированный Мир)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
// Рендер (Main World/Основной Мир)
window.electron.doThing()
Глоссарий
Main World / Основной Мир
"Main World" - это контекст javascript, в котором запускается ваш основной код рендера. По умолчанию загружаемая вами страница выполняет код в этом контексте.
Isolated World / Изолированный Мир
Когда contextIsolation
включен в вашем webPreferences
(Это стандартное поведение в Electron 12.0.0) ваши preload
скрипты выполняются в "Isolated World". Вы можете прочитать больше о контекстной изоляции и на что это влияет в документации BrowserWindow.
Методы
Модуль contextBridge
имеет следующие методы:
contextBridge.exposeInMainWorld(apiKey, api)
apiKey
string - Ключ для вставки API вwindow
. API будет доступен вwindow[apiKey]
.api
Record - Ваш объект API, более подробная информация о том, что это и как он будет работать, доступна ниже.
contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)
worldId
Integer - The ID of the world to inject the API into.0
is the default world,999
is the world used by Electron'scontextIsolation
feature. Using 999 would expose the object for preload context. We recommend using 1000+ while creating isolated world.apiKey
string - Ключ для вставки API вwindow
. API будет доступен вwindow[apiKey]
.api
Record - Ваш объект API, более подробная информация о том, что это и как он будет работать, доступна ниже.
Usage
API
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 показан ниже:
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
})
}
}
}
}
)
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
Значения Function
, которые вы связываете через contextBridge
, передаются через Electron, чтобы гарантировать, что контексты остаются изолированными. Это приводит к некоторым ключевым ограничениям, которые мы описали ниже.
Параметр / Ошибка / Поддержка возвращаемого типа
Поскольку параметры, ошибки и возвращаемые значения скопированы при отправке через мост, существуют только определенные типы, которые могут быть использованы. На высоком уровне, если тип, который вы хотите использовать, может быть сериализован и десериализован в один и тот же объект, то он будет работать. Ниже для полноты изложения приводится таблица поддержки типов:
Тип | Сложность | Поддержка параметров | Возврат значения поддержки | Ограничения |
---|---|---|---|---|
string | Простой | ✅ | ✅ | Нет |
number | Простой | ✅ | ✅ | Нет |
boolean | Простой | ✅ | ✅ | Нет |
Object | Сложный | ✅ | ✅ | Ключи должны поддерживаться «Простыми» типами в этой таблице. Значения должны поддерживаться в этой таблице. Модификации прототипа отбрасываются. Отправка пользовательских классов будет копировать значения, но не прототип. |
Array | Сложный | ✅ | ✅ | Те же ограничения, что и в типе Object |
Error | Сложный | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context, and any custom properties on the Error object will be lost |
Promise | Сложный | ✅ | ✅ | Нет |
Function | Сложный | ✅ | ✅ | Модификации прототипа отбрасываются. Отправка классов или конструкторов не будет работать. |
Cloneable Types | Простой | ✅ | ✅ | Смотрите связанный документ по клонируемым типам |
Element | Сложный | ✅ | ✅ | Модификации прототипа отбрасываются. Отправка пользовательских элементов не будет работать. |
Blob | Сложный | ✅ | ✅ | Нет |
Symbol | Нет | ❌ | ❌ | Символы не могут быть скопированы в разных контекстах, поэтому они отбрасываются |
Если тип, о котором вы хотите использовать, не находится в приведенной выше таблице, то он, вероятно, не поддерживается.
Exposing 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
может использоваться скриптом preload для предоставления вашему устройству доступа к узлам API. Таблица поддерживаемых типов, описанная выше, также применяется к Node API, которые вы используете через contextBridge
. Пожалуйста, обратите внимание, что многие Node API предоставляют доступ к локальным системным ресурсам. Будьте очень осторожны относительно того, какие глобальные переменные и API вы предоставляете в ненадежном удаленном контенте.
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')
}
})