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
This commit is contained in:
Karl Seguin
2026-03-21 18:37:06 +08:00
parent b4b7a7d58a
commit 107da49f81
4 changed files with 42 additions and 5 deletions

View File

@@ -441,6 +441,12 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi
if (is_about_blank or is_blob) { if (is_about_blank or is_blob) {
self.url = if (is_about_blank) "about:blank" else try self.arena.dupeZ(u8, request_url); 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) { if (is_blob) {
// strip out blob: // strip out blob:
self.origin = try URL.getOrigin(self.arena, request_url[5.. :0]); self.origin = try URL.getOrigin(self.arena, request_url[5.. :0]);

View File

@@ -323,14 +323,22 @@ pub fn getPassword(raw: [:0]const u8) []const u8 {
} }
pub fn getPathname(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 protocol_end = std.mem.indexOf(u8, raw, "://");
const path_start = std.mem.indexOfScalarPos(u8, raw, if (protocol_end > 0) protocol_end + 3 else 0, '/') orelse raw.len;
// 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; const query_or_hash_start = std.mem.indexOfAnyPos(u8, raw, path_start, "?#") orelse raw.len;
if (path_start >= query_or_hash_start) { 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]; return raw[path_start..query_or_hash_start];

View File

@@ -798,3 +798,19 @@
testing.expectEqual(true, url2.startsWith('blob:')); testing.expectEqual(true, url2.startsWith('blob:'));
} }
</script> </script>
<script id="about:blank">
{
const url = new URL('about:blank');
testing.expectEqual('about:blank', url.href);
testing.expectEqual('null', url.origin);
testing.expectEqual('about:', url.protocol);
testing.expectEqual('blank', url.pathname);
testing.expectEqual('', url.username);
testing.expectEqual('', url.password);
testing.expectEqual('', url.host);
testing.expectEqual('', url.hostname);
testing.expectEqual('', url.port);
testing.expectEqual('', url.search);
}
</script>

View File

@@ -37,6 +37,14 @@ pub const resolve = @import("../URL.zig").resolve;
pub const eqlDocument = @import("../URL.zig").eqlDocument; pub const eqlDocument = @import("../URL.zig").eqlDocument;
pub fn init(url: [:0]const u8, base_: ?[:0]const u8, page: *Page) !*URL { 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 url_is_absolute = @import("../URL.zig").isCompleteHTTPUrl(url);
const base = if (base_) |b| blk: { 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; return error.TypeError;
} else page.url; } else page.url;
const arena = page.arena;
const raw = try resolve(arena, base, url, .{ .always_dupe = true }); const raw = try resolve(arena, base, url, .{ .always_dupe = true });
return page._factory.create(URL{ return page._factory.create(URL{