diff --git a/src/browser/html/window.zig b/src/browser/html/window.zig
index e41c18ad..0a8494b6 100644
--- a/src/browser/html/window.zig
+++ b/src/browser/html/window.zig
@@ -20,7 +20,7 @@ const std = @import("std");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
-const Function = @import("../env.zig").Function;
+const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const Loop = @import("../../runtime/loop.zig").Loop;
@@ -36,6 +36,9 @@ const CSSStyleDeclaration = @import("../cssom/css_style_declaration.zig").CSSSty
const Screen = @import("screen.zig").Screen;
const Css = @import("../css/css.zig").Css;
+const Function = Env.Function;
+const JsObject = Env.JsObject;
+
const storage = @import("../storage/storage.zig");
// https://dom.spec.whatwg.org/#interface-window-extensions
@@ -184,13 +187,12 @@ pub const Window = struct {
return page.loop.cancel(kv.value.loop_id);
}
- pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
- return self.createTimeout(cbk, delay, page, .{});
+ pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, params: []Env.JsObject, page: *Page) !u32 {
+ return self.createTimeout(cbk, delay, page, .{ .args = params });
}
- // TODO handle callback arguments.
- pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
- return self.createTimeout(cbk, delay, page, .{ .repeat = true });
+ pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, params: []Env.JsObject, page: *Page) !u32 {
+ return self.createTimeout(cbk, delay, page, .{ .repeat = true, .args = params });
}
pub fn _clearTimeout(self: *Window, id: u32, page: *Page) !void {
@@ -230,10 +232,11 @@ pub const Window = struct {
}
const CreateTimeoutOpts = struct {
+ args: []Env.JsObject = &.{},
repeat: bool = false,
animation_frame: bool = false,
};
- fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, comptime opts: CreateTimeoutOpts) !u32 {
+ fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, opts: CreateTimeoutOpts) !u32 {
const delay = delay_ orelse 0;
if (delay > 5000) {
log.warn(.user_script, "long timeout ignored", .{ .delay = delay, .interval = opts.repeat });
@@ -258,6 +261,15 @@ pub const Window = struct {
}
errdefer _ = self.timers.remove(timer_id);
+ const args = opts.args;
+ var persisted_args: []Env.JsObject = &.{};
+ if (args.len > 0) {
+ persisted_args = try page.arena.alloc(Env.JsObject, args.len);
+ for (args, persisted_args) |a, *ca| {
+ ca.* = try a.persist();
+ }
+ }
+
const delay_ms: u63 = @as(u63, delay) * std.time.ns_per_ms;
const callback = try arena.create(TimerCallback);
@@ -266,6 +278,7 @@ pub const Window = struct {
.loop_id = 0, // we're going to set this to a real value shortly
.window = self,
.timer_id = timer_id,
+ .args = persisted_args,
.node = .{ .func = TimerCallback.run },
.repeat = if (opts.repeat) delay_ms else null,
.animation_frame = opts.animation_frame,
@@ -344,6 +357,8 @@ const TimerCallback = struct {
window: *Window,
+ args: []Env.JsObject = &.{},
+
fn run(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
const self: *TimerCallback = @fieldParentPtr("node", node);
@@ -353,7 +368,7 @@ const TimerCallback = struct {
if (self.animation_frame) {
call = self.cbk.tryCall(void, .{self.window.performance._now()}, &result);
} else {
- call = self.cbk.tryCall(void, .{}, &result);
+ call = self.cbk.tryCall(void, self.args, &result);
}
call catch {
@@ -427,11 +442,17 @@ test "Browser.HTML.Window" {
.{ "innerWidth", "2" },
}, .{});
- // cancelAnimationFrame should be able to cancel a request with the given id
try runner.testCases(&.{
.{ "let longCall = false;", null },
.{ "window.setTimeout(() => {longCall = true}, 5001);", null },
.{ "longCall;", "false" },
+
+ .{ "let wst = 0;", null },
+ .{ "window.setTimeout(() => {wst += 1}, 1)", null },
+ .{ "wst", "1" },
+
+ .{ "window.setTimeout((a, b) => {wst = a + b}, 1, 2, 3)", null },
+ .{ "wst", "5" },
}, .{});
// window event target
diff --git a/src/runtime/js.zig b/src/runtime/js.zig
index 789ca93e..b5216ab8 100644
--- a/src/runtime/js.zig
+++ b/src/runtime/js.zig
@@ -1766,13 +1766,28 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
const js_this = try js_context.valueToExistingObject(this);
const aargs = if (comptime @typeInfo(@TypeOf(args)) == .null) struct {}{} else args;
- const fields = @typeInfo(@TypeOf(aargs)).@"struct".fields;
- var js_args: [fields.len]v8.Value = undefined;
- inline for (fields, 0..) |f, i| {
- js_args[i] = try js_context.zigValueToJs(@field(aargs, f.name));
- }
- const result = self.func.castToFunction().call(js_context.v8_context, js_this, &js_args);
+ const js_args: []const v8.Value = switch (@typeInfo(@TypeOf(aargs))) {
+ .@"struct" => |s| blk: {
+ const fields = s.fields;
+ var js_args: [fields.len]v8.Value = undefined;
+ inline for (fields, 0..) |f, i| {
+ js_args[i] = try js_context.zigValueToJs(@field(aargs, f.name));
+ }
+ const cargs: [fields.len]v8.Value = js_args;
+ break :blk &cargs;
+ },
+ .pointer => blk: {
+ var values = try js_context.call_arena.alloc(v8.Value, args.len);
+ for (args, 0..) |a, i| {
+ values[i] = try js_context.zigValueToJs(a);
+ }
+ break :blk values;
+ },
+ else => @compileError("JS Function called with invalid paremter type"),
+ };
+
+ const result = self.func.castToFunction().call(js_context.v8_context, js_this, js_args);
if (result == null) {
return error.JSExecCallback;
}