diff --git a/src/browser/Page.zig b/src/browser/Page.zig index abe46f47f..5b19e01c8 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -320,7 +320,7 @@ fn findPopupBy(self: *Page, comptime field: []const u8, id: u32) ?*Frame { // // The returned set is fixed, so a caller may run user JS (which can create or // tear down frames/workers) while walking it without invalidating the slice. -pub fn executionsForOrigin(self: *Page, arena: Allocator, origin: *js.Origin) ![]*js.Execution { +pub fn executionsForOrigin(self: *Page, arena: Allocator, origin: []const u8) ![]*js.Execution { var list: std.ArrayList(*js.Execution) = .empty; try appendFrameExecutions(&self.frame, origin, arena, &list); for (self.popups.items) |popup| { @@ -329,14 +329,18 @@ pub fn executionsForOrigin(self: *Page, arena: Allocator, origin: *js.Origin) ![ return list.items; } -fn appendFrameExecutions(frame: *Frame, origin: *js.Origin, arena: Allocator, list: *std.ArrayList(*js.Execution)) !void { - if (frame.js.origin == origin) { - try list.append(arena, &frame.js.execution); +fn appendFrameExecutions(frame: *Frame, origin: []const u8, arena: Allocator, list: *std.ArrayList(*js.Execution)) !void { + if (frame.origin) |fo| { + if (std.mem.eql(u8, fo, origin)) { + try list.append(arena, &frame.js.execution); + } } for (frame.workers.items) |worker| { const wgs = worker._worker_scope; - if (wgs.js.origin == origin) { - try list.append(arena, &wgs.js.execution); + if (wgs.origin) |wo| { + if (std.mem.eql(u8, wo, origin)) { + try list.append(arena, &wgs.js.execution); + } } } for (frame.child_frames.items) |child| { diff --git a/src/browser/URL.zig b/src/browser/URL.zig index 27f0d917c..f1e7e25c5 100644 --- a/src/browser/URL.zig +++ b/src/browser/URL.zig @@ -537,6 +537,17 @@ pub fn getHostname(raw: [:0]const u8) []const u8 { return host[0..port_sep]; } +// Like getHostname, but for an origin serialization ("scheme://host[:port]"), +// which is not necessarily sentinel-terminated. Used where the document's host +// must come from its (possibly inherited) origin rather than its URL — e.g. an +// about:blank iframe whose url stays "about:blank" but whose origin is the +// parent's. +pub fn getOriginHostname(origin: []const u8) []const u8 { + const host = getHost(origin); + const port_sep = findPortSeparator(host) orelse return host; + return host[0..port_sep]; +} + pub fn getPort(raw: [:0]const u8) []const u8 { const host = getHost(raw); const port_sep = findPortSeparator(host) orelse return ""; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 089841e22..7cc777f23 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -229,18 +229,11 @@ pub fn deinit(self: *Context) void { v8.v8__MicrotaskQueue__DELETE(self.microtask_queue); } +// setOrigin is called at navigation (opaque -> real origin) and again when a +// script sets document.domain (real origin -> '!'-marked effective domain). pub fn setOrigin(self: *Context, key: ?[]const u8) !void { const env = self.env; const isolate = env.isolate; - - if (comptime IS_DEBUG) { - // A frame starts off with an opaque origin. After navigation, setOrigin - // is called. This is the only time setOrigin should be called for that - // frame. Therefore, when setOrigin is called, the previous origin should - // have been opaque and its rc should have been 1. - lp.assert(self.origin.rc == 1, "Ref opaque origin", .{ .rc = self.origin.rc }); - } - const origin = try self.page.getOrCreateOrigin(key); self.page.releaseOrigin(self.origin); diff --git a/src/browser/tests/document/document.html b/src/browser/tests/document/document.html index eb69f5d84..44587ae7d 100644 --- a/src/browser/tests/document/document.html +++ b/src/browser/tests/document/document.html @@ -62,6 +62,14 @@ testing.expectEqual(document.URL, document.documentURI); testing.expectEqual('', document.referrer); testing.expectEqual(testing.HOST, document.domain); + + document.domain = testing.HOST; + testing.expectEqual(testing.HOST, document.domain); + testing.expectError('SecurityError', () => { document.domain = 'example.org'; }); + testing.expectError('SecurityError', () => { document.domain = 'com'; }); + testing.expectError('SecurityError', () => { document.domain = ''; }); + // Rejected assignments must not have changed the domain. + testing.expectEqual(testing.HOST, document.domain);