mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-23 18:14:53 +02:00
[eric] browser: Chrome-style suspend capsules (sessionStorage+scroll survive), last-visible-frame snapshots, 20s working grace
This commit is contained in:
@@ -2799,6 +2799,31 @@ async function readPartitionCookies(domain) {
|
||||
}
|
||||
ipcMain.handle('get-partition-cookies', (_e, domain) => readPartitionCookies(domain));
|
||||
|
||||
// Suspend/resume state capsules: the app renderer stages a resumed webview's sessionStorage snapshot here (keyed by that guest's webContents id) right before loadURL; the guest preload sync-takes it at document-start with an origin match, so page scripts see restored state and logins survive suspension. In-memory only, single-shot, short TTL; a guest can only ever take its OWN capsule.
|
||||
const pendingSessionCapsules = new Map();
|
||||
const SESSION_CAPSULE_TTL_MS = 2 * 60 * 1000;
|
||||
ipcMain.on('browser-capsule-set', (event, wcId, capsule) => {
|
||||
// Only the app window may stage capsules; a compromised guest must not be able to seed storage into another guest.
|
||||
if (!mainWindow || event.sender !== mainWindow.webContents) return;
|
||||
if (typeof wcId !== 'number' || !capsule || typeof capsule.origin !== 'string' || typeof capsule.ss !== 'object') return;
|
||||
pendingSessionCapsules.set(wcId, { capsule, expiresAt: Date.now() + SESSION_CAPSULE_TTL_MS });
|
||||
});
|
||||
ipcMain.on('browser-capsule-take', (event, origin) => {
|
||||
const entry = pendingSessionCapsules.get(event.sender.id);
|
||||
if (!entry || entry.expiresAt < Date.now()) {
|
||||
if (entry) pendingSessionCapsules.delete(event.sender.id);
|
||||
event.returnValue = null;
|
||||
return;
|
||||
}
|
||||
// Origin-gated take: about:blank and cross-origin redirects leave the capsule staged for the real page (until TTL).
|
||||
if (entry.capsule.origin !== origin) {
|
||||
event.returnValue = null;
|
||||
return;
|
||||
}
|
||||
pendingSessionCapsules.delete(event.sender.id);
|
||||
event.returnValue = entry.capsule;
|
||||
});
|
||||
|
||||
// The renderer relays cookie reads for the session-borrow bridge, but macOS throttles it when the
|
||||
// window is backgrounded, so those reads intermittently time out. Main never throttles: hold our own
|
||||
// socket to the backend and answer get_session_cookies here. Cookie reads only; the renderer still
|
||||
|
||||
@@ -62,6 +62,8 @@ contextBridge.exposeInMainWorld('openswarm', {
|
||||
connectSlack: () => ipcRenderer.invoke('connect-slack'),
|
||||
// Hands a vetted social platform's partition cookies to its session-backed MCP shim (allowlisted domains only, gated again in the main process).
|
||||
getPartitionCookies: (domain) => ipcRenderer.invoke('get-partition-cookies', domain),
|
||||
// Suspend/resume state capsule: stages a resumed webview's sessionStorage snapshot in main (keyed by webContents id, short TTL) so the guest preload can sync-take it at document-start. Fire-and-forget; main validates the sender.
|
||||
setSessionCapsule: (wcId, capsule) => ipcRenderer.send('browser-capsule-set', wcId, capsule),
|
||||
sendCdpCommand: (wcId, method, params, sessionId) => ipcRenderer.invoke('send-cdp-command', wcId, method, params, sessionId),
|
||||
cdpDetachClean: (wcId) => ipcRenderer.invoke('cdp-detach-clean', wcId),
|
||||
cdpCacheSet: (wcId, indexMap) => ipcRenderer.invoke('cdp-cache-set', wcId, indexMap),
|
||||
|
||||
@@ -10,6 +10,23 @@
|
||||
// this webview. Surfaces via main.js's console-message listener.
|
||||
try { console.warn('[openswarm:webview-preload] loaded for', window.location.href); } catch (_) {}
|
||||
|
||||
// Chrome-style suspend/resume: sync-take a pending sessionStorage capsule (staged by the host right before this load) at document-start, so page scripts wake to their old state and logins survive suspension. Origin-matched in main; null for every ordinary navigation.
|
||||
(function restoreSessionCapsule() {
|
||||
try {
|
||||
const { ipcRenderer } = require('electron');
|
||||
const cap = ipcRenderer.sendSync('browser-capsule-take', window.location.origin);
|
||||
if (!cap || !cap.ss) return;
|
||||
for (const k of Object.keys(cap.ss)) {
|
||||
try { sessionStorage.setItem(k, cap.ss[k]); } catch (_) {}
|
||||
}
|
||||
if (cap.sx || cap.sy) {
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => { try { window.scrollTo(cap.sx, cap.sy); } catch (_) {} }, 80);
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
})();
|
||||
|
||||
// Hide webdriver flag
|
||||
Object.defineProperty(navigator, 'webdriver', {
|
||||
get: () => false,
|
||||
|
||||
Reference in New Issue
Block a user