From ca0eaa5f1eb05c0868dfc0dbb0f3fb59f684831b Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Thu, 21 May 2026 12:33:50 +0800 Subject: [PATCH] Ensure about:blank frame visibility Our navigate on about:blank short-circuits most of the complex logic and loads the content then and there, not asynchronously. This results in notifications for the frame navigation/creation that happen immediately. With the current code ordering though, the frame isn't entered in child_frames until AFTER navigation, resulting in these events trigger on a frame which can't be found via Session.findFrameByFrameId This code adds the frame to child_frames BEFORE navigate (and removes it on error). Note: about:blank iframe navigation is more common than you probably think. As soon as a frame is [dynamically] created, it navigates to about:blank, e.g.: let iframe = document.createElement('iframe'); // IMPLICIT navigation to about:blank happens here // and this will be another navigation event iframe.src = "keemun.php"; --- src/browser/Frame.zig | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index fcb7d7d3..7a56064c 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -1379,6 +1379,11 @@ pub fn iframeAddedCallback(self: *Frame, iframe: *IFrame) !void { ); }; + // Append the new frame before navigate() so synchronous navigation paths + // (about:blank, blob:) and the notifications they dispatch can see this + // frame in self.child_frames. + try self.child_frames.append(self.arena, new_frame); + // Iframe's initial src request carries the parent's URL as Referer and // as the SameSite initiator. Parent frame outlives this navigate() // call, so the slice is safe. @@ -1388,20 +1393,22 @@ pub fn iframeAddedCallback(self: *Frame, iframe: *IFrame) !void { .referer = parent_url, .initiator_url = parent_url, }) catch |err| { + // extra defensive..maybe navigate added a new fame, and the index it + // was added at was removed. Or maybe this frame was removed somehow + // (which I don't think is possible) + if (std.mem.indexOfScalar(*Frame, self.child_frames.items, new_frame)) |idx| { + _ = self.child_frames.swapRemove(idx); + } log.warn(.frame, "iframe navigate failure", .{ .url = url, .err = err }); self._pending_loads -= 1; iframe._window = null; return error.IFrameLoadError; }; - // window[N] is based on document order. For now we'll just append the frame - // at the end of our list and set child_frames_sorted == false. window.getFrame - // will check this flag to decide if it needs to sort the frames or not. - // But, we can optimize this a bit. Since we expect frames to often be - // added in document order, we can do a quick check to see whether the list - // is sorted or not. - try self.child_frames.append(self.arena, new_frame); - + // window[N] is based on document order. We appended above and rely on + // child_frames_sorted to tell window.getFrame whether it has to sort. + // Since we expect frames to often be added in document order, do a quick + // check to keep the list flagged as sorted when possible. const frames_len = self.child_frames.items.len; if (frames_len == 1) { // this is the only frame, it must be sorted.