mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1817 from lightpanda-io/frames_postMessage
window.postMessage across frames
This commit is contained in:
2
.github/actions/install/action.yml
vendored
2
.github/actions/install/action.yml
vendored
@@ -13,7 +13,7 @@ inputs:
|
|||||||
zig-v8:
|
zig-v8:
|
||||||
description: 'zig v8 version to install'
|
description: 'zig v8 version to install'
|
||||||
required: false
|
required: false
|
||||||
default: 'v0.3.3'
|
default: 'v0.3.4'
|
||||||
v8:
|
v8:
|
||||||
description: 'v8 version to install'
|
description: 'v8 version to install'
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ FROM debian:stable-slim
|
|||||||
ARG MINISIG=0.12
|
ARG MINISIG=0.12
|
||||||
ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
|
ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
|
||||||
ARG V8=14.0.365.4
|
ARG V8=14.0.365.4
|
||||||
ARG ZIG_V8=v0.3.3
|
ARG ZIG_V8=v0.3.4
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
RUN apt-get update -yq && \
|
RUN apt-get update -yq && \
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
.minimum_zig_version = "0.15.2",
|
.minimum_zig_version = "0.15.2",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.v8 = .{
|
.v8 = .{
|
||||||
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/refs/tags/v0.3.3.tar.gz",
|
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/refs/tags/v0.3.4.tar.gz",
|
||||||
.hash = "v8-0.0.0-xddH6yx3BAAGD9jSoq_ttt_bk9MectTU44s_HZxxE5LD",
|
.hash = "v8-0.0.0-xddH6_F3BAAiFvKY6R1H-gkuQlk19BkDQ0--uZuTrSup",
|
||||||
},
|
},
|
||||||
// .v8 = .{ .path = "../zig-v8-fork" },
|
// .v8 = .{ .path = "../zig-v8-fork" },
|
||||||
.brotli = .{
|
.brotli = .{
|
||||||
|
|||||||
@@ -91,25 +91,32 @@ pub fn runMicrotasks(self: *Browser) void {
|
|||||||
self.env.runMicrotasks();
|
self.env.runMicrotasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runMacrotasks(self: *Browser) !?u64 {
|
pub fn runMacrotasks(self: *Browser) !void {
|
||||||
const env = &self.env;
|
const env = &self.env;
|
||||||
|
|
||||||
const time_to_next = try self.env.runMacrotasks();
|
try self.env.runMacrotasks();
|
||||||
env.pumpMessageLoop();
|
env.pumpMessageLoop();
|
||||||
|
|
||||||
// either of the above could have queued more microtasks
|
// either of the above could have queued more microtasks
|
||||||
env.runMicrotasks();
|
env.runMicrotasks();
|
||||||
|
|
||||||
return time_to_next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hasBackgroundTasks(self: *Browser) bool {
|
pub fn hasBackgroundTasks(self: *Browser) bool {
|
||||||
return self.env.hasBackgroundTasks();
|
return self.env.hasBackgroundTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn waitForBackgroundTasks(self: *Browser) void {
|
pub fn waitForBackgroundTasks(self: *Browser) void {
|
||||||
self.env.waitForBackgroundTasks();
|
self.env.waitForBackgroundTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn msToNextMacrotask(self: *Browser) ?u64 {
|
||||||
|
return self.env.msToNextMacrotask();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn msTo(self: *Browser) bool {
|
||||||
|
return self.env.hasBackgroundTasks();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn runIdleTasks(self: *const Browser) void {
|
pub fn runIdleTasks(self: *const Browser) void {
|
||||||
self.env.runIdleTasks();
|
self.env.runIdleTasks();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -709,11 +709,14 @@ pub fn scriptsCompletedLoading(self: *Page) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn iframeCompletedLoading(self: *Page, iframe: *IFrame) void {
|
pub fn iframeCompletedLoading(self: *Page, iframe: *IFrame) void {
|
||||||
blk: {
|
var ls: JS.Local.Scope = undefined;
|
||||||
var ls: JS.Local.Scope = undefined;
|
self.js.localScope(&ls);
|
||||||
self.js.localScope(&ls);
|
defer ls.deinit();
|
||||||
defer ls.deinit();
|
|
||||||
|
|
||||||
|
const entered = self.js.enter(&ls.handle_scope);
|
||||||
|
defer entered.exit();
|
||||||
|
|
||||||
|
blk: {
|
||||||
const event = Event.initTrusted(comptime .wrap("load"), .{}, self) catch |err| {
|
const event = Event.initTrusted(comptime .wrap("load"), .{}, self) catch |err| {
|
||||||
log.err(.page, "iframe event init", .{ .err = err, .url = iframe._src });
|
log.err(.page, "iframe event init", .{ .err = err, .url = iframe._src });
|
||||||
break :blk;
|
break :blk;
|
||||||
@@ -722,6 +725,7 @@ pub fn iframeCompletedLoading(self: *Page, iframe: *IFrame) void {
|
|||||||
log.warn(.js, "iframe onload", .{ .err = err, .url = iframe._src });
|
log.warn(.js, "iframe onload", .{ .err = err, .url = iframe._src });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pendingLoadCompleted();
|
self.pendingLoadCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ fn _wait(self: *Session, page: *Page, wait_ms: u32) !WaitResult {
|
|||||||
// scheduler.run could trigger new http transfers, so do not
|
// scheduler.run could trigger new http transfers, so do not
|
||||||
// store http_client.active BEFORE this call and then use
|
// store http_client.active BEFORE this call and then use
|
||||||
// it AFTER.
|
// it AFTER.
|
||||||
const ms_to_next_task = try browser.runMacrotasks();
|
try browser.runMacrotasks();
|
||||||
|
|
||||||
// Each call to this runs scheduled load events.
|
// Each call to this runs scheduled load events.
|
||||||
try page.dispatchLoad();
|
try page.dispatchLoad();
|
||||||
@@ -423,16 +423,16 @@ fn _wait(self: *Session, page: *Page, wait_ms: u32) !WaitResult {
|
|||||||
std.debug.assert(http_client.intercepted == 0);
|
std.debug.assert(http_client.intercepted == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ms: u64 = ms_to_next_task orelse blk: {
|
var ms = blk: {
|
||||||
if (wait_ms - ms_remaining < 100) {
|
// if (wait_ms - ms_remaining < 100) {
|
||||||
if (comptime builtin.is_test) {
|
// if (comptime builtin.is_test) {
|
||||||
return .done;
|
// return .done;
|
||||||
}
|
// }
|
||||||
// Look, we want to exit ASAP, but we don't want
|
// // Look, we want to exit ASAP, but we don't want
|
||||||
// to exit so fast that we've run none of the
|
// // to exit so fast that we've run none of the
|
||||||
// background jobs.
|
// // background jobs.
|
||||||
break :blk 50;
|
// break :blk 50;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (browser.hasBackgroundTasks()) {
|
if (browser.hasBackgroundTasks()) {
|
||||||
// _we_ have nothing to run, but v8 is working on
|
// _we_ have nothing to run, but v8 is working on
|
||||||
@@ -441,9 +441,7 @@ fn _wait(self: *Session, page: *Page, wait_ms: u32) !WaitResult {
|
|||||||
break :blk 20;
|
break :blk 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No http transfers, no cdp extra socket, no
|
break :blk browser.msToNextMacrotask() orelse return .done;
|
||||||
// scheduled tasks, we're done.
|
|
||||||
return .done;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ms > ms_remaining) {
|
if (ms > ms_remaining) {
|
||||||
@@ -470,9 +468,9 @@ fn _wait(self: *Session, page: *Page, wait_ms: u32) !WaitResult {
|
|||||||
// We're here because we either have active HTTP
|
// We're here because we either have active HTTP
|
||||||
// connections, or exit_when_done == false (aka, there's
|
// connections, or exit_when_done == false (aka, there's
|
||||||
// an cdp_socket registered with the http client).
|
// an cdp_socket registered with the http client).
|
||||||
// We should continue to run lowPriority tasks, so we
|
// We should continue to run tasks, so we minimize how long
|
||||||
// minimize how long we'll poll for network I/O.
|
// we'll poll for network I/O.
|
||||||
var ms_to_wait = @min(200, ms_to_next_task orelse 200);
|
var ms_to_wait = @min(200, browser.msToNextMacrotask() orelse 200);
|
||||||
if (ms_to_wait > 10 and browser.hasBackgroundTasks()) {
|
if (ms_to_wait > 10 and browser.hasBackgroundTasks()) {
|
||||||
// if we have background tasks, we don't want to wait too
|
// if we have background tasks, we don't want to wait too
|
||||||
// long for a message from the client. We want to go back
|
// long for a message from the client. We want to go back
|
||||||
|
|||||||
@@ -255,6 +255,10 @@ pub fn toLocal(self: *Context, global: anytype) js.Local.ToLocalReturnType(@Type
|
|||||||
return l.toLocal(global);
|
return l.toLocal(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getIncumbent(self: *Context) *Page {
|
||||||
|
return fromC(v8.v8__Isolate__GetIncumbentContext(self.env.isolate.handle).?).page;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stringToPersistedFunction(
|
pub fn stringToPersistedFunction(
|
||||||
self: *Context,
|
self: *Context,
|
||||||
function_body: []const u8,
|
function_body: []const u8,
|
||||||
|
|||||||
@@ -382,8 +382,7 @@ pub fn runMicrotasks(self: *Env) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runMacrotasks(self: *Env) !?u64 {
|
pub fn runMacrotasks(self: *Env) !void {
|
||||||
var ms_to_next_task: ?u64 = null;
|
|
||||||
for (self.contexts[0..self.context_count]) |ctx| {
|
for (self.contexts[0..self.context_count]) |ctx| {
|
||||||
if (comptime builtin.is_test == false) {
|
if (comptime builtin.is_test == false) {
|
||||||
// I hate this comptime check as much as you do. But we have tests
|
// I hate this comptime check as much as you do. But we have tests
|
||||||
@@ -398,13 +397,17 @@ pub fn runMacrotasks(self: *Env) !?u64 {
|
|||||||
var hs: js.HandleScope = undefined;
|
var hs: js.HandleScope = undefined;
|
||||||
const entered = ctx.enter(&hs);
|
const entered = ctx.enter(&hs);
|
||||||
defer entered.exit();
|
defer entered.exit();
|
||||||
|
try ctx.scheduler.run();
|
||||||
const ms = (try ctx.scheduler.run()) orelse continue;
|
|
||||||
if (ms_to_next_task == null or ms < ms_to_next_task.?) {
|
|
||||||
ms_to_next_task = ms;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ms_to_next_task;
|
}
|
||||||
|
|
||||||
|
pub fn msToNextMacrotask(self: *Env) ?u64 {
|
||||||
|
var next_task: u64 = std.math.maxInt(u64);
|
||||||
|
for (self.contexts[0..self.context_count]) |ctx| {
|
||||||
|
const candidate = ctx.scheduler.msToNextHigh() orelse continue;
|
||||||
|
next_task = @min(candidate, next_task);
|
||||||
|
}
|
||||||
|
return if (next_task == std.math.maxInt(u64)) null else next_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pumpMessageLoop(self: *const Env) void {
|
pub fn pumpMessageLoop(self: *const Env) void {
|
||||||
|
|||||||
@@ -74,9 +74,10 @@ pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Scheduler) !?u64 {
|
pub fn run(self: *Scheduler) !void {
|
||||||
_ = try self.runQueue(&self.low_priority);
|
const now = milliTimestamp(.monotonic);
|
||||||
return self.runQueue(&self.high_priority);
|
try self.runQueue(&self.low_priority, now);
|
||||||
|
try self.runQueue(&self.high_priority, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hasReadyTasks(self: *Scheduler) bool {
|
pub fn hasReadyTasks(self: *Scheduler) bool {
|
||||||
@@ -84,16 +85,23 @@ pub fn hasReadyTasks(self: *Scheduler) bool {
|
|||||||
return queueuHasReadyTask(&self.low_priority, now) or queueuHasReadyTask(&self.high_priority, now);
|
return queueuHasReadyTask(&self.low_priority, now) or queueuHasReadyTask(&self.high_priority, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runQueue(self: *Scheduler, queue: *Queue) !?u64 {
|
pub fn msToNextHigh(self: *Scheduler) ?u64 {
|
||||||
if (queue.count() == 0) {
|
const task = self.high_priority.peek() orelse return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = milliTimestamp(.monotonic);
|
const now = milliTimestamp(.monotonic);
|
||||||
|
if (task.run_at <= now) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return @intCast(task.run_at - now);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runQueue(self: *Scheduler, queue: *Queue, now: u64) !void {
|
||||||
|
if (queue.count() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (queue.peek()) |*task_| {
|
while (queue.peek()) |*task_| {
|
||||||
if (task_.run_at > now) {
|
if (task_.run_at > now) {
|
||||||
return @intCast(task_.run_at - now);
|
return;
|
||||||
}
|
}
|
||||||
var task = queue.remove();
|
var task = queue.remove();
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
@@ -114,7 +122,7 @@ fn runQueue(self: *Scheduler, queue: *Queue) !?u64 {
|
|||||||
try self.low_priority.add(task);
|
try self.low_priority.add(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queueuHasReadyTask(queue: *Queue, now: u64) bool {
|
fn queueuHasReadyTask(queue: *Queue, now: u64) bool {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
testing.expectEqual('', $('#a0').href);
|
testing.expectEqual('', $('#a0').href);
|
||||||
|
|
||||||
testing.expectEqual(testing.BASE_URL + 'element/anchor1.html', $('#a1').href);
|
testing.expectEqual(testing.BASE_URL + 'element/anchor1.html', $('#a1').href);
|
||||||
testing.expectEqual(testing.ORIGIN + 'hello/world/anchor2.html', $('#a2').href);
|
testing.expectEqual(testing.ORIGIN + '/hello/world/anchor2.html', $('#a2').href);
|
||||||
testing.expectEqual('https://www.openmymind.net/Elixirs-With-Statement/', $('#a3').href);
|
testing.expectEqual('https://www.openmymind.net/Elixirs-With-Statement/', $('#a3').href);
|
||||||
|
|
||||||
testing.expectEqual(testing.BASE_URL + 'element/html/foo', $('#link').href);
|
testing.expectEqual(testing.BASE_URL + 'element/html/foo', $('#link').href);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
testing.expectEqual(testing.BASE_URL + 'element/html/hello', form.action)
|
testing.expectEqual(testing.BASE_URL + 'element/html/hello', form.action)
|
||||||
|
|
||||||
form.action = '/hello';
|
form.action = '/hello';
|
||||||
testing.expectEqual(testing.ORIGIN + 'hello', form.action)
|
testing.expectEqual(testing.ORIGIN + '/hello', form.action)
|
||||||
|
|
||||||
form.action = 'https://lightpanda.io/hello';
|
form.action = 'https://lightpanda.io/hello';
|
||||||
testing.expectEqual('https://lightpanda.io/hello', form.action)
|
testing.expectEqual('https://lightpanda.io/hello', form.action)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
testing.expectEqual('test.png', img.getAttribute('src'));
|
testing.expectEqual('test.png', img.getAttribute('src'));
|
||||||
|
|
||||||
img.src = '/absolute/path.png';
|
img.src = '/absolute/path.png';
|
||||||
testing.expectEqual(testing.ORIGIN + 'absolute/path.png', img.src);
|
testing.expectEqual(testing.ORIGIN + '/absolute/path.png', img.src);
|
||||||
testing.expectEqual('/absolute/path.png', img.getAttribute('src'));
|
testing.expectEqual('/absolute/path.png', img.getAttribute('src'));
|
||||||
|
|
||||||
img.src = 'https://example.com/image.png';
|
img.src = 'https://example.com/image.png';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
testing.expectEqual('https://lightpanda.io/opensource-browser/15', l2.href);
|
testing.expectEqual('https://lightpanda.io/opensource-browser/15', l2.href);
|
||||||
|
|
||||||
l2.href = '/over/9000';
|
l2.href = '/over/9000';
|
||||||
testing.expectEqual(testing.ORIGIN + 'over/9000', l2.href);
|
testing.expectEqual(testing.ORIGIN + '/over/9000', l2.href);
|
||||||
|
|
||||||
l2.crossOrigin = 'nope';
|
l2.crossOrigin = 'nope';
|
||||||
testing.expectEqual('anonymous', l2.crossOrigin);
|
testing.expectEqual('anonymous', l2.crossOrigin);
|
||||||
|
|||||||
25
src/browser/tests/frames/post_message.html
Normal file
25
src/browser/tests/frames/post_message.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
|
||||||
|
<iframe id="receiver"></iframe>
|
||||||
|
|
||||||
|
<script id="messages">
|
||||||
|
{
|
||||||
|
let reply = null;
|
||||||
|
window.addEventListener('message', (e) => {
|
||||||
|
console.warn('reply')
|
||||||
|
reply = e.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
const iframe = $('#receiver');
|
||||||
|
iframe.src = 'support/message_receiver.html';
|
||||||
|
iframe.addEventListener('load', () => {
|
||||||
|
iframe.contentWindow.postMessage('ping', '*');
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual('pong', reply.data);
|
||||||
|
testing.expectEqual(testing.ORIGIN, reply.origin);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
9
src/browser/tests/frames/support/message_receiver.html
Normal file
9
src/browser/tests/frames/support/message_receiver.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('message', (e) => {
|
||||||
|
console.warn('Frame Message', e.data);
|
||||||
|
if (e.data === 'ping') {
|
||||||
|
window.top.postMessage({data: 'pong', origin: e.origin}, '*');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
eventually: eventually,
|
eventually: eventually,
|
||||||
IS_TEST_RUNNER: IS_TEST_RUNNER,
|
IS_TEST_RUNNER: IS_TEST_RUNNER,
|
||||||
HOST: '127.0.0.1',
|
HOST: '127.0.0.1',
|
||||||
ORIGIN: 'http://127.0.0.1:9582/',
|
ORIGIN: 'http://127.0.0.1:9582',
|
||||||
BASE_URL: 'http://127.0.0.1:9582/src/browser/tests/',
|
BASE_URL: 'http://127.0.0.1:9582/src/browser/tests/',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
// seemless, namely around adapting paths/urls.
|
// seemless, namely around adapting paths/urls.
|
||||||
console.warn(`The page is not being executed in the test runner, certain behavior has been adjusted`);
|
console.warn(`The page is not being executed in the test runner, certain behavior has been adjusted`);
|
||||||
window.testing.HOST = location.hostname;
|
window.testing.HOST = location.hostname;
|
||||||
window.testing.ORIGIN = location.origin + '/';
|
window.testing.ORIGIN = location.origin;
|
||||||
window.testing.BASE_URL = location.origin + '/src/browser/tests/';
|
window.testing.BASE_URL = location.origin + '/src/browser/tests/';
|
||||||
window.addEventListener('load', testing.assertOk);
|
window.addEventListener('load', testing.assertOk);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ _on_load: ?js.Function.Global = null,
|
|||||||
_on_pageshow: ?js.Function.Global = null,
|
_on_pageshow: ?js.Function.Global = null,
|
||||||
_on_popstate: ?js.Function.Global = null,
|
_on_popstate: ?js.Function.Global = null,
|
||||||
_on_error: ?js.Function.Global = null,
|
_on_error: ?js.Function.Global = null,
|
||||||
|
_on_message: ?js.Function.Global = null,
|
||||||
_on_unhandled_rejection: ?js.Function.Global = null, // TODO: invoke on error
|
_on_unhandled_rejection: ?js.Function.Global = null, // TODO: invoke on error
|
||||||
_location: *Location,
|
_location: *Location,
|
||||||
_timer_id: u30 = 0,
|
_timer_id: u30 = 0,
|
||||||
@@ -208,6 +209,14 @@ pub fn setOnError(self: *Window, setter: ?FunctionSetter) void {
|
|||||||
self._on_error = getFunctionFromSetter(setter);
|
self._on_error = getFunctionFromSetter(setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getOnMessage(self: *const Window) ?js.Function.Global {
|
||||||
|
return self._on_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setOnMessage(self: *Window, setter: ?FunctionSetter) void {
|
||||||
|
self._on_message = getFunctionFromSetter(setter);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getOnUnhandledRejection(self: *const Window) ?js.Function.Global {
|
pub fn getOnUnhandledRejection(self: *const Window) ?js.Function.Global {
|
||||||
return self._on_unhandled_rejection;
|
return self._on_unhandled_rejection;
|
||||||
}
|
}
|
||||||
@@ -369,19 +378,26 @@ pub fn postMessage(self: *Window, message: js.Value.Temp, target_origin: ?[]cons
|
|||||||
// In a full implementation, we would validate the origin
|
// In a full implementation, we would validate the origin
|
||||||
_ = target_origin;
|
_ = target_origin;
|
||||||
|
|
||||||
// postMessage queues a task (not a microtask), so use the scheduler
|
// self = the window that will get the message
|
||||||
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
// page = the context calling postMessage
|
||||||
errdefer page.releaseArena(arena);
|
const target_page = self._page;
|
||||||
|
const source_window = target_page.js.getIncumbent().window;
|
||||||
|
|
||||||
const origin = try self._location.getOrigin(page);
|
const arena = try target_page.getArena(.{ .debug = "Window.postMessage" });
|
||||||
|
errdefer target_page.releaseArena(arena);
|
||||||
|
|
||||||
|
// Origin should be the source window's origin (where the message came from)
|
||||||
|
const origin = try source_window._location.getOrigin(page);
|
||||||
const callback = try arena.create(PostMessageCallback);
|
const callback = try arena.create(PostMessageCallback);
|
||||||
callback.* = .{
|
callback.* = .{
|
||||||
.page = page,
|
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.message = message,
|
.message = message,
|
||||||
|
.page = target_page,
|
||||||
|
.source = source_window,
|
||||||
.origin = try arena.dupe(u8, origin),
|
.origin = try arena.dupe(u8, origin),
|
||||||
};
|
};
|
||||||
try page.js.scheduler.add(callback, PostMessageCallback.run, 0, .{
|
|
||||||
|
try target_page.js.scheduler.add(callback, PostMessageCallback.run, 0, .{
|
||||||
.name = "postMessage",
|
.name = "postMessage",
|
||||||
.low_priority = false,
|
.low_priority = false,
|
||||||
.finalizer = PostMessageCallback.cancelled,
|
.finalizer = PostMessageCallback.cancelled,
|
||||||
@@ -702,6 +718,7 @@ const ScheduleCallback = struct {
|
|||||||
|
|
||||||
const PostMessageCallback = struct {
|
const PostMessageCallback = struct {
|
||||||
page: *Page,
|
page: *Page,
|
||||||
|
source: *Window,
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
origin: []const u8,
|
origin: []const u8,
|
||||||
message: js.Value.Temp,
|
message: js.Value.Temp,
|
||||||
@@ -712,7 +729,7 @@ const PostMessageCallback = struct {
|
|||||||
|
|
||||||
fn cancelled(ctx: *anyopaque) void {
|
fn cancelled(ctx: *anyopaque) void {
|
||||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||||
self.page.releaseArena(self.arena);
|
self.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(ctx: *anyopaque) !?u32 {
|
fn run(ctx: *anyopaque) !?u32 {
|
||||||
@@ -722,14 +739,17 @@ const PostMessageCallback = struct {
|
|||||||
const page = self.page;
|
const page = self.page;
|
||||||
const window = page.window;
|
const window = page.window;
|
||||||
|
|
||||||
const event = (try MessageEvent.initTrusted(comptime .wrap("message"), .{
|
const event_target = window.asEventTarget();
|
||||||
.data = self.message,
|
if (page._event_manager.hasDirectListeners(event_target, "message", window._on_message)) {
|
||||||
.origin = self.origin,
|
const event = (try MessageEvent.initTrusted(comptime .wrap("message"), .{
|
||||||
.source = window,
|
.data = self.message,
|
||||||
.bubbles = false,
|
.origin = self.origin,
|
||||||
.cancelable = false,
|
.source = self.source,
|
||||||
}, page)).asEvent();
|
.bubbles = false,
|
||||||
try page._event_manager.dispatch(window.asEventTarget(), event);
|
.cancelable = false,
|
||||||
|
}, page)).asEvent();
|
||||||
|
try page._event_manager.dispatchDirect(event_target, event, window._on_message, .{ .context = "window.postMessage" });
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -783,6 +803,7 @@ pub const JsApi = struct {
|
|||||||
pub const onpageshow = bridge.accessor(Window.getOnPageShow, Window.setOnPageShow, .{});
|
pub const onpageshow = bridge.accessor(Window.getOnPageShow, Window.setOnPageShow, .{});
|
||||||
pub const onpopstate = bridge.accessor(Window.getOnPopState, Window.setOnPopState, .{});
|
pub const onpopstate = bridge.accessor(Window.getOnPopState, Window.setOnPopState, .{});
|
||||||
pub const onerror = bridge.accessor(Window.getOnError, Window.setOnError, .{});
|
pub const onerror = bridge.accessor(Window.getOnError, Window.setOnError, .{});
|
||||||
|
pub const onmessage = bridge.accessor(Window.getOnMessage, Window.setOnMessage, .{});
|
||||||
pub const onunhandledrejection = bridge.accessor(Window.getOnUnhandledRejection, Window.setOnUnhandledRejection, .{});
|
pub const onunhandledrejection = bridge.accessor(Window.getOnUnhandledRejection, Window.setOnUnhandledRejection, .{});
|
||||||
pub const fetch = bridge.function(Window.fetch, .{});
|
pub const fetch = bridge.function(Window.fetch, .{});
|
||||||
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});
|
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user