diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 70abf189..1db063cd 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -299,7 +299,9 @@ pub fn init(self: *Page, frame_id: u32, session: *Session, parent: ?*Page) !void ._performance = Performance.init(), ._screen = screen, ._visual_viewport = visual_viewport, + ._cross_origin_wrapper = undefined, }); + self.window._cross_origin_wrapper = .{ .window = self.window }; self._style_manager = try StyleManager.init(self); errdefer self._style_manager.deinit(); diff --git a/src/browser/tests/window/cross_origin.html b/src/browser/tests/window/cross_origin.html new file mode 100644 index 00000000..74f28f16 --- /dev/null +++ b/src/browser/tests/window/cross_origin.html @@ -0,0 +1,51 @@ + + + + + + diff --git a/src/browser/tests/window/support/frame1.html b/src/browser/tests/window/support/frame1.html new file mode 100644 index 00000000..be6a714e --- /dev/null +++ b/src/browser/tests/window/support/frame1.html @@ -0,0 +1,7 @@ + + diff --git a/src/browser/tests/window/support/frame2.html b/src/browser/tests/window/support/frame2.html new file mode 100644 index 00000000..7dcce7f1 --- /dev/null +++ b/src/browser/tests/window/support/frame2.html @@ -0,0 +1,7 @@ + + diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index b89e7383..aaed5494 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -49,6 +49,10 @@ const IS_DEBUG = builtin.mode == .Debug; const Allocator = std.mem.Allocator; +pub fn registerTypes() []const type { + return &.{ Window, CrossOriginWindow }; +} + const Window = @This(); _proto: *EventTarget, @@ -87,6 +91,8 @@ _scroll_pos: struct { .y = 0, .state = .done, }, +// A cross origin wrapper for this window +_cross_origin_wrapper: CrossOriginWindow, pub fn asEventTarget(self: *Window) *EventTarget { return self._proto; @@ -104,19 +110,19 @@ pub fn getWindow(self: *Window) *Window { return self; } -pub fn getTop(self: *Window) *Window { +pub fn getTop(self: *Window, page: *Page) Access { var p = self._page; while (p.parent) |parent| { p = parent; } - return p.window; + return Access.init(page.window, p.window); } -pub fn getParent(self: *Window) *Window { +pub fn getParent(self: *Window, page: *Page) Access { if (self._page.parent) |p| { - return p.window; + return Access.init(page.window, p.window); } - return self; + return .{ .window = self }; } pub fn getDocument(self: *Window) *Document { @@ -606,6 +612,25 @@ pub fn unhandledPromiseRejection(self: *Window, no_handler: bool, rejection: js. } } +pub const Access = union(enum) { + window: *Window, + cross_origin: *CrossOriginWindow, + + pub fn init(callee: *Window, accessing: *Window) Access { + if (callee == accessing) { + // common enough that it's worth the check + return .{ .window = accessing }; + } + + if (callee._page.js.origin == accessing._page.js.origin) { + // two different windows, but same origin, return the full window + return .{ .window = accessing }; + } + + return .{ .cross_origin = &accessing._cross_origin_wrapper }; + } +}; + const ScheduleOpts = struct { repeat: bool, params: []js.Value.Temp, @@ -892,6 +917,41 @@ pub const JsApi = struct { }.prompt, .{}); }; +const CrossOriginWindow = struct { + window: *Window, + + pub fn postMessage(self: *CrossOriginWindow, message: js.Value.Temp, target_origin: ?[]const u8, page: *Page) !void { + return self.window.postMessage(message, target_origin, page); + } + + pub fn getTop(self: *CrossOriginWindow, page: *Page) Access { + return self.window.getParent(page); + } + + pub fn getParent(self: *CrossOriginWindow, page: *Page) Access { + return self.window.getParent(page); + } + + pub fn getFramesLength(self: *const CrossOriginWindow) u32 { + return self.window.getFramesLength(); + } + + pub const JsApi = struct { + pub const bridge = js.Bridge(CrossOriginWindow); + + pub const Meta = struct { + pub const name = "CrossOriginWindow"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const postMessage = bridge.function(CrossOriginWindow.postMessage, .{}); + pub const top = bridge.accessor(CrossOriginWindow.getTop, null, .{}); + pub const parent = bridge.accessor(CrossOriginWindow.getParent, null, .{}); + pub const length = bridge.accessor(CrossOriginWindow.getFramesLength, null, .{}); + }; +}; + const testing = @import("../../testing.zig"); test "WebApi: Window" { try testing.htmlRunner("window", .{}); diff --git a/src/browser/webapi/element/html/IFrame.zig b/src/browser/webapi/element/html/IFrame.zig index 79bf3f7f..8aa24814 100644 --- a/src/browser/webapi/element/html/IFrame.zig +++ b/src/browser/webapi/element/html/IFrame.zig @@ -39,8 +39,9 @@ pub fn asNode(self: *IFrame) *Node { return self.asElement().asNode(); } -pub fn getContentWindow(self: *const IFrame) ?*Window { - return self._window; +pub fn getContentWindow(self: *const IFrame, page: *Page) ?Window.Access { + const frame_window = self._window orelse return null; + return Window.Access.init(page.window, frame_window); } pub fn getContentDocument(self: *const IFrame) ?*Document {