javascript - 在具有 Electron 的同一个 BrowserWindow 中有两个隔离的(就历史/cookies/本地存储而言)BrowserViews
问题描述
假设我有两个BrowserView
相同的 UI 按钮,允许用户在显示或BrowserWindow
之间切换(就像 Firefox、Chrome 等浏览器中的“标签”系统,它允许您在不同页面之间切换):bv1
bv2
browserWindow = new BrowserWindow({ width: 1200, height: 600 });
let bv1 = new BrowserView({ webPreferences: { nodeIntegration: false }});
bv1.setBounds({ x: 0, y: 0, width: 1200, height: 600 });
bv1.webContents.loadURL('https://www.twitter.com');
let bv2 = new BrowserView({ webPreferences: { nodeIntegration: false }});
bv2.setBounds({ x: 0, y: 0, width: 1200, height: 600 });
bv2.webContents.loadURL('https://www.twitter.com');
browserWindow.setBrowserView(bv1);
当按下按钮(如浏览器中的“选项卡”)时:
browserWindow.setBrowserView(bv2);
我注意到这两个BrowserView
:
共享相同的cookies/localStorage(我不想要!),即如果第一个连接到一个帐户,第二个也将连接到同一个帐户
重新启动 Electron 应用程序后保留历史记录和 cookie(这很好,确实需要!)
问题:如何让这两者在 cookie/localStorage/history 方面BrowserView
完全隔离(因此bv1
可以连接到一个 Twitter 帐户和bv2
另一个帐户)?
解决方案
所以,我设法让这个工作,但以一种非常、非常、迂回的方式。有效地劫持您自己的会话,在应用程序关闭/打开时保存和加载它。下面的代码带有一些注释,前面有一些有用的链接。这在以开发人员身份运行以及与构建应用程序一起运行时有效。
您可能需要在此处查看可能的安全问题,并像这样在本地存储 cookie。
我在这个答案中唯一没有解决的是:
保留历史记录...重启 Electron 应用程序后
- Electron-Json-Storage Package - 我们使用它来存储/检索 cookie。存储的默认位置是
C:\Users\%user%\AppData\Roaming\%appname%\storage
. - 电子 Cookie 文档
- Electron Session 文档- 特别是
session.fromPartition
文档。
const { app, BrowserWindow, BrowserView, globalShortcut, session } = require('electron');
const eJSONStorage = require('electron-json-storage');
// Our two different sesions, views, and base URL for our 'tabs'.
let bv1Session, bv2Session = session;
let bv1, bv2 = BrowserView;
const appTabUrl = 'https://www.twitter.com';
app.on('ready', () => {
const width = 1200; const height = 600;
let b1Active = true;
// Our browser window
browserWindow = new BrowserWindow({
width: width,
height: height,
});
// Our first browser window with it's own session instance.
bv1Session = session.fromPartition('persist:bv1Session', { cache: true });
bv1 = createBrowserView(appTabUrl, bv1Session, width, height);
loadCookieState('view1Cookies', bv1Session);
// Our second browser window with it's own session instance.
bv2Session = session.fromPartition('persist:bv2Session', { cache: true });
bv2 = createBrowserView(appTabUrl, bv2Session, width, height);
loadCookieState('view2Cookies', bv2Session);
// Our initial setting of the browserview
browserWindow.setBrowserView(bv1);
// Our shortcut listener and basic switch mechanic
// Set to [CTRL + /] for windows or [CMD + /] for OSX
globalShortcut.register('CommandOrControl+/', () => {
b1Active ? browserWindow.setBrowserView(bv2) : browserWindow.setBrowserView(bv1);
b1Active = !b1Active
});
});
// When the app closes, exit gracefully.
// Unregister keypress listener, save cookie states, exit the app.
app.on('window-all-closed', () => {
globalShortcut.unregisterAll();
saveCookieState('view1Cookies', bv1Session);
saveCookieState('view2Cookies', bv2Session);
app.quit();
})
// Helper method to generate a browser view.
function createBrowserView(url, session, width, height) {
let browserView = new BrowserView({
webPreferences: {
nodeIntegration: false,
nodeIntegrationInWorker: false,
session: session
}
});
browserView.setBounds({ x: 0, y: 0, width: width, height: height });
browserView.webContents.loadURL(url);
return browserView;
}
// Method that takes a session name, and our current session to save its state.
function saveCookieState(sessionName, currentSession) {
currentSession.cookies.get({}, (_, cookies) => {
cookies.forEach(cookie => {
// URL is a required paramater, take it from the domain with a little parsing.
// Twitter always uses HTTPS otherwise, we would need to check for http vs https too.
const cDomain = !cookie.domain.startsWith('.') ? `.${cookie.domain}` : cookie.domain;
cookie.url = `https://www${cDomain}`
});
// Save the set of cookies against the session name.
eJSONStorage.set(sessionName, cookies, err => {
if (err) {
throw err;
}
});
});
}
// Method that loads a session based on its name, into a session created by us.
function loadCookieState(sessionName, currentSession) {
eJSONStorage.get(sessionName, (error, cookieData) => {
// Check for empty object returned, this means no saved sessions.
if (Object.entries(cookieData).length === 0) {
return;
}
if (error) {
throw error;
}
// If we have saved sessions and no errors, load the sessions.
cookieData.forEach(cookie => currentSession.cookies.set(cookie, error => {
if (error) console.error(error);
}));
});
}
推荐阅读
- rust - 为什么线程::睡眠的两次执行的期货::加入不并行运行?
- c++ - 如何在 C++ 中使用线程实现优先级抢占式调度(类似于中断)
- ggplotly - 保存带有指向 HTML 依赖项的链接的 HTMLWidget,而不是嵌入它们
- php - 抛出 Deprecated 警告的代码可以正常工作,但更新后的代码不能正常工作?
- python - Pandas - 删除一些行后如何通过真实索引进行选择?
- r - 如何计算来自不同列的两个连续行之间的时间差?
- react-native - 动画 getLayout() 函数在 react-native 中不起作用
- javascript - Google App Script中的正则表达式在结果后出现大量垃圾
- javascript - 如何使用 React 测试库测试支持 Redux 的组件?
- javascript - Gatsby\React - window.location.href 不工作