diff --git a/src/browser/Browser.zig b/src/browser/Browser.zig index 09f7773d..6572b20f 100644 --- a/src/browser/Browser.zig +++ b/src/browser/Browser.zig @@ -87,7 +87,7 @@ pub fn closeSession(self: *Browser) void { } } -pub fn runMicrotasks(self: *const Browser) void { +pub fn runMicrotasks(self: *Browser) void { self.env.runMicrotasks(); } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 309285e8..e0e060cb 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -62,7 +62,8 @@ platform: *const Platform, // the global isolate isolate: js.Isolate, -contexts: std.ArrayList(*js.Context), +contexts: [64]*Context, +context_count: usize, // just kept around because we need to free it on deinit isolate_params: *v8.CreateParams, @@ -85,6 +86,8 @@ inspector: ?*Inspector, // which an be created once per isolaet. private_symbols: PrivateSymbols, +microtask_queues_are_running: bool, + pub const InitOpts = struct { with_inspector: bool = false, }; @@ -203,7 +206,8 @@ pub fn init(app: *App, opts: InitOpts) !Env { return .{ .app = app, .context_id = 0, - .contexts = .empty, + .contexts = undefined, + .context_count = 0, .isolate = isolate, .platform = &app.platform, .templates = templates, @@ -211,15 +215,16 @@ pub fn init(app: *App, opts: InitOpts) !Env { .inspector = inspector, .global_template = global_eternal, .private_symbols = private_symbols, + .microtask_queues_are_running = false, .eternal_function_templates = eternal_function_templates, }; } pub fn deinit(self: *Env) void { if (comptime IS_DEBUG) { - std.debug.assert(self.contexts.items.len == 0); + std.debug.assert(self.context_count == 0); } - for (self.contexts.items) |ctx| { + for (self.contexts[0..self.context_count]) |ctx| { ctx.deinit(); } @@ -228,8 +233,6 @@ pub fn deinit(self: *Env) void { i.deinit(allocator); } - self.contexts.deinit(allocator); - allocator.free(self.templates); allocator.free(self.eternal_function_templates); self.private_symbols.deinit(); @@ -312,14 +315,22 @@ pub fn createContext(self: *Env, page: *Page) !*Context { // a v8 context, we can get our context out v8.v8__Context__SetAlignedPointerInEmbedderData(v8_context, 1, @ptrCast(context)); - try self.contexts.append(self.app.allocator, context); + const count = self.context_count; + if (count >= self.contexts.len) { + return error.TooManyContexts; + } + self.contexts[count] = context; + self.context_count = count + 1; + return context; } pub fn destroyContext(self: *Env, context: *Context) void { - for (self.contexts.items, 0..) |ctx, i| { + for (self.contexts[0..self.context_count], 0..) |ctx, i| { if (ctx == context) { - _ = self.contexts.swapRemove(i); + // Swap with last element and decrement count + self.context_count -= 1; + self.contexts[i] = self.contexts[self.context_count]; break; } } else { @@ -339,16 +350,24 @@ pub fn destroyContext(self: *Env, context: *Context) void { context.deinit(); } -pub fn runMicrotasks(self: *const Env) void { - const v8_isolate = self.isolate.handle; - for (self.contexts.items) |ctx| { - v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate); +pub fn runMicrotasks(self: *Env) void { + if (self.microtask_queues_are_running == false) { + const v8_isolate = self.isolate.handle; + + self.microtask_queues_are_running = true; + defer self.microtask_queues_are_running = false; + + var i: usize = 0; + while (i < self.context_count) : (i += 1) { + const ctx = self.contexts[i]; + v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate); + } } } pub fn runMacrotasks(self: *Env) !?u64 { var ms_to_next_task: ?u64 = null; - for (self.contexts.items) |ctx| { + for (self.contexts[0..self.context_count]) |ctx| { if (comptime builtin.is_test == false) { // I hate this comptime check as much as you do. But we have tests // which rely on short execution before shutdown. In real world, it's diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig index 235a5fad..9a413170 100644 --- a/src/browser/webapi/Node.zig +++ b/src/browser/webapi/Node.zig @@ -713,7 +713,17 @@ pub fn normalize(self: *Node, page: *Page) !void { return self._normalize(page.call_arena, &buffer, page); } -pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, StringTooLarge, NotSupported, NotImplemented, InvalidCharacterError, CloneError, IFrameLoadError }!*Node { +const CloneError = error{ + OutOfMemory, + StringTooLarge, + NotSupported, + NotImplemented, + InvalidCharacterError, + CloneError, + IFrameLoadError, + TooManyContexts, +}; +pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) CloneError!*Node { const deep = deep_ orelse false; switch (self._type) { .cdata => |cd| {