From 88681b1fdba7464c6646bd530cdeee653978a9cc Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 20 Mar 2026 16:50:03 +0800 Subject: [PATCH] Fix Context's call_arena The Context's call_arena should be based on the source, e.g. the IsolateWorld or the Page, not always the page. There's no rule that says all Contexts have to be a subset of the Page, and thus some might live longer and by doing so outlive the page_arena. Also, on context cleanup, isolate worlds now cleanup their identity. --- src/ArenaPool.zig | 1 - src/browser/Page.zig | 1 + src/browser/js/Context.zig | 4 +++- src/browser/js/Env.zig | 3 ++- src/cdp/cdp.zig | 9 +++++++++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ArenaPool.zig b/src/ArenaPool.zig index 998838df..2e3f25a4 100644 --- a/src/ArenaPool.zig +++ b/src/ArenaPool.zig @@ -112,7 +112,6 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator { } gop.value_ptr.* += 1; } - return entry.arena.allocator(); } diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 148654a0..eef70bbc 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -305,6 +305,7 @@ pub fn init(self: *Page, frame_id: u32, session: *Session, parent: ?*Page) !void self.js = try browser.env.createContext(self, .{ .identity = &session.identity, .identity_arena = session.page_arena, + .call_arena = self.call_arena, }); errdefer self.js.deinit(); diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 2b861e7a..b46ec11d 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -63,7 +63,9 @@ templates: []*const v8.FunctionTemplate, // Arena for the lifetime of the context arena: Allocator, -// The page.call_arena +// The call_arena for this context. For main world contexts this is +// page.call_arena. For isolated world contexts this is a separate arena +// owned by the IsolatedWorld. call_arena: Allocator, // Because calls can be nested (i.e.a function calling a callback), diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index d248a411..9b3a1b4c 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -258,6 +258,7 @@ pub fn deinit(self: *Env) void { pub const ContextParams = struct { identity: *js.Identity, identity_arena: Allocator, + call_arena: Allocator, debug_name: []const u8 = "Context", }; @@ -325,7 +326,7 @@ pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context { .arena = context_arena, .handle = context_global, .templates = self.templates, - .call_arena = page.call_arena, + .call_arena = params.call_arena, .microtask_queue = microtask_queue, .script_manager = &page._script_manager, .scheduler = .init(context_arena), diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index d6b35d83..d3a2f64e 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -492,9 +492,13 @@ pub fn BrowserContext(comptime CDP_T: type) type { const arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld" }); errdefer browser.arena_pool.release(arena); + const call_arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld.call_arena" }); + errdefer browser.arena_pool.release(call_arena); + const world = try arena.create(IsolatedWorld); world.* = .{ .arena = arena, + .call_arena = call_arena, .context = null, .browser = browser, .name = try arena.dupe(u8, world_name), @@ -745,6 +749,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { /// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts. const IsolatedWorld = struct { arena: Allocator, + call_arena: Allocator, browser: *Browser, name: []const u8, context: ?*js.Context = null, @@ -757,6 +762,7 @@ const IsolatedWorld = struct { pub fn deinit(self: *IsolatedWorld) void { self.removeContext() catch {}; self.identity.deinit(); + self.browser.arena_pool.release(self.call_arena); self.browser.arena_pool.release(self.arena); } @@ -764,6 +770,8 @@ const IsolatedWorld = struct { const ctx = self.context orelse return error.NoIsolatedContextToRemove; self.browser.env.destroyContext(ctx); self.context = null; + self.identity.deinit(); + self.identity = .{}; } // The isolate world must share at least some of the state with the related page, specifically the DocumentHTML @@ -776,6 +784,7 @@ const IsolatedWorld = struct { const ctx = try self.browser.env.createContext(page, .{ .identity = &self.identity, .identity_arena = self.arena, + .call_arena = self.call_arena, .debug_name = "IsolatedContext", }); self.context = ctx;