Having two isolated (in terms of history/cookies/localstorage) BrowserViews in the same BrowserWindow with Electron
So, I managed to get this working but in a very, very, roundabout way. Effectively session hijacking your own session, saving and loading it on app close/open. Code below with some comments, prefaced with some useful links. This worked when running as dev, and when running with a build application.
You may need to look into possible security issues here with storing cookies locally like this.
The only thing I have not tackled in this answer is:
keep history ... after restart of the Electron app
- Electron-Json-Storage Package - We use this to store/retrieve cookies. The default location for storage is
C:\Users\%user%\AppData\Roaming\%appname%\storage
. - Electron Cookies documentation
- Electron Session documentation - Notably the
session.fromPartition
docs.
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); })); });}