Rename to ExecutionWorlds

This commit is contained in:
sjorsdonkers
2025-05-21 13:58:42 +02:00
parent 3ee17e01e1
commit 193e012aa6
7 changed files with 35 additions and 36 deletions

View File

@@ -49,14 +49,14 @@ pub const Session = struct {
// page and start another. // page and start another.
transfer_arena: Allocator, transfer_arena: Allocator,
executor: Env.Executor, executor: Env.ExecutionWorld,
storage_shed: storage.Shed, storage_shed: storage.Shed,
cookie_jar: storage.CookieJar, cookie_jar: storage.CookieJar,
page: ?Page = null, page: ?Page = null,
pub fn init(self: *Session, browser: *Browser) !void { pub fn init(self: *Session, browser: *Browser) !void {
var executor = try browser.env.newExecutor(); var executor = try browser.env.newExecutionWorld();
errdefer executor.deinit(); errdefer executor.deinit();
const allocator = browser.app.allocator; const allocator = browser.app.allocator;

View File

@@ -372,12 +372,11 @@ pub fn BrowserContext(comptime CDP_T: type) type {
return error.CurrentlyOnly1IsolatedWorldSupported; return error.CurrentlyOnly1IsolatedWorldSupported;
} }
var executor = try self.cdp.browser.env.newExecutor(); var executor = try self.cdp.browser.env.newExecutionWorld();
errdefer executor.deinit(); errdefer executor.deinit();
self.isolated_world = .{ self.isolated_world = .{
.name = try self.arena.dupe(u8, world_name), .name = try self.arena.dupe(u8, world_name),
.scope = null,
.executor = executor, .executor = executor,
.grant_universal_access = grant_universal_access, .grant_universal_access = grant_universal_access,
}; };
@@ -511,18 +510,15 @@ 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. /// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
const IsolatedWorld = struct { const IsolatedWorld = struct {
name: []const u8, name: []const u8,
scope: ?*Env.Scope, executor: Env.ExecutionWorld,
executor: Env.Executor,
grant_universal_access: bool, grant_universal_access: bool,
pub fn deinit(self: *IsolatedWorld) void { pub fn deinit(self: *IsolatedWorld) void {
self.executor.deinit(); self.executor.deinit();
self.scope = null;
} }
pub fn removeContext(self: *IsolatedWorld) !void { pub fn removeContext(self: *IsolatedWorld) !void {
if (self.scope == null) return error.NoIsolatedContextToRemove; if (self.executor.scope == null) return error.NoIsolatedContextToRemove;
self.executor.endScope(); self.executor.endScope();
self.scope = null;
} }
// The isolate world must share at least some of the state with the related page, specifically the DocumentHTML // The isolate world must share at least some of the state with the related page, specifically the DocumentHTML
@@ -531,8 +527,8 @@ const IsolatedWorld = struct {
// This also means this pointer becomes invalid after removePage untill a new page is created. // This also means this pointer becomes invalid after removePage untill a new page is created.
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world. // Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
pub fn createContext(self: *IsolatedWorld, page: *Page) !void { pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
if (self.scope != null) return error.Only1IsolatedContextSupported; if (self.executor.scope != null) return error.Only1IsolatedContextSupported;
self.scope = try self.executor.startScope(&page.window, &page.state, {}, false); _ = try self.executor.startScope(&page.window, &page.state, {}, false);
} }
}; };

View File

@@ -262,8 +262,8 @@ fn resolveNode(cmd: anytype) !void {
var scope = page.scope; var scope = page.scope;
if (params.executionContextId) |context_id| { if (params.executionContextId) |context_id| {
if (scope.context.debugContextId() != context_id) { if (scope.context.debugContextId() != context_id) {
const isolated_world = bc.isolated_world orelse return error.ContextNotFound; var isolated_world = bc.isolated_world orelse return error.ContextNotFound;
scope = isolated_world.scope orelse return error.ContextNotFound; scope = &(isolated_world.executor.scope orelse return error.ContextNotFound);
if (scope.context.debugContextId() != context_id) return error.ContextNotFound; if (scope.context.debugContextId() != context_id) return error.ContextNotFound;
} }

View File

@@ -115,7 +115,7 @@ fn createIsolatedWorld(cmd: anytype) !void {
const world = try bc.createIsolatedWorld(params.worldName, params.grantUniveralAccess); const world = try bc.createIsolatedWorld(params.worldName, params.grantUniveralAccess);
const page = bc.session.currentPage() orelse return error.PageNotLoaded; const page = bc.session.currentPage() orelse return error.PageNotLoaded;
try pageCreated(bc, page); try pageCreated(bc, page);
const scope = world.scope.?; const scope = &world.executor.scope.?;
// Create the auxdata json for the contextCreated event // Create the auxdata json for the contextCreated event
// Calling contextCreated will assign a Id to the context and send the contextCreated event // Calling contextCreated will assign a Id to the context and send the contextCreated event
@@ -236,7 +236,7 @@ pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void
const aux_json = try std.fmt.bufPrint(&buffer, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id}); const aux_json = try std.fmt.bufPrint(&buffer, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id});
// Calling contextCreated will assign a new Id to the context and send the contextCreated event // Calling contextCreated will assign a new Id to the context and send the contextCreated event
bc.inspector.contextCreated( bc.inspector.contextCreated(
isolated_world.scope.?, &isolated_world.executor.scope.?,
isolated_world.name, isolated_world.name,
"://", "://",
aux_json, aux_json,
@@ -258,7 +258,7 @@ pub fn pageCreated(bc: anytype, page: *Page) !void {
try isolated_world.createContext(page); try isolated_world.createContext(page);
const polyfill = @import("../../browser/polyfill/polyfill.zig"); const polyfill = @import("../../browser/polyfill/polyfill.zig");
try polyfill.load(bc.arena, isolated_world.scope.?); try polyfill.load(bc.arena, &isolated_world.executor.scope.?);
} }
} }

View File

@@ -53,8 +53,8 @@ pub const Platform = struct {
// The Env maps to a V8 isolate, which represents a isolated sandbox for // The Env maps to a V8 isolate, which represents a isolated sandbox for
// executing JavaScript. The Env is where we'll define our V8 <-> Zig bindings, // executing JavaScript. The Env is where we'll define our V8 <-> Zig bindings,
// and it's where we'll start Executors, which actually execute JavaScript. // and it's where we'll start ExecutionWorlds, which actually execute JavaScript.
// The `S` parameter is arbitrary state. When we start an Executor, an instance // The `S` parameter is arbitrary state. When we start an ExecutionWorld, an instance
// of S must be given. This instance is available to any Zig binding. // of S must be given. This instance is available to any Zig binding.
// The `types` parameter is a tuple of Zig structures we want to bind to V8. // The `types` parameter is a tuple of Zig structures we want to bind to V8.
pub fn Env(comptime State: type, comptime WebApis: type) type { pub fn Env(comptime State: type, comptime WebApis: type) type {
@@ -259,7 +259,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
self.isolate.performMicrotasksCheckpoint(); self.isolate.performMicrotasksCheckpoint();
} }
pub fn newExecutor(self: *Self) !Executor { pub fn newExecutionWorld(self: *Self) !ExecutionWorld {
return .{ return .{
.env = self, .env = self,
.scope = null, .scope = null,
@@ -280,32 +280,35 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
self.isolate.lowMemoryNotification(); self.isolate.lowMemoryNotification();
} }
pub const Executor = struct { // ExecutionWorld closely models a JS World.
// https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md#World
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/ExecutionWorld
pub const ExecutionWorld = struct {
env: *Self, env: *Self,
// Arena whose lifetime is for a single getter/setter/function/etc. // Arena whose lifetime is for a single getter/setter/function/etc.
// Largely used to get strings out of V8, like a stack trace from // Largely used to get strings out of V8, like a stack trace from
// a TryCatch. The allocator will be owned by the Scope, but the // a TryCatch. The allocator will be owned by the Scope, but the
// arena itself is owned by the Executor so that we can re-use it // arena itself is owned by the ExecutionWorld so that we can re-use it
// from scope to scope. // from scope to scope.
call_arena: ArenaAllocator, call_arena: ArenaAllocator,
// Arena whose lifetime is for a single page load, aka a Scope. Where // Arena whose lifetime is for a single page load, aka a Scope. Where
// the call_arena lives for a single function call, the scope_arena // the call_arena lives for a single function call, the scope_arena
// lives for the lifetime of the entire page. The allocator will be // lives for the lifetime of the entire page. The allocator will be
// owned by the Scope, but the arena itself is owned by the Executor // owned by the Scope, but the arena itself is owned by the ExecutionWorld
// so that we can re-use it from scope to scope. // so that we can re-use it from scope to scope.
scope_arena: ArenaAllocator, scope_arena: ArenaAllocator,
// A Scope maps to a Browser's Page. Here though, it's only a // A Scope maps to a Browser's Page. Here though, it's only a
// mechanism to organization page-specific memory. The Executor // mechanism to organization page-specific memory. The ExecutionWorld
// does all the work, but having all page-specific data structures // does all the work, but having all page-specific data structures
// grouped together helps keep things clean. // grouped together helps keep things clean.
scope: ?Scope = null, scope: ?Scope = null,
// no init, must be initialized via env.newExecutor() // no init, must be initialized via env.newExecutionWorld()
pub fn deinit(self: *Executor) void { pub fn deinit(self: *ExecutionWorld) void {
if (self.scope != null) { if (self.scope != null) {
self.endScope(); self.endScope();
} }
@@ -320,7 +323,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
// when the handle_scope is freed. // when the handle_scope is freed.
// We also maintain our own "scope_arena" which allows us to have // We also maintain our own "scope_arena" which allows us to have
// all page related memory easily managed. // all page related memory easily managed.
pub fn startScope(self: *Executor, global: anytype, state: State, module_loader: anytype, enter: bool) !*Scope { pub fn startScope(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype, enter: bool) !*Scope {
std.debug.assert(self.scope == null); std.debug.assert(self.scope == null);
const ModuleLoader = switch (@typeInfo(@TypeOf(module_loader))) { const ModuleLoader = switch (@typeInfo(@TypeOf(module_loader))) {
@@ -338,9 +341,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
const Global = @TypeOf(global.*); const Global = @TypeOf(global.*);
var context: v8.Context = blk: { var context: v8.Context = blk: {
var handle_scope: v8.HandleScope = undefined; var temp_scope: v8.HandleScope = undefined;
v8.HandleScope.init(&handle_scope, isolate); v8.HandleScope.init(&temp_scope, isolate);
defer handle_scope.deinit(); defer temp_scope.deinit();
const js_global = v8.FunctionTemplate.initDefault(isolate); const js_global = v8.FunctionTemplate.initDefault(isolate);
attachClass(Global, isolate, js_global); attachClass(Global, isolate, js_global);
@@ -466,7 +469,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
return scope; return scope;
} }
pub fn endScope(self: *Executor) void { pub fn endScope(self: *ExecutionWorld) void {
self.scope.?.deinit(); self.scope.?.deinit();
self.scope = null; self.scope = null;
_ = self.scope_arena.reset(.{ .retain_with_limit = SCOPE_ARENA_RETAIN }); _ = self.scope_arena.reset(.{ .retain_with_limit = SCOPE_ARENA_RETAIN });
@@ -1517,7 +1520,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
} }
// Retrieves the RemoteObject for a given value. // Retrieves the RemoteObject for a given value.
// The value is loaded through the Executor's mapZigInstanceToJs function, // The value is loaded through the ExecutionWorld's mapZigInstanceToJs function,
// just like a method return value. Therefore, if we've mapped this // just like a method return value. Therefore, if we've mapped this
// value before, we'll get the existing JS PersistedObject and if not // value before, we'll get the existing JS PersistedObject and if not
// we'll create it and track it for cleanup when the scope ends. // we'll create it and track it for cleanup when the scope ends.
@@ -2198,7 +2201,7 @@ fn isEmpty(comptime T: type) bool {
} }
// Responsible for calling Zig functions from JS invokations. This could // Responsible for calling Zig functions from JS invokations. This could
// probably just contained in Executor, but having this specific logic, which // probably just contained in ExecutionWorld, but having this specific logic, which
// is somewhat repetitive between constructors, functions, getters, etc contained // is somewhat repetitive between constructors, functions, getters, etc contained
// here does feel like it makes it clenaer. // here does feel like it makes it clenaer.
fn Caller(comptime E: type, comptime State: type) type { fn Caller(comptime E: type, comptime State: type) type {

View File

@@ -30,7 +30,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
return struct { return struct {
env: *Env, env: *Env,
scope: *Env.Scope, scope: *Env.Scope,
executor: Env.Executor, executor: Env.ExecutionWorld,
pub const Env = js.Env(State, struct { pub const Env = js.Env(State, struct {
pub const Interfaces = AdjustedTypes; pub const Interfaces = AdjustedTypes;
@@ -45,7 +45,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
self.env = try Env.init(allocator, .{}); self.env = try Env.init(allocator, .{});
errdefer self.env.deinit(); errdefer self.env.deinit();
self.executor = try self.env.newExecutor(); self.executor = try self.env.newExecutionWorld();
errdefer self.executor.deinit(); errdefer self.executor.deinit();
self.scope = try self.executor.startScope( self.scope = try self.executor.startScope(

View File

@@ -383,7 +383,7 @@ pub const JsRunner = struct {
renderer: Renderer, renderer: Renderer,
http_client: HttpClient, http_client: HttpClient,
scope: *Env.Scope, scope: *Env.Scope,
executor: Env.Executor, executor: Env.ExecutionWorld,
storage_shelf: storage.Shelf, storage_shelf: storage.Shelf,
cookie_jar: storage.CookieJar, cookie_jar: storage.CookieJar,
@@ -435,7 +435,7 @@ pub const JsRunner = struct {
.tls_verify_host = false, .tls_verify_host = false,
}); });
self.executor = try self.env.newExecutor(); self.executor = try self.env.newExecutionWorld();
errdefer self.executor.deinit(); errdefer self.executor.deinit();
self.scope = try self.executor.startScope(&self.window, &self.state, {}, true); self.scope = try self.executor.startScope(&self.window, &self.state, {}, true);