diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig
index e77c60ac..d45a5e69 100644
--- a/src/browser/js/Local.zig
+++ b/src/browser/js/Local.zig
@@ -120,14 +120,14 @@ pub fn exec(self: *const Local, src: []const u8, name: ?[]const u8) !js.Value {
/// https://v8.github.io/api/head/classv8_1_1ScriptCompiler.html#a3a15bb5a7dfc3f998e6ac789e6b4646a
pub fn compileFunction(
self: *const Local,
- function_body: []const u8,
+ src: anytype,
/// We tend to know how many params we'll pass; can remove the comptime if necessary.
comptime parameter_names: []const []const u8,
extensions: []const v8.Object,
) !js.Function {
// TODO: Make configurable.
const script_name = self.isolate.initStringHandle("anonymous");
- const script_source = self.isolate.initStringHandle(function_body);
+ const script_source = if (@TypeOf(src) == js.String) src.handle else self.isolate.initStringHandle(src);
var parameter_list: [parameter_names.len]*const v8.String = undefined;
inline for (0..parameter_names.len) |i| {
@@ -742,6 +742,7 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
else => unreachable,
};
},
+ js.String => return js_val.isString(),
string.String => {
const js_str = js_val.isString() orelse return null;
return try js_str.toSSO(false);
diff --git a/src/browser/tests/window/timers.html b/src/browser/tests/window/timers.html
index 1b4f4236..5c20dfa5 100644
--- a/src/browser/tests/window/timers.html
+++ b/src/browser/tests/window/timers.html
@@ -51,3 +51,33 @@
clearImmediate(-3);
testing.expectEqual(true, true);
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig
index c5673d60..745a94ae 100644
--- a/src/browser/webapi/Window.zig
+++ b/src/browser/webapi/Window.zig
@@ -253,7 +253,13 @@ pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, pag
return Fetch.init(input, options, page);
}
-pub fn setTimeout(self: *Window, cb: js.Function.Temp, delay_ms: ?u32, params: []js.Value.Temp, page: *Page) !u32 {
+const LegacyHandler = union(enum) {
+ function: js.Function.Temp,
+ string: js.String,
+};
+
+pub fn setTimeout(self: *Window, handler: LegacyHandler, delay_ms: ?u32, params: []js.Value.Temp, page: *Page) !u32 {
+ const cb = try resolveTimerHandler(handler, page);
return self.scheduleCallback(cb, delay_ms orelse 0, .{
.repeat = false,
.params = params,
@@ -262,7 +268,8 @@ pub fn setTimeout(self: *Window, cb: js.Function.Temp, delay_ms: ?u32, params: [
}, page);
}
-pub fn setInterval(self: *Window, cb: js.Function.Temp, delay_ms: ?u32, params: []js.Value.Temp, page: *Page) !u32 {
+pub fn setInterval(self: *Window, handler: LegacyHandler, delay_ms: ?u32, params: []js.Value.Temp, page: *Page) !u32 {
+ const cb = try resolveTimerHandler(handler, page);
return self.scheduleCallback(cb, delay_ms orelse 0, .{
.repeat = true,
.params = params,
@@ -271,6 +278,21 @@ pub fn setInterval(self: *Window, cb: js.Function.Temp, delay_ms: ?u32, params:
}, page);
}
+// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
+// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timerhandler
+// TimerHandler = Function or DOMString. When a string is passed, it is
+// compiled into an anonymous function body, matching how legacy browsers
+// (and all current UAs) interpret `setTimeout("foo()", 100)`.
+fn resolveTimerHandler(handler: LegacyHandler, page: *Page) !js.Function.Temp {
+ switch (handler) {
+ .function => |fun| return fun,
+ .string => |str| {
+ const fun = try page.js.local.?.compileFunction(str, &.{}, &.{});
+ return fun.temp();
+ },
+ }
+}
+
pub fn setImmediate(self: *Window, cb: js.Function.Temp, params: []js.Value.Temp, page: *Page) !u32 {
return self.scheduleCallback(cb, 0, .{
.repeat = false,