From 94fe34bd10fcc570ed124e814969fbdadf5f1c55 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 9 Sep 2025 17:06:58 +0200 Subject: [PATCH] cdp: multiple isolated worlds --- src/cdp/cdp.zig | 42 +++++++++++++++++--------------------- src/cdp/domains/dom.zig | 13 ++++++++---- src/cdp/domains/page.zig | 14 +++++-------- src/cdp/domains/target.zig | 4 ++-- 4 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 6dec217b..3373d51d 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -331,7 +331,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { node_search_list: Node.Search.List, inspector: Inspector, - isolated_world: ?IsolatedWorld, + isolated_worlds: std.ArrayListUnmanaged(IsolatedWorld), http_proxy_changed: bool = false, @@ -375,7 +375,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { .page_life_cycle_events = false, // TODO; Target based value .node_registry = registry, .node_search_list = undefined, - .isolated_world = null, + .isolated_worlds = .empty, .inspector = inspector, .notification_arena = cdp.notification_arena.allocator(), .intercept_state = try InterceptState.init(allocator), @@ -404,9 +404,10 @@ pub fn BrowserContext(comptime CDP_T: type) type { // so we need to shutdown the page one first. self.cdp.browser.closeSession(); - if (self.isolated_world) |*world| { + for (self.isolated_worlds.items) |*world| { world.deinit(); } + self.isolated_worlds.clearRetainingCapacity(); self.node_registry.deinit(); self.node_search_list.deinit(); self.cdp.browser.notification.unregisterAll(self); @@ -427,32 +428,19 @@ pub fn BrowserContext(comptime CDP_T: type) type { } pub fn createIsolatedWorld(self: *Self, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld { - if (self.isolated_world != null) { - // if the two world have different names, be safe and return an - // error. - if (std.mem.eql(u8, self.isolated_world.?.name, world_name) == false) { - return error.CurrentlyOnly1IsolatedWorldSupported; - } - - // If the two worlds have the same name, reuse the existing one - // but send a warning. - log.warn(.cdp, "not implemented", .{ - .feature = "createIsolatedWorld: Not implemented second isolated world creation", - .info = "reuse existing isolated world with the same name", - .world_name = world_name, - }); - return &self.isolated_world.?; - } - var executor = try self.cdp.browser.env.newExecutionWorld(); errdefer executor.deinit(); - self.isolated_world = .{ - .name = try self.arena.dupe(u8, world_name), + const owned_name = try self.arena.dupe(u8, world_name); + const world = try self.isolated_worlds.addOne(self.arena); + + world.* = .{ + .name = owned_name, .executor = executor, .grant_universal_access = grant_universal_access, }; - return &self.isolated_world.?; + + return world; } pub fn nodeWriter(self: *Self, root: *const Node, opts: Node.Writer.Opts) Node.Writer { @@ -711,6 +699,14 @@ const IsolatedWorld = struct { Env.GlobalMissingCallback.init(&self.polyfill_loader), ); } + + pub fn createContextAndLoadPolyfills(self: *IsolatedWorld, arena: Allocator, page: *Page) !void { + // We need to recreate the isolated world context + try self.createContext(page); + + const loader = @import("../browser/polyfill/polyfill.zig"); + try loader.preload(arena, &self.executor.js_context.?); + } }; // This is a generic because when we send a result we have two different diff --git a/src/cdp/domains/dom.zig b/src/cdp/domains/dom.zig index 08564afa..fdb5dd13 100644 --- a/src/cdp/domains/dom.zig +++ b/src/cdp/domains/dom.zig @@ -277,10 +277,15 @@ fn resolveNode(cmd: anytype) !void { var js_context = page.main_context; if (params.executionContextId) |context_id| { if (js_context.v8_context.debugContextId() != context_id) { - var isolated_world = bc.isolated_world orelse return error.ContextNotFound; - js_context = &(isolated_world.executor.js_context orelse return error.ContextNotFound); - - if (js_context.v8_context.debugContextId() != context_id) return error.ContextNotFound; + var found = false; + for (bc.isolated_worlds.items) |*isolated_world| { + js_context = &(isolated_world.executor.js_context orelse return error.ContextNotFound); + if (js_context.v8_context.debugContextId() == context_id) { + found = true; + break; + } + } + if (!found) return error.ContextNotFound; } } diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 4495a474..978ae7a8 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -122,7 +122,7 @@ fn createIsolatedWorld(cmd: anytype) !void { const world = try bc.createIsolatedWorld(params.worldName, params.grantUniveralAccess); const page = bc.session.currentPage() orelse return error.PageNotLoaded; - try pageCreated(bc, page); + try world.createContextAndLoadPolyfills(bc.arena, page); const js_context = &world.executor.js_context.?; // Create the auxdata json for the contextCreated event @@ -259,7 +259,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa true, ); } - if (bc.isolated_world) |*isolated_world| { + for (bc.isolated_worlds.items) |*isolated_world| { const aux_json = try std.fmt.allocPrint(arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id}); // Calling contextCreated will assign a new Id to the context and send the contextCreated event bc.inspector.contextCreated( @@ -274,18 +274,14 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa pub fn pageRemove(bc: anytype) !void { // The main page is going to be removed, we need to remove contexts from other worlds first. - if (bc.isolated_world) |*isolated_world| { + for (bc.isolated_worlds.items) |*isolated_world| { try isolated_world.removeContext(); } } pub fn pageCreated(bc: anytype, page: *Page) !void { - if (bc.isolated_world) |*isolated_world| { - // We need to recreate the isolated world context - try isolated_world.createContext(page); - - const polyfill = @import("../../browser/polyfill/polyfill.zig"); - try polyfill.preload(bc.arena, &isolated_world.executor.js_context.?); + for (bc.isolated_worlds.items) |*isolated_world| { + try isolated_world.createContextAndLoadPolyfills(bc.arena, page); } } diff --git a/src/cdp/domains/target.zig b/src/cdp/domains/target.zig index 0f55bb74..b3b839ad 100644 --- a/src/cdp/domains/target.zig +++ b/src/cdp/domains/target.zig @@ -241,10 +241,10 @@ fn closeTarget(cmd: anytype) !void { } bc.session.removePage(); - if (bc.isolated_world) |*world| { + for (bc.isolated_worlds.items) |*world| { world.deinit(); - bc.isolated_world = null; } + bc.isolated_worlds.clearRetainingCapacity(); bc.target_id = null; }