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.
This commit is contained in:
Karl Seguin
2026-03-15 21:03:55 +08:00
parent a9b9cf14c3
commit 0243c6b450
2 changed files with 26 additions and 8 deletions

View File

@@ -548,7 +548,9 @@ fn processQueuedNavigation(self: *Session) !void {
continue; 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 // Clear the queue after first pass
@@ -588,7 +590,8 @@ fn processFrameNavigation(self: *Session, page: *Page, qn: *QueuedNavigation) !v
errdefer iframe._window = null; 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 // we already notified the parent that we had loaded
parent._pending_loads += 1; parent._pending_loads += 1;
} }
@@ -598,7 +601,19 @@ fn processFrameNavigation(self: *Session, page: *Page, qn: *QueuedNavigation) !v
page.* = undefined; page.* = undefined;
try Page.init(page, frame_id, self, parent); 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; page.iframe = iframe;
iframe._window = page.window; iframe._window = page.window;

View File

@@ -197,18 +197,20 @@ pub fn trackTemp(self: *Context, global: v8.Global) !void {
} }
pub fn weakRef(self: *Context, obj: anytype) 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) { if (comptime IS_DEBUG) {
// should not be possible // should not be possible
std.debug.assert(false); std.debug.assert(false);
} }
return; 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 { 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) { if (comptime IS_DEBUG) {
// should not be possible // should not be possible
std.debug.assert(false); std.debug.assert(false);
@@ -216,11 +218,12 @@ pub fn safeWeakRef(self: *Context, obj: anytype) void {
return; return;
}; };
v8.v8__Global__ClearWeak(&fc.global); 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 { 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) { if (comptime IS_DEBUG) {
// should not be possible // should not be possible
std.debug.assert(false); std.debug.assert(false);