From 0243c6b450f0e251f8dc69ecb13f61ca86ec2352 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sun, 15 Mar 2026 21:03:55 +0800 Subject: [PATCH] Fix issues with blobs https://github.com/lightpanda-io/browser/pull/1775 made blobs finalizable and https://github.com/lightpanda-io/browser/pull/1795 made it possible to navigate from blobs (important for WPT tests). This fixes a number of issues related to both. First, weak/strong ref'ing a value now uses the resolved value. When registering a finalizer, we use the resolved value (the most specific type in the prototype chain). For this reason, when toggling a weak/strong ref, we have to use the same resolved value. This solves a segfault where a File is created, but extended as a Blob (e.g. in createObjectURL). Next, two issues were fixed when navigating to an invalid blob. First, the frame is properly removed from the parent list on frame navigation error. Second, on frame navigation error, we don't stop _all_ other navigations, we just log the error and move on to the next frame. --- src/browser/Session.zig | 21 ++++++++++++++++++--- src/browser/js/Context.zig | 13 ++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/browser/Session.zig b/src/browser/Session.zig index fea56a87..404a8bc4 100644 --- a/src/browser/Session.zig +++ b/src/browser/Session.zig @@ -548,7 +548,9 @@ fn processQueuedNavigation(self: *Session) !void { continue; } - try self.processFrameNavigation(page, qn); + self.processFrameNavigation(page, qn) catch |err| { + log.warn(.page, "frame navigation", .{ .url = qn.url, .err = err }); + }; } // Clear the queue after first pass @@ -588,7 +590,8 @@ fn processFrameNavigation(self: *Session, page: *Page, qn: *QueuedNavigation) !v errdefer iframe._window = null; - if (page._parent_notified) { + const parent_notified = page._parent_notified; + if (parent_notified) { // we already notified the parent that we had loaded parent._pending_loads += 1; } @@ -598,7 +601,19 @@ fn processFrameNavigation(self: *Session, page: *Page, qn: *QueuedNavigation) !v page.* = undefined; try Page.init(page, frame_id, self, parent); - errdefer page.deinit(true); + errdefer { + for (parent.frames.items, 0..) |frame, i| { + if (frame == page) { + parent.frames_sorted = false; + _ = parent.frames.swapRemove(i); + break; + } + } + if (parent_notified) { + parent._pending_loads -= 1; + } + page.deinit(true); + } page.iframe = iframe; iframe._window = page.window; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 5c58c5cb..70af9d24 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -197,18 +197,20 @@ pub fn trackTemp(self: *Context, global: v8.Global) !void { } pub fn weakRef(self: *Context, obj: anytype) void { - const fc = self.origin.finalizer_callbacks.get(@intFromPtr(obj)) orelse { + const resolved = js.Local.resolveValue(obj); + const fc = self.origin.finalizer_callbacks.get(@intFromPtr(resolved.ptr)) orelse { if (comptime IS_DEBUG) { // should not be possible std.debug.assert(false); } return; }; - v8.v8__Global__SetWeakFinalizer(&fc.global, fc, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter); + v8.v8__Global__SetWeakFinalizer(&fc.global, fc, resolved.finalizer_from_v8, v8.kParameter); } pub fn safeWeakRef(self: *Context, obj: anytype) void { - const fc = self.origin.finalizer_callbacks.get(@intFromPtr(obj)) orelse { + const resolved = js.Local.resolveValue(obj); + const fc = self.origin.finalizer_callbacks.get(@intFromPtr(resolved.ptr)) orelse { if (comptime IS_DEBUG) { // should not be possible std.debug.assert(false); @@ -216,11 +218,12 @@ pub fn safeWeakRef(self: *Context, obj: anytype) void { return; }; v8.v8__Global__ClearWeak(&fc.global); - v8.v8__Global__SetWeakFinalizer(&fc.global, fc, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter); + v8.v8__Global__SetWeakFinalizer(&fc.global, fc, resolved.finalizer_from_v8, v8.kParameter); } pub fn strongRef(self: *Context, obj: anytype) void { - const fc = self.origin.finalizer_callbacks.get(@intFromPtr(obj)) orelse { + const resolved = js.Local.resolveValue(obj); + const fc = self.origin.finalizer_callbacks.get(@intFromPtr(resolved.ptr)) orelse { if (comptime IS_DEBUG) { // should not be possible std.debug.assert(false);