Merge pull request #1885 from lightpanda-io/danling_context_fallback

Fallback to the Incumbent Context when the Current Context is dangling
This commit is contained in:
Karl Seguin
2026-03-18 19:41:38 +08:00
committed by GitHub
3 changed files with 29 additions and 16 deletions

View File

@@ -40,8 +40,8 @@ prev_context: *Context,
// Takes the raw v8 isolate and extracts the context from it. // Takes the raw v8 isolate and extracts the context from it.
pub fn init(self: *Caller, v8_isolate: *v8.Isolate) void { pub fn init(self: *Caller, v8_isolate: *v8.Isolate) void {
const v8_context = v8.v8__Isolate__GetCurrentContext(v8_isolate).?; const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate });
initWithContext(self, Context.fromC(v8_context), v8_context); initWithContext(self, ctx, v8_context);
} }
fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) void { fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) void {
@@ -537,9 +537,7 @@ pub const Function = struct {
pub fn call(comptime T: type, info_handle: *const v8.FunctionCallbackInfo, func: anytype, comptime opts: Opts) void { 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 v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(info_handle).?;
const v8_context = v8.v8__Isolate__GetCurrentContext(v8_isolate).?; const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate });
const ctx = Context.fromC(v8_context);
const info = FunctionCallbackInfo{ .handle = info_handle }; const info = FunctionCallbackInfo{ .handle = info_handle };
var hs: js.HandleScope = undefined; var hs: js.HandleScope = undefined;

View File

@@ -119,12 +119,22 @@ const ModuleEntry = struct {
resolver_promise: ?js.Promise.Global = null, resolver_promise: ?js.Promise.Global = null,
}; };
pub fn fromC(c_context: *const v8.Context) *Context { pub fn fromC(c_context: *const v8.Context) ?*Context {
return @ptrCast(@alignCast(v8.v8__Context__GetAlignedPointerFromEmbedderData(c_context, 1))); return @ptrCast(@alignCast(v8.v8__Context__GetAlignedPointerFromEmbedderData(c_context, 1)));
} }
pub fn fromIsolate(isolate: js.Isolate) *Context { /// Returns the Context and v8::Context for the given isolate.
return fromC(v8.v8__Isolate__GetCurrentContext(isolate.handle).?); /// 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 } {
const v8_context = v8.v8__Isolate__GetCurrentContext(isolate.handle).?;
if (fromC(v8_context)) |ctx| {
return .{ ctx, 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 };
} }
pub fn deinit(self: *Context) void { pub fn deinit(self: *Context) void {
@@ -155,6 +165,11 @@ pub fn deinit(self: *Context) void {
self.session.releaseOrigin(self.origin); self.session.releaseOrigin(self.origin);
// Clear the embedder data so that if V8 keeps this context alive
// (because objects created in it are still referenced), we don't
// have a dangling pointer to our freed Context struct.
v8.v8__Context__SetAlignedPointerInEmbedderData(entered.handle, 1, null);
v8.v8__Global__Reset(&self.handle); v8.v8__Global__Reset(&self.handle);
env.isolate.notifyContextDisposed(); env.isolate.notifyContextDisposed();
// There can be other tasks associated with this context that we need to // There can be other tasks associated with this context that we need to
@@ -255,7 +270,7 @@ pub fn toLocal(self: *Context, global: anytype) js.Local.ToLocalReturnType(@Type
} }
pub fn getIncumbent(self: *Context) *Page { pub fn getIncumbent(self: *Context) *Page {
return fromC(v8.v8__Isolate__GetIncumbentContext(self.env.isolate.handle).?).page; return fromC(v8.v8__Isolate__GetIncumbentContext(self.env.isolate.handle).?).?.page;
} }
pub fn stringToPersistedFunction( pub fn stringToPersistedFunction(
@@ -479,7 +494,7 @@ fn resolveModuleCallback(
) callconv(.c) ?*const v8.Module { ) callconv(.c) ?*const v8.Module {
_ = import_attributes; _ = import_attributes;
const self = fromC(c_context.?); const self = fromC(c_context.?).?;
const local = js.Local{ const local = js.Local{
.ctx = self, .ctx = self,
.handle = c_context.?, .handle = c_context.?,
@@ -512,7 +527,7 @@ pub fn dynamicModuleCallback(
_ = host_defined_options; _ = host_defined_options;
_ = import_attrs; _ = import_attrs;
const self = fromC(c_context.?); const self = fromC(c_context.?).?;
const local = js.Local{ const local = js.Local{
.ctx = self, .ctx = self,
.handle = c_context.?, .handle = c_context.?,
@@ -559,7 +574,7 @@ pub fn dynamicModuleCallback(
pub fn metaObjectCallback(c_context: ?*v8.Context, c_module: ?*v8.Module, c_meta: ?*v8.Value) callconv(.c) void { pub fn metaObjectCallback(c_context: ?*v8.Context, c_module: ?*v8.Module, c_meta: ?*v8.Value) callconv(.c) void {
// @HandleScope implement this without a fat context/local.. // @HandleScope implement this without a fat context/local..
const self = fromC(c_context.?); const self = fromC(c_context.?).?;
var local = js.Local{ var local = js.Local{
.ctx = self, .ctx = self,
.handle = c_context.?, .handle = c_context.?,

View File

@@ -497,13 +497,13 @@ pub fn terminate(self: *const Env) void {
fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) void { fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) void {
const promise_handle = v8.v8__PromiseRejectMessage__GetPromise(&message_handle).?; const promise_handle = v8.v8__PromiseRejectMessage__GetPromise(&message_handle).?;
const v8_isolate = v8.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; const v8_isolate = v8.v8__Object__GetIsolate(@ptrCast(promise_handle)).?;
const js_isolate = js.Isolate{ .handle = v8_isolate }; const isolate = js.Isolate{ .handle = v8_isolate };
const ctx = Context.fromIsolate(js_isolate); const ctx, const v8_context = Context.fromIsolate(isolate);
const local = js.Local{ const local = js.Local{
.ctx = ctx, .ctx = ctx,
.isolate = js_isolate, .isolate = isolate,
.handle = v8.v8__Isolate__GetCurrentContext(v8_isolate).?, .handle = v8_context,
.call_arena = ctx.call_arena, .call_arena = ctx.call_arena,
}; };