Merge pull request #2105 from lightpanda-io/no-context-handling

Better handle v8 callback with no valid context
This commit is contained in:
Karl Seguin
2026-04-10 08:11:36 +08:00
committed by GitHub
4 changed files with 54 additions and 18 deletions

View File

@@ -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;

View File

@@ -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.? };

View File

@@ -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,

View File

@@ -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;