From 107da49f8163113ef04c0c724f3e00c9f7e414f8 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 21 Mar 2026 18:37:06 +0800 Subject: [PATCH] new URL('about:blank'); Add correct handling for new URL('about:blank'); When a frame is navigated to about:blank (which happens often, since it happens as soon as a dynamic iframe is created), we make sure to give window._location a unique value. This prevents 2 frames from referencing the same window._location object. Fixes a WPT crash in: 0/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html --- src/browser/Page.zig | 6 ++++++ src/browser/URL.zig | 16 ++++++++++++---- src/browser/tests/url.html | 16 ++++++++++++++++ src/browser/webapi/URL.zig | 9 ++++++++- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/browser/Page.zig b/src/browser/Page.zig index eef70bbc..18292aa6 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -441,6 +441,12 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi if (is_about_blank or is_blob) { self.url = if (is_about_blank) "about:blank" else try self.arena.dupeZ(u8, request_url); + // even though this might be the same _data_ as `default_location`, we + // have to do this to make sure window.location is at a unique _address_. + // If we don't do this, mulitple window._location will have the same + // address and thus be mapped to the same v8::Object in the identity map. + self.window._location = try Location.init(self.url, self); + if (is_blob) { // strip out blob: self.origin = try URL.getOrigin(self.arena, request_url[5.. :0]); diff --git a/src/browser/URL.zig b/src/browser/URL.zig index 1c26a981..935d1791 100644 --- a/src/browser/URL.zig +++ b/src/browser/URL.zig @@ -323,14 +323,22 @@ pub fn getPassword(raw: [:0]const u8) []const u8 { } pub fn getPathname(raw: [:0]const u8) []const u8 { - const protocol_end = std.mem.indexOf(u8, raw, "://") orelse 0; - const path_start = std.mem.indexOfScalarPos(u8, raw, if (protocol_end > 0) protocol_end + 3 else 0, '/') orelse raw.len; + const protocol_end = std.mem.indexOf(u8, raw, "://"); + + // Handle scheme:path URLs like about:blank (no "://") + if (protocol_end == null) { + const colon_pos = std.mem.indexOfScalar(u8, raw, ':') orelse return ""; + const path = raw[colon_pos + 1 ..]; + const query_or_hash = std.mem.indexOfAny(u8, path, "?#") orelse path.len; + return path[0..query_or_hash]; + } + + const path_start = std.mem.indexOfScalarPos(u8, raw, protocol_end.? + 3, '/') orelse raw.len; const query_or_hash_start = std.mem.indexOfAnyPos(u8, raw, path_start, "?#") orelse raw.len; if (path_start >= query_or_hash_start) { - if (std.mem.indexOf(u8, raw, "://") != null) return "/"; - return ""; + return "/"; } return raw[path_start..query_or_hash_start]; diff --git a/src/browser/tests/url.html b/src/browser/tests/url.html index f8074422..c1bfa09b 100644 --- a/src/browser/tests/url.html +++ b/src/browser/tests/url.html @@ -798,3 +798,19 @@ testing.expectEqual(true, url2.startsWith('blob:')); } + + diff --git a/src/browser/webapi/URL.zig b/src/browser/webapi/URL.zig index e9263319..cb8e5d11 100644 --- a/src/browser/webapi/URL.zig +++ b/src/browser/webapi/URL.zig @@ -37,6 +37,14 @@ pub const resolve = @import("../URL.zig").resolve; pub const eqlDocument = @import("../URL.zig").eqlDocument; pub fn init(url: [:0]const u8, base_: ?[:0]const u8, page: *Page) !*URL { + const arena = page.arena; + + if (std.mem.eql(u8, url, "about:blank")) { + return page._factory.create(URL{ + ._raw = "about:blank", + ._arena = arena, + }); + } const url_is_absolute = @import("../URL.zig").isCompleteHTTPUrl(url); const base = if (base_) |b| blk: { @@ -53,7 +61,6 @@ pub fn init(url: [:0]const u8, base_: ?[:0]const u8, page: *Page) !*URL { return error.TypeError; } else page.url; - const arena = page.arena; const raw = try resolve(arena, base, url, .{ .always_dupe = true }); return page._factory.create(URL{