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 {