Перейти к основному содержанию

contextBridge

History

Создает безопасный, двунаправленный, синхронный мост через изолированные контексты

Process: Renderer

Пример предоставления 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". You can read more about context isolation and what it affects in the security docs.

Методы

Модуль 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's contextIsolation 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')
}
})