diff --git a/src/browser/Scheduler.zig b/src/browser/Scheduler.zig index 5362c5bf..ecafbb42 100644 --- a/src/browser/Scheduler.zig +++ b/src/browser/Scheduler.zig @@ -37,8 +37,10 @@ pub fn init(allocator: Allocator) Scheduler { } pub fn reset(self: *Scheduler) void { - self.high_priority.clearRetainingCapacity(); - self.low_priority.clearRetainingCapacity(); + // Our allocator is the page arena, it's been reset. We cannot use + // clearAndRetainCapacity, since that space is no longer ours + self.high_priority.clearAndFree(); + self.low_priority.clearAndFree(); } const AddOpts = struct { diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index bcb35a80..538a814d 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -138,10 +138,7 @@ fn getThis(self: *const Function) v8.Object { return self.this orelse self.context.v8_context.getGlobal(); } -// debug/helper to print the source of the JS callback -pub fn printFunc(self: Function) !void { - const context = self.context; +pub fn src(self: *const Function) ![]const u8 { const value = self.func.castToFunction().toValue(); - const src = try js.valueToString(context.call_arena, value, context.isolate, context.v8_context); - std.debug.print("{s}\n", .{src}); + return self.context.valueToString(value, .{}); } diff --git a/src/browser/page.zig b/src/browser/page.zig index 6cdc4f96..721e4c26 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -24,6 +24,7 @@ const Allocator = std.mem.Allocator; const Dump = @import("dump.zig"); const State = @import("State.zig"); const Mime = @import("mime.zig").Mime; +const Browser = @import("browser.zig").Browser; const Session = @import("session.zig").Session; const Renderer = @import("renderer.zig").Renderer; const Window = @import("html/window.zig").Window; @@ -146,11 +147,7 @@ pub const Page = struct { self.js = try session.executor.createContext(self, true, js.GlobalMissingCallback.init(&self.polyfill_loader)); try polyfill.preload(self.arena, self.js); - try self.scheduler.add(self, runMicrotasks, 5, .{ .name = "page.microtasks" }); - // message loop must run only non-test env - if (comptime !builtin.is_test) { - try self.scheduler.add(self, runMessageLoop, 5, .{ .name = "page.messageLoop" }); - } + try self.registerBackgroundTasks(); } pub fn deinit(self: *Page) void { @@ -160,7 +157,7 @@ pub const Page = struct { self.script_manager.deinit(); } - fn reset(self: *Page) void { + fn reset(self: *Page) !void { // Force running the micro task to drain the queue. self.session.browser.env.runMicrotasks(); @@ -171,18 +168,31 @@ pub const Page = struct { self.load_state = .parsing; self.mode = .{ .pre = {} }; _ = self.session.browser.page_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 }); + + try self.registerBackgroundTasks(); } - fn runMicrotasks(ctx: *anyopaque) ?u32 { - const self: *Page = @ptrCast(@alignCast(ctx)); - self.session.browser.runMicrotasks(); - return 5; - } + fn registerBackgroundTasks(self: *Page) !void { + if (comptime builtin.is_test) { + // HTML test runner manually calls these as necessary + return; + } - fn runMessageLoop(ctx: *anyopaque) ?u32 { - const self: *Page = @ptrCast(@alignCast(ctx)); - self.session.browser.runMessageLoop(); - return 100; + try self.scheduler.add(self.session.browser, struct { + fn runMicrotasks(ctx: *anyopaque) ?u32 { + const b: *Browser = @ptrCast(@alignCast(ctx)); + b.runMicrotasks(); + return 5; + } + }.runMicrotasks, 5, .{ .name = "page.microtasks" }); + + try self.scheduler.add(self.session.browser, struct { + fn runMessageLoop(ctx: *anyopaque) ?u32 { + const b: *Browser = @ptrCast(@alignCast(ctx)); + b.runMessageLoop(); + return 100; + } + }.runMessageLoop, 5, .{ .name = "page.messageLoop" }); } pub const DumpOpts = struct { @@ -529,7 +539,7 @@ pub const Page = struct { if (self.mode != .pre) { // it's possible for navigate to be called multiple times on the // same page (via CDP). We want to reset the page between each call. - self.reset(); + try self.reset(); } log.info(.http, "navigate", .{