diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index cd4d25d3..ed4a4119 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -39,9 +39,22 @@ prev_local: ?*const js.Local, prev_context: *Context, // Takes the raw v8 isolate and extracts the context from it. -pub fn init(self: *Caller, v8_isolate: *v8.Isolate) void { - const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }); +// Returns false if the context has been destroyed (e.g., navigated-away iframe), +// in which case a JS exception has been thrown and the caller should return immediately. +pub fn init(self: *Caller, v8_isolate: *v8.Isolate) bool { + const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }) orelse { + throwDetachedError(v8_isolate); + return false; + }; initWithContext(self, ctx, v8_context); + return true; +} + +fn throwDetachedError(isolate: *v8.Isolate) void { + const message = "Cannot execute in detached context (e.g., navigated-away iframe)"; + const v8_message = v8.v8__String__NewFromUtf8(isolate, message.ptr, v8.kNormal, @intCast(message.len)); + const js_exception = v8.v8__Exception__Error(v8_message); + _ = v8.v8__Isolate__ThrowException(isolate, js_exception); } fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) void { @@ -60,9 +73,9 @@ fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) ctx.local = &self.local; } -pub fn initFromHandle(self: *Caller, handle: ?*const v8.FunctionCallbackInfo) void { +pub fn initFromHandle(self: *Caller, handle: ?*const v8.FunctionCallbackInfo) bool { const isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; - self.init(isolate); + return self.init(isolate); } pub fn deinit(self: *Caller) void { @@ -538,7 +551,10 @@ pub const Function = struct { pub fn call(comptime T: type, info_handle: *const v8.FunctionCallbackInfo, func: anytype, comptime opts: Opts) void { const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(info_handle).?; - const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }); + const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }) orelse { + throwDetachedError(v8_isolate); + return; + }; const info = FunctionCallbackInfo{ .handle = info_handle }; var hs: js.HandleScope = undefined; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index beec0625..b691af95 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -138,7 +138,8 @@ pub fn fromC(c_context: *const v8.Context) ?*Context { /// Returns the Context and v8::Context for the given isolate. /// If the current context is from a destroyed Context (e.g., navigated-away iframe), /// falls back to the incumbent context (the calling context). -pub fn fromIsolate(isolate: js.Isolate) struct { *Context, *const v8.Context } { +/// Returns null if neither context has a valid Context struct (both were destroyed). +pub fn fromIsolate(isolate: js.Isolate) ?struct { *Context, *const v8.Context } { const v8_context = v8.v8__Isolate__GetCurrentContext(isolate.handle).?; if (fromC(v8_context)) |ctx| { return .{ ctx, v8_context }; @@ -146,7 +147,8 @@ pub fn fromIsolate(isolate: js.Isolate) struct { *Context, *const v8.Context } { // The current context's Context struct has been freed (e.g., iframe navigated away). // Fall back to the incumbent context (the calling context). const v8_incumbent = v8.v8__Isolate__GetIncumbentContext(isolate.handle).?; - return .{ fromC(v8_incumbent).?, v8_incumbent }; + const ctx = fromC(v8_incumbent) orelse return null; + return .{ ctx, v8_incumbent }; } pub fn deinit(self: *Context) void { @@ -806,7 +808,9 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul const then_callback = newFunctionWithData(local, struct { pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { var c: Caller = undefined; - c.initFromHandle(callback_handle); + if (!c.initFromHandle(callback_handle)) { + return; + } defer c.deinit(); const info = Caller.FunctionCallbackInfo{ .handle = callback_handle.? }; @@ -830,7 +834,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul const catch_callback = newFunctionWithData(local, struct { pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { var c: Caller = undefined; - c.initFromHandle(callback_handle); + if (!c.initFromHandle(callback_handle)) return; defer c.deinit(); const info = Caller.FunctionCallbackInfo{ .handle = callback_handle.? }; diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index bae6a8f0..2c1ebf38 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -519,7 +519,7 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v const promise_handle = v8.v8__PromiseRejectMessage__GetPromise(&message_handle).?; const v8_isolate = v8.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; const isolate = js.Isolate{ .handle = v8_isolate }; - const ctx, const v8_context = Context.fromIsolate(isolate); + const ctx, const v8_context = Context.fromIsolate(isolate) orelse return; const local = js.Local{ .ctx = ctx, diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 8fbdc315..93c51f78 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -116,7 +116,9 @@ pub const Constructor = struct { fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return; + } defer caller.deinit(); caller.constructor(T, func, handle.?, .{ @@ -216,7 +218,9 @@ pub const Indexed = struct { fn wrap(idx: u32, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getIndex(T, getter, idx, handle.?, .{ @@ -232,7 +236,9 @@ pub const Indexed = struct { fn wrap(handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getEnumerator(T, enumerator, handle.?, .{}); } @@ -258,7 +264,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getNamedIndex(T, getter, c_name.?, handle.?, .{ @@ -272,7 +280,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, c_value: ?*const v8.Value, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.setNamedIndex(T, setter, c_name.?, c_value.?, handle.?, .{ @@ -286,7 +296,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.deleteNamedIndex(T, deleter, c_name.?, handle.?, .{ @@ -387,7 +399,9 @@ pub const Property = struct { pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); const local = &caller.local; @@ -465,7 +479,9 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); const local = &caller.local;