mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-28 07:33:16 +00:00
Merge pull request #1981 from lightpanda-io/window_cross_origin
Window cross origin
This commit is contained in:
@@ -300,7 +300,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();
|
||||
|
||||
51
src/browser/tests/window/cross_origin.html
Normal file
51
src/browser/tests/window/cross_origin.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<iframe src=support/frame1.html></iframe>
|
||||
|
||||
<script id=post_message type=module>
|
||||
const state = await testing.async();
|
||||
|
||||
{
|
||||
const ALT_BASE = testing.BASE_URL.replace('127.0.0.1', 'localhost');
|
||||
|
||||
{
|
||||
let iframe2 = document.createElement('iframe');
|
||||
iframe2.src = ALT_BASE + 'window/support/frame1.html';
|
||||
document.documentElement.appendChild(iframe2);
|
||||
}
|
||||
|
||||
{
|
||||
let iframe3 = document.createElement('iframe');
|
||||
iframe3.src = ALT_BASE + 'window/support/frame2.html';
|
||||
document.documentElement.appendChild(iframe3);
|
||||
}
|
||||
|
||||
let captures = [];
|
||||
window.addEventListener('message', (e) => {
|
||||
captures.push(e.data);
|
||||
if (captures.length == 3) {
|
||||
state.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
await state.done(() => {
|
||||
const expected_urls = [
|
||||
testing.BASE_URL + 'window/support/frame1.html',
|
||||
ALT_BASE + 'window/support/frame1.html',
|
||||
ALT_BASE + 'window/support/frame2.html',
|
||||
];
|
||||
|
||||
// No strong order guarantee for messaages, and we don't care about the order
|
||||
// so long as it's the correct data.
|
||||
testing.expectEqual(expected_urls.sort(), captures.map((c) => {return c.url}).sort());
|
||||
captures.forEach((c) => {
|
||||
if (c.url.includes(testing.BASE_URL)) {
|
||||
testing.expectEqual(false, c.document_is_undefined);
|
||||
} else {
|
||||
testing.expectEqual(true, c.document_is_undefined);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
7
src/browser/tests/window/support/frame1.html
Normal file
7
src/browser/tests/window/support/frame1.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
window.parent.postMessage({
|
||||
url: location.toString(),
|
||||
document_is_undefined: window.parent.document === undefined,
|
||||
}, '*')
|
||||
</script>
|
||||
7
src/browser/tests/window/support/frame2.html
Normal file
7
src/browser/tests/window/support/frame2.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
window.top.postMessage({
|
||||
url: location.toString(),
|
||||
document_is_undefined: window.top.document === undefined,
|
||||
}, '*')
|
||||
</script>
|
||||
@@ -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", .{});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user