Issue
This Content is from Stack Overflow. Question asked by coinfaces
I’m new to electron, a piece of code in the electron tutorial code puzzles me, it should be simple, but I did not find any documentation or article that explains why it’s possible.
With context isolation enabled, it is possible to update DOM by setting innerText
property of an element.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
It seems impossible to me because Electron documentation says
parameters, errors and return values are copied when they are sent over the bridge
To find out why, I tried to expose a function setElementText
into the “main world”.
// preload.js
const {contextBridge} = require('electron')
// ...
contextBridge.exposeInMainWorld('bridgeTest', {
setElementText: (element, text) => {
window.abc = 123 // executed in isolated world, won't work
element.innerText = text
}
})
In render.js, call exposed function setElementText
.
Execution of this function is proxied so that the body of the function is executed in the “isolated world”, I know it because trying to set window.abc
, but later in devtools of the window, console.log(window.abc)
prints undefined
.
Also, if a normal object is passed to setElementText
, its innerText
property remain unchanged, a similar function setElementText2
defined in the “main world” can change innerText
property of the same object.
const setElementText2 = (element, text) => {
element.innerText = text
}
window.addEventListener('DOMContentLoaded', () => {
window.def = 456 // executed in the main world, works
const o = {innerText: 'xyz'}
window.bridgeTest.setElementText(o, 'xxyyzz')
console.log(o.innerText) // xyz
setElementText2(o, 'aabbcc')
console.log(o.innerText) // aabbcc
})
If a DOM element is passed to the exposed setElementText
, its innerText
property does change, so does the DOM content
window.addEventListener('DOMContentLoaded', () => {
window.def = 456 // executed in main world, works
const helloElement = document.getElementById('hello')
window.bridgeTest.setElementText(
helloElement, 'Hello World')
console.log(helloElement.innerText) // Hello World
})
All source code:
main.js
// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')
function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
preload.js
const {contextBridge} = require('electron')
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
contextBridge.exposeInMainWorld('bridgeTest', {
setElementText: (element, text) => {
window.abc = 123 // executed in isolated world, won't work
element.innerText = text
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<link href="./styles.css" rel="stylesheet">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
<p id="hello">abcdefg</p>
<!-- You can also require other files to run in this process -->
<script src="./renderer.js"></script>
</body>
</html>
render.js
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.
const setElementText2 = (element, text) => {
element.innerText = text
}
window.addEventListener('DOMContentLoaded', () => {
const helloElement = document.getElementById('hello')
window.def = 456 // executed in main world, works
window.bridgeTest.setElementText(
helloElement, 'Hello World')
console.log(helloElement.innerText) // Hello World
const o = {innerText: 'xyz'}
window.bridgeTest.setElementText(o, 'xxyyzz')
console.log(o.innerText) // xyz
setElementText2(o, 'aabbcc')
console.log(o.innerText) // aabbcc
})
style.css
/* styles.css */
/* Add styles here to customize the appearance of your app */
Solution
This question is not yet answered, be the first one who answer using the comment. Later the confirmed answer will be published as the solution.
This Question and Answer are collected from stackoverflow and tested by JTuto community, is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.