我想使用BrowserView
在Electron中加载外部网页。它与BrowserWindow
具有几乎相同的API。
const currentWindow = remote.getCurrentWindow();
const view = new remote.BrowserView({
webPreferences: {
// contextIsolation: true,
partition: 'my-view-partition',
enableRemoteModule: false,
nodeIntegration: false,
preload: `${__dirname}/preload.js`,
sandbox: true,
},
});
view.setAutoResize({ width: true, height: true });
view.webContents.loadURL('http://localhost:3000');
在我的preload.js
文件中,我只是将变量附加到全局对象。
process.once('loaded', () => {
global.baz = 'qux';
});
在localhost:3000
上运行的应用程序是一个React应用程序,它引用的值是这样的:
const sharedString = global.baz || 'Not found';
问题是我创建contextIsolation: true
时必须注释掉设置BrowserView
。这暴露了一个安全漏洞。
是否有可能(从电子到网页的一种方式)将变量注入BrowserView
(或BrowserWindow
)中,同时仍使用contextIsolation
使电子环境与任何环境隔离加载的内容对全局环境所做的更改?
更新: 一种可能的方法可能是拦截网络协议,但是我不确定这个?
app.on('ready', () => {
const { protocol } = session.fromPartition('my-partition')
protocol.interceptBufferProtocol('https', (req, callback) => {
if (req.uploadData) {
// How to handle file uploads?
callback()
return
}
// This is electron.net, docs: https://electronjs.org/docs/api/net
net
.request(req)
.on('response', (res) => {
const chunks = []
res.on('data', (chunk) => {
chunks.push(Buffer.from(chunk))
})
res.on('end', () => {
const blob = Buffer.concat(chunks)
const type = res.headers['content-type'] || []
if (type.includes('text/html') && blob.includes('<head>')) {
// FIXME?
const pos = blob.indexOf('<head>')
// inject contains the Buffer with the injected HTML script
callback(Buffer.concat([blob.slice(0, pos), inject, blob.slice(pos)]))
} else {
callback(blob)
}
})
})
.on('error', (err) => {
console.error('error', err)
callback()
})
.end()
})
})
答案 0 :(得分:4)
进行一些挖掘之后,我发现了一些对Electron的请求,这些请求详细说明了您遇到的问题。 first描述了一个与您描述的问题非常相似的可重现示例。
预期行为
https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content 预加载脚本应该能够使用contextIsolation:true将任何东西附加到窗口或文档上。
实际行为
preload.js中附加到窗口的所有内容在渲染器中都消失了。
似乎最后的评论解释了预期的行为不再有效
实际上直到最近,孤立世界的PR才改变了行为。
second让用户建议他们发现的解决方案:
经过几天的研究并摆弄了IPC,我得出结论,最好的方法是走协议路线。
我查看了BrowserWindow
和BrowserView
的文档以及显示您想要的行为的example的文档,但是这些PR提示这不再可行(沿着这条路线)。
在文档中,从webContents
获得的view.webContents
对象具有功能executeJavaScript,因此您可以尝试以下操作来设置全局变量。
...
view.setAutoResize({ width: true, height: true });
view.webContents.loadURL('http://localhost:3000');
view.webContents.executeJavaScript("global.baz = 'qux';");
...
答案 1 :(得分:2)
接受的答案已过时,使用 contextBridge 务必使用 sendToHost() 而不是 send()
// Preload (Isolated World)
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.sendToHost('do-a-thing')
}
)
// Renderer (Main World)
window.electron.doThing()
答案 2 :(得分:0)
因此,Zapparatus建议的executeJavaScript
最终成为解决方案的一部分。
这就是renderer.js
中的情况。
view.webContents.executeJavaScript(`
window.communicator = {
request: function(data) {
const url = 'prefix://?data=' + encodeURIComponent(JSON.stringify(data))
const req = new XMLHttpRequest()
req.open('GET', url)
req.send();
},
receive: function(data) {
alert('got: ' + JSON.stringify(data))
}
};
`)
const setContent = data => view.webContents.executeJavaScript(
`window.communicator.receive(${JSON.stringify(data)})`
)
ipcRenderer.on('communicator', (event, message) => {
setContent(`Hello, ${message}!`)
})
我们最终建立了一个自定义协议,类似于here的完成方式。在您的main.js
文件中,设置以下内容:
const { app, session, protocol } = require('electron')
const { appWindows } = require('./main/app-run')
const { URL } = require('url')
protocol.registerSchemesAsPrivileged([
{
scheme: 'prefix',
privileges: {
bypassCSP: true, // ignore CSP, we won't need to patch CSP
secure: true // allow requests from https context
}
}
])
app.on('ready', () => {
const sess = session.fromPartition('my-view-partition')
// https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content
sess.setPermissionRequestHandler((webContents, permission, callback) => {
// Denies the permissions request
const decision = false
return callback(decision)
})
sess.protocol.registerStringProtocol('prefix', (req, callback) => {
const url = new URL(req.url)
try {
const data = JSON.parse(url.searchParams.get('data'))
appWindows.main.webContents.send('prefix', data)
} catch (e) {
console.error('Could not parse prefix request!')
}
const response = {
mimeType: 'text/plain',
data: 'ok'
}
callback(response)
})
})
不需要preload.js
或postMessage
。