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,