From e089ae71ae03b1c74b5c087088000ba8a450a8bf Mon Sep 17 00:00:00 2001 From: Lars Thomas Denstad Date: Wed, 8 Apr 2026 20:08:54 +0200 Subject: [PATCH] fix(macos): persist CEF cookies by using global request context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CefRequestContext::CreateContext with custom cache_path fails in Chrome runtime with "Cannot create profile at path". Use the global request context for persistent partitions instead — it already has a working profile at root_cache_path/Default with proper cookie persistence. Also pass partition option through BrowserWindow to BrowserView so apps can specify partition when creating windows. Co-Authored-By: Claude Opus 4.6 (1M context) --- package/src/bun/core/BrowserWindow.ts | 6 +++ package/src/native/macos/nativeWrapper.mm | 63 ++++++++++------------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/package/src/bun/core/BrowserWindow.ts b/package/src/bun/core/BrowserWindow.ts index 7370127c5..4fa361c61 100644 --- a/package/src/bun/core/BrowserWindow.ts +++ b/package/src/bun/core/BrowserWindow.ts @@ -41,6 +41,8 @@ export type WindowOptionsType = { // Use for untrusted content (remote URLs) to prevent malicious sites from // accessing internal APIs, creating OOPIFs, or communicating with Bun sandbox: boolean; + // CEF partition for session/cookie isolation. Use "persist:" for persistent storage. + partition: string | null; }; const defaultOptions: WindowOptionsType = { @@ -62,6 +64,7 @@ const defaultOptions: WindowOptionsType = { hidden: false, navigationRules: null, sandbox: false, + partition: null, }; export const BrowserWindowMap: { @@ -126,6 +129,7 @@ export class BrowserWindow { navigationRules: string | null = null; // Sandbox mode disables RPC and only allows event emission (for untrusted content) sandbox: boolean = false; + partition: string | null = null; frame: { x: number; y: number; @@ -155,6 +159,7 @@ export class BrowserWindow { this.hidden = options.hidden ?? false; this.navigationRules = options.navigationRules || null; this.sandbox = options.sandbox ?? false; + this.partition = options.partition || null; this.init(options); } @@ -238,6 +243,7 @@ export class BrowserWindow { windowId: this.id, navigationRules: this.navigationRules, sandbox: this.sandbox, + partition: this.partition, startPassthrough: this.passthrough, }); diff --git a/package/src/native/macos/nativeWrapper.mm b/package/src/native/macos/nativeWrapper.mm index 3411da782..47c9b8e13 100644 --- a/package/src/native/macos/nativeWrapper.mm +++ b/package/src/native/macos/nativeWrapper.mm @@ -5788,45 +5788,34 @@ void Cancel() override { CefRefPtr CreateRequestContextForPartition(const char* partitionIdentifier, uint32_t webviewId) { NSLog(@"DEBUG CEF: CreateRequestContextForPartition called for webview %u, partition: %s", webviewId, partitionIdentifier ? partitionIdentifier : "null"); - CefRequestContextSettings settings; - if (!partitionIdentifier || !partitionIdentifier[0]) { - settings.persist_session_cookies = false; - } else { - std::string identifier(partitionIdentifier); - bool isPersistent = identifier.substr(0, 8) == "persist:"; - - if (isPersistent) { - std::string partitionName = identifier.substr(8); - NSString* appSupportPath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject]; - - // Build path with identifier/channel structure to match root_cache_path logic - std::string cachePathStr = buildPartitionPath( - [appSupportPath UTF8String], - g_electrobunIdentifier, - g_electrobunChannel, - "CEF", - partitionName - ); - NSString* cachePath = [NSString stringWithUTF8String:cachePathStr.c_str()]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - if (![fileManager fileExistsAtPath:cachePath]) { - [fileManager createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:nil]; - } - settings.persist_session_cookies = true; - CefString(&settings.cache_path).FromString([cachePath UTF8String]); - } else { - settings.persist_session_cookies = false; - } + + std::string identifier(partitionIdentifier ? partitionIdentifier : ""); + bool isPersistent = identifier.size() >= 8 && identifier.substr(0, 8) == "persist:"; + + // For persistent partitions, use the global request context which already has + // a working profile at root_cache_path/Default with cookie persistence. + // CefRequestContext::CreateContext with custom cache_path fails in Chrome runtime + // with "Cannot create profile at path" — the global context avoids this entirely. + if (isPersistent) { + CefRefPtr context = CefRequestContext::GetGlobalContext(); + NSLog(@"DEBUG CEF: Using global request context for persistent partition '%s'", partitionIdentifier); + + // Register scheme handler factory for views:// protocol + static CefRefPtr schemeFactory = new ElectrobunSchemeHandlerFactory(); + bool registered = context->RegisterSchemeHandlerFactory("views", "", schemeFactory); + NSLog(@"DEBUG CEF: Registered scheme handler factory - success: %s", registered ? "yes" : "no"); + + return context; } + // Non-persistent (ephemeral) partition — create an isolated in-memory context + CefRequestContextSettings settings; + settings.persist_session_cookies = false; CefRefPtr context = CefRequestContext::CreateContext(settings, nullptr); - // Register scheme handler factory for this request context - // Note: Each CefRequestContext needs its own registration - it's not global static CefRefPtr schemeFactory = new ElectrobunSchemeHandlerFactory(); bool registered = context->RegisterSchemeHandlerFactory("views", "", schemeFactory); - NSLog(@"DEBUG CEF: Registered scheme handler factory for partition '%s' - success: %s", - partitionIdentifier ? partitionIdentifier : "(default)", registered ? "yes" : "no"); + NSLog(@"DEBUG CEF: Registered scheme handler factory for ephemeral partition - success: %s", registered ? "yes" : "no"); return context; } @@ -6537,6 +6526,11 @@ - (void)windowDidResignKey:(NSNotification *)notification { +// Forward declaration — ElectrobunWindow interface needed before initWebview +@interface ElectrobunWindow : NSWindow +@property (nonatomic, assign) BOOL backgroundMediaEnabled; +@end + // Global flags set by setNextWebviewFlags, consumed by initWebview static struct { bool startTransparent; @@ -7013,9 +7007,6 @@ - (void)windowDidResignKey:(NSNotification *)notification { } -@interface ElectrobunWindow : NSWindow -@end - @implementation ElectrobunWindow - (BOOL)canBecomeKeyWindow { return YES; } - (BOOL)canBecomeMainWindow { return YES; }