diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index f903f3e1d..7cde1c6c8 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -251,10 +251,15 @@ js: *JS.Context, // An arena for the lifetime of the frame. arena: Allocator, -// An arena with a lifetime guaranteed to be for 1 invoking of a Zig function -// from JS. Best arena to use, when possible. +// An arena with a lifetime for at least the scope of one Zig invocation from +// JS. Prefer local_arena where possible. Use call_arena when allocations may +// need to call back into JS (event dispatch, forEach callback, ....) call_arena: Allocator, +// An arena with a lifetime guaranteed to be for exactly 1 invoking of a Zig +// function from JS. Best arena to use, when possible. +local_arena: Allocator, + parent: ?*Frame, window: *Window, document: *Document, @@ -306,6 +311,9 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void { const call_arena = try session.getArena(.medium, "call_arena"); errdefer session.releaseArena(call_arena); + const local_arena = try session.getArena(.medium, "local_arena"); + errdefer session.releaseArena(local_arena); + const factory = &page.factory; const document = (try factory.document(Node.Document.HTMLDocument{ ._proto = undefined, @@ -320,6 +328,7 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void { .document = document, .window = undefined, .call_arena = call_arena, + .local_arena = local_arena, ._frame_id = frame_id, ._page = page, ._session = session, @@ -382,6 +391,7 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void { .identity = &page.identity, .identity_arena = arena, .call_arena = self.call_arena, + .local_arena = self.local_arena, }); errdefer browser.env.destroyContext(self.js); @@ -480,6 +490,7 @@ pub fn deinit(self: *Frame) void { self._style_manager.deinit(); page.releaseArena(self.call_arena); + page.releaseArena(self.local_arena); } pub fn trackWorker(self: *Frame, worker: *Worker) !void { diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index d07c1b31f..f00bdbd7c 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -368,7 +368,7 @@ pub fn waitForScript(self: *Runner, frame_id: u32, src: [:0]const u8, timeout_ms defer try_catch.deinit(); const s = ls.local.compile(src, "wait_script") catch |err| { - const caught = try_catch.caughtOrError(frame.call_arena, err); + const caught = try_catch.caughtOrError(frame.local_arena, err); log.err(.app, "wait script error", .{ .err = caught }); return error.ScriptError; }; @@ -396,7 +396,7 @@ pub fn waitForScript(self: *Runner, frame_id: u32, src: [:0]const u8, timeout_ms const script = compiled.get(ls.local.isolate).bindToCurrentContext(&ls.local); const value = script.run() catch |err| { - const caught = try_catch.caughtOrError(frame.call_arena, err); + const caught = try_catch.caughtOrError(frame.local_arena, err); log.err(.app, "wait script error", .{ .err = caught }); return error.ScriptError; }; diff --git a/src/browser/ScriptManagerBase.zig b/src/browser/ScriptManagerBase.zig index 07d9e16bb..509b4523f 100644 --- a/src/browser/ScriptManagerBase.zig +++ b/src/browser/ScriptManagerBase.zig @@ -935,7 +935,7 @@ pub const Script = struct { return; } - const caught = try_catch.caughtOrError(frame.call_arena, error.Unknown); + const caught = try_catch.caughtOrError(frame.local_arena, error.Unknown); log.warn(.js, "eval script", .{ .url = url, .caught = caught, diff --git a/src/browser/frame/node_factory.zig b/src/browser/frame/node_factory.zig index 43412acb8..5665efcff 100644 --- a/src/browser/frame/node_factory.zig +++ b/src/browser/frame/node_factory.zig @@ -356,7 +356,7 @@ pub fn createElementNS(frame: *Frame, namespace: Element.Namespace, name: []cons defer try_catch.deinit(); ls.local.eval(inject_script, "inject_script") catch |err| { - const caught = try_catch.caughtOrError(frame.call_arena, err); + const caught = try_catch.caughtOrError(frame.local_arena, err); log.err(.app, "inject script error", .{ .err = caught }); }; } diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index 55daf7c32..7cda1646e 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -33,6 +33,7 @@ const v8 = js.v8; const log = lp.log; const ArenaAllocator = std.heap.ArenaAllocator; const CALL_ARENA_RETAIN = 1024 * 16; +const LOCAL_ARENA_RETAIN = 1024 * 16; const IS_DEBUG = @import("builtin").mode == .Debug; const Caller = @This(); @@ -101,6 +102,16 @@ pub fn deinit(self: *Caller) void { _ = arena.reset(.{ .retain_with_limit = CALL_ARENA_RETAIN }); } + // Unlike call_arena, local_arena is reset on _every_ return, since its + // users promise not to hold data across a nested call. In debug, free + // back to the backing allocator so a stale pointer trips the + // DebugAllocator's use-after-free detection; in release, retain a buffer + // to avoid realloc churn. + { + const local_arena: *ArenaAllocator = @ptrCast(@alignCast(ctx.local_arena.ptr)); + _ = local_arena.reset(if (comptime IS_DEBUG) .free_all else .{ .retain_with_limit = LOCAL_ARENA_RETAIN }); + } + ctx.call_depth = call_depth; ctx.local = self.prev_local; ctx.global.setJs(self.prev_context); diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 7cc777f23..c0945742e 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -100,6 +100,10 @@ arena: Allocator, // owned by the IsolatedWorld. call_arena: Allocator, +// Like call_arena, but reset on _every_ Caller.deinit rather than only at +// call_depth 0. +local_arena: Allocator, + // Because calls can be nested (i.e.a function calling a callback), // we can only reset the call_arena when call_depth == 0. If we were // to reset it within a callback, it would invalidate the data of @@ -700,7 +704,7 @@ fn importMetaResolveCallback(callback_handle: ?*const v8.FunctionCallbackInfo) c return; }; - const resolved = ctx.script_manager.resolveSpecifier(ctx.call_arena, data.base, specifier) catch { + const resolved = ctx.script_manager.resolveSpecifier(ctx.local_arena, data.base, specifier) catch { _ = isolate.throwException(isolate.createTypeError("failed to resolve module specifier")); return; }; @@ -904,7 +908,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM defer try_catch.deinit(); break :blk self.module(true, local, ms.src(), state.specifier, true) catch |err| { - const caught = try_catch.caughtOrError(self.call_arena, err); + const caught = try_catch.caughtOrError(self.local_arena, err); log.err(.js, "module compilation failed", .{ .caught = caught, .specifier = state.specifier, diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 3a37e8d4a..299038ad5 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -229,6 +229,7 @@ pub const ContextParams = struct { identity: *js.Identity, identity_arena: Allocator, call_arena: Allocator, + local_arena: Allocator, debug_name: []const u8 = "Context", }; @@ -315,6 +316,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context .handle = context_global, .templates = self.templates, .call_arena = params.call_arena, + .local_arena = params.local_arena, .microtask_queue = microtask_queue, .script_manager = if (comptime is_frame) &global._script_manager.base else &global._script_manager, .scheduler = .init(context_arena), @@ -332,6 +334,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context .page = context.page, .session = page.session, .call_arena = params.call_arena, + .local_arena = params.local_arena, ._factory = global._factory, ._scheduler = &context.scheduler, }; diff --git a/src/browser/js/Execution.zig b/src/browser/js/Execution.zig index b4fbebd8a..ee3abadf6 100644 --- a/src/browser/js/Execution.zig +++ b/src/browser/js/Execution.zig @@ -51,6 +51,7 @@ js: *Context, buf: []u8, arena: Allocator, call_arena: Allocator, +local_arena: Allocator, page: *Page, session: *Session, diff --git a/src/browser/markdown.zig b/src/browser/markdown.zig index 50c0e26cc..b56d25bb3 100644 --- a/src/browser/markdown.zig +++ b/src/browser/markdown.zig @@ -381,7 +381,7 @@ const Context = struct { if (!info.has_visible and label == null and href_raw == null) return; - const href = if (href_raw) |h| URL.resolve(frame.call_arena, frame.base(), h, .{ .encoding = frame.charset }) catch h else null; + const href = if (href_raw) |h| URL.resolve(frame.local_arena, frame.base(), h, .{ .encoding = frame.charset }) catch h else null; if (info.has_block) { try self.renderChildren(el.asNode()); diff --git a/src/browser/webapi/CSS.zig b/src/browser/webapi/CSS.zig index 6b560a5a4..00bd1a952 100644 --- a/src/browser/webapi/CSS.zig +++ b/src/browser/webapi/CSS.zig @@ -65,7 +65,7 @@ pub fn escape(value: []const u8, frame: *Frame) ![]const u8 { return value; } - const result = try frame.call_arena.alloc(u8, out_len); + const result = try frame.local_arena.alloc(u8, out_len); var pos: usize = 0; if (needsEscape(true, first)) { diff --git a/src/browser/webapi/CryptoKey.zig b/src/browser/webapi/CryptoKey.zig index 3874b3fb9..f85c7ce20 100644 --- a/src/browser/webapi/CryptoKey.zig +++ b/src/browser/webapi/CryptoKey.zig @@ -190,7 +190,7 @@ pub fn getUsages(self: *const CryptoKey, exec: *const Execution) ![]const []cons n += 1; } } - return exec.call_arena.dupe([]const u8, buf[0..n]); + return exec.local_arena.dupe([]const u8, buf[0..n]); } pub const JsApi = struct { diff --git a/src/browser/webapi/DOMMatrixReadOnly.zig b/src/browser/webapi/DOMMatrixReadOnly.zig index ca39901a7..9da9975ca 100644 --- a/src/browser/webapi/DOMMatrixReadOnly.zig +++ b/src/browser/webapi/DOMMatrixReadOnly.zig @@ -627,7 +627,7 @@ pub fn inverse(self: *const DOMMatrixReadOnly, page: *Page) !*DOMMatrix { } pub fn toFloat32Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) !js.TypedArray(f32) { - const out = try exec.call_arena.alloc(f32, 16); + const out = try exec.local_arena.alloc(f32, 16); for (0..16) |i| { out[i] = @floatCast(self._m[i]); } @@ -635,7 +635,7 @@ pub fn toFloat32Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) } pub fn toFloat64Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) !js.TypedArray(f64) { - const out = try exec.call_arena.dupe(f64, &self._m); + const out = try exec.local_arena.dupe(f64, &self._m); return .{ .values = out }; } @@ -648,7 +648,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co return error.InvalidStateError; } } - return std.fmt.allocPrint(exec.call_arena, "matrix({d}, {d}, {d}, {d}, {d}, {d})", .{ + return std.fmt.allocPrint(exec.local_arena, "matrix({d}, {d}, {d}, {d}, {d}, {d})", .{ m[0], m[1], m[4], m[5], m[12], m[13], }); } @@ -657,7 +657,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co return error.InvalidStateError; } } - return std.fmt.allocPrint(exec.call_arena, "matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{ + return std.fmt.allocPrint(exec.local_arena, "matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], diff --git a/src/browser/webapi/DataTransfer.zig b/src/browser/webapi/DataTransfer.zig index d53eb07d5..b8505d42c 100644 --- a/src/browser/webapi/DataTransfer.zig +++ b/src/browser/webapi/DataTransfer.zig @@ -102,7 +102,7 @@ fn normalizeFormat(arena: Allocator, format: []const u8) ![]const u8 { } pub fn getData(self: *const DataTransfer, format: []const u8, frame: *Frame) ![]const u8 { - const norm = try normalizeFormat(frame.call_arena, format); + const norm = try normalizeFormat(frame.local_arena, format); for (self._items.items) |it| { if (it._kind == .string and std.mem.eql(u8, it._type, norm)) { return it._payload.string; @@ -127,7 +127,7 @@ pub fn setData(self: *DataTransfer, format: []const u8, data: []const u8) !void pub fn clearData(self: *DataTransfer, format_: ?[]const u8, frame: *Frame) !void { if (format_) |format| { - const norm = try normalizeFormat(frame.call_arena, format); + const norm = try normalizeFormat(frame.local_arena, format); var i: usize = 0; while (i < self._items.items.len) { const it = self._items.items[i]; @@ -222,14 +222,14 @@ pub fn getTypes(self: *DataTransfer, frame: *Frame) ![][]const u8 { var has_files = false; for (self._items.items) |it| { switch (it._kind) { - .string => try out.append(frame.call_arena, it._type), + .string => try out.append(frame.local_arena, it._type), .file => has_files = true, } } if (has_files) { - try out.append(frame.call_arena, "Files"); + try out.append(frame.local_arena, "Files"); } - return out.toOwnedSlice(frame.call_arena); + return out.toOwnedSlice(frame.local_arena); } pub fn getDropEffect(self: *const DataTransfer) []const u8 { diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index 9f02ce08e..1913fd55f 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -171,7 +171,7 @@ pub fn setDomain(self: *Document, value: []const u8) !void { const doc_frame = self._frame orelse return error.SecurityError; const origin = doc_frame.origin orelse return error.SecurityError; - const arena = doc_frame.call_arena; + const arena = doc_frame.local_arena; const requested = if (idna.needsAscii(value)) try idna.toAscii(arena, value) else value; // Validate against the current effective domain. Once relaxed, @@ -193,7 +193,7 @@ pub fn setDomain(self: *Document, value: []const u8) !void { pub fn getCookie(_: *Document, frame: *Frame) ![]const u8 { var buf: std.ArrayList(u8) = .empty; - try frame._session.cookie_jar.forRequest(frame.url, buf.writer(frame.call_arena), .{ + try frame._session.cookie_jar.forRequest(frame.url, buf.writer(frame.local_arena), .{ .is_http = false, .is_navigation = true, }); @@ -738,7 +738,7 @@ pub fn elementFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) !?*Eleme const root = self.asNode(); var stack: std.ArrayList(*Node) = .empty; - try stack.append(frame.call_arena, root); + try stack.append(frame.local_arena, root); var visibility_cache: Element.VisibilityCache = .{}; var preorder_index: f64 = 0; @@ -770,7 +770,7 @@ pub fn elementFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) !?*Eleme // Add children to stack in reverse order so we process them in document order var child = node.lastChild(); while (child) |c| { - try stack.append(frame.call_arena, c); + try stack.append(frame.local_arena, c); child = c.previousSibling(); } } @@ -783,7 +783,7 @@ pub fn elementsFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) ![]cons var current: ?*Element = (try self.elementFromPoint(x, y, frame)) orelse return &.{}; var result: std.ArrayList(*Element) = .empty; while (current) |el| { - try result.append(frame.call_arena, el); + try result.append(frame.local_arena, el); current = el.parentElement(); } return result.items; diff --git a/src/browser/webapi/DocumentFragment.zig b/src/browser/webapi/DocumentFragment.zig index da09e9bbc..ccb53f50a 100644 --- a/src/browser/webapi/DocumentFragment.zig +++ b/src/browser/webapi/DocumentFragment.zig @@ -222,7 +222,7 @@ pub const JsApi = struct { pub const innerHTML = bridge.accessor(_getInnerHTML, _setInnerHTML, .{ .ce_reactions = true }); fn _getInnerHTML(self: *DocumentFragment, frame: *Frame) ![]const u8 { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.getInnerHTML(&buf.writer, frame); return buf.written(); } diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index f4c16641d..4530872a3 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -1280,7 +1280,7 @@ pub fn getClientRects(self: *Element, frame: *Frame) ![]DOMRect { if (!self.checkVisibilityCached(null, frame)) { return &.{}; } - const rects = try frame.call_arena.alloc(DOMRect, 1); + const rects = try frame.local_arena.alloc(DOMRect, 1); rects[0] = self.getBoundingClientRectForVisible(frame); return rects; } @@ -1924,14 +1924,16 @@ pub const JsApi = struct { pub const innerText = bridge.accessor(_innerText, Element.setInnerText, .{ .ce_reactions = true }); fn _innerText(self: *Element, frame: *Frame) ![]const u8 { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.getInnerText(&buf.writer, frame); return buf.written(); } pub const outerHTML = bridge.accessor(_getOuterHTML, _setOuterHTML, .{ .ce_reactions = true }); fn _getOuterHTML(self: *Element, frame: *Frame) ![]const u8 { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + // local_arena: serialization is read-only and the returned string is + // converted to v8 before this call returns. No JS runs in between. + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.getOuterHTML(&buf.writer, frame); return buf.written(); } @@ -1942,7 +1944,9 @@ pub const JsApi = struct { pub const innerHTML = bridge.accessor(_getInnerHTML, _setInnerHTML, .{ .ce_reactions = true }); fn _getInnerHTML(self: *Element, frame: *Frame) ![]const u8 { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + // local_arena: read-only serialization, result converted to v8 before + // returning; no JS runs in between. + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.getInnerHTML(&buf.writer, frame); return buf.written(); } diff --git a/src/browser/webapi/HTMLDocument.zig b/src/browser/webapi/HTMLDocument.zig index 4d2ccb9c2..ed3f6282b 100644 --- a/src/browser/webapi/HTMLDocument.zig +++ b/src/browser/webapi/HTMLDocument.zig @@ -103,7 +103,7 @@ pub fn getTitle(self: *HTMLDocument, frame: *Frame) ![]const u8 { return ""; }; - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try title_element.asNode().getTextContent(&buf.writer); const text = buf.written(); @@ -114,7 +114,7 @@ pub fn getTitle(self: *HTMLDocument, frame: *Frame) ![]const u8 { var started = false; var in_whitespace = false; var result: std.ArrayList(u8) = .empty; - try result.ensureTotalCapacity(frame.call_arena, text.len); + try result.ensureTotalCapacity(frame.local_arena, text.len); for (text) |c| { const is_ascii_ws = c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == '\x0C'; diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index 579a4192e..69bdfcd64 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -191,7 +191,7 @@ pub fn disconnect(self: *IntersectionObserver, frame: *Frame) void { } pub fn takeRecords(self: *IntersectionObserver, frame: *Frame) ![]*IntersectionObserverEntry { - const entries = try frame.call_arena.dupe(*IntersectionObserverEntry, self._pending_entries.items); + const entries = try frame.local_arena.dupe(*IntersectionObserverEntry, self._pending_entries.items); self._pending_entries.clearRetainingCapacity(); return entries; } diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig index 5b696cc97..6d3dab74e 100644 --- a/src/browser/webapi/MutationObserver.zig +++ b/src/browser/webapi/MutationObserver.zig @@ -188,7 +188,7 @@ pub fn disconnect(self: *MutationObserver, frame: *Frame) void { } pub fn takeRecords(self: *MutationObserver, frame: *Frame) ![]*MutationRecord { - const records = try frame.call_arena.dupe(*MutationRecord, self._pending_records.items); + const records = try frame.local_arena.dupe(*MutationRecord, self._pending_records.items); self._pending_records.clearRetainingCapacity(); return records; } diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig index e56209dd6..3cb562342 100644 --- a/src/browser/webapi/Node.zig +++ b/src/browser/webapi/Node.zig @@ -908,7 +908,7 @@ pub fn setData(self: *Node, data: []const u8, frame: *Frame) !void { pub fn normalize(self: *Node, frame: *Frame) !void { var buffer: std.ArrayList(u8) = .empty; - return self._normalize(frame.call_arena, &buffer, frame); + return self._normalize(frame.local_arena, &buffer, frame); } const CloneError = error{ @@ -1311,7 +1311,9 @@ pub const JsApi = struct { // cdata and attributes can return value directly, avoiding the copy switch (self._type) { .element, .document_fragment => { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + // local_arena: read-only text collection, result converted to + // v8 before returning; no JS runs in between. + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.getTextContent(&buf.writer); return buf.written(); }, diff --git a/src/browser/webapi/Performance.zig b/src/browser/webapi/Performance.zig index 796376c04..b718f5aa5 100644 --- a/src/browser/webapi/Performance.zig +++ b/src/browser/webapi/Performance.zig @@ -215,11 +215,11 @@ pub fn getEntries(self: *const Performance) []*Entry { } pub fn getEntriesByType(self: *const Performance, entry_type: []const u8, exec: *const Execution) ![]const *Entry { - return filterEntriesByType(exec.call_arena, self._entries.items, entry_type); + return filterEntriesByType(exec.local_arena, self._entries.items, entry_type); } pub fn getEntriesByName(self: *const Performance, name: []const u8, entry_type: ?[]const u8, exec: *const Execution) ![]const *Entry { - return filterEntriesByName(exec.call_arena, self._entries.items, name, entry_type); + return filterEntriesByName(exec.local_arena, self._entries.items, name, entry_type); } // Also used by PerformanceObserver diff --git a/src/browser/webapi/PerformanceObserver.zig b/src/browser/webapi/PerformanceObserver.zig index 1c879e38a..c5f017e0b 100644 --- a/src/browser/webapi/PerformanceObserver.zig +++ b/src/browser/webapi/PerformanceObserver.zig @@ -216,11 +216,11 @@ pub const EntryList = struct { } pub fn getEntriesByType(self: *const EntryList, entry_type: []const u8, exec: *Execution) ![]const *Performance.Entry { - return Performance.filterEntriesByType(exec.call_arena, self._entries, entry_type); + return Performance.filterEntriesByType(exec.local_arena, self._entries, entry_type); } pub fn getEntriesByName(self: *const EntryList, name: []const u8, entry_type: ?[]const u8, exec: *Execution) ![]const *Performance.Entry { - return Performance.filterEntriesByName(exec.call_arena, self._entries, name, entry_type); + return Performance.filterEntriesByName(exec.local_arena, self._entries, name, entry_type); } pub const JsApi = struct { diff --git a/src/browser/webapi/Range.zig b/src/browser/webapi/Range.zig index a617125e3..03994dfc5 100644 --- a/src/browser/webapi/Range.zig +++ b/src/browser/webapi/Range.zig @@ -569,7 +569,7 @@ pub fn createContextualFragment(self: *const Range, html: []const u8, frame: *Fr pub fn toString(self: *const Range, frame: *Frame) ![]const u8 { // Simplified implementation: just extract text content - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.writeTextContent(&buf.writer); return buf.written(); } diff --git a/src/browser/webapi/SubtleCrypto.zig b/src/browser/webapi/SubtleCrypto.zig index 4e6f48ef6..662a525cd 100644 --- a/src/browser/webapi/SubtleCrypto.zig +++ b/src/browser/webapi/SubtleCrypto.zig @@ -185,7 +185,7 @@ pub fn importKey( const k = jwk.k orelse { return local.rejectPromise(.{ .dom_exception = .{ .err = error.DataError } }); }; - break :blk common.base64Decode(exec.call_arena, k) catch |err| switch (err) { + break :blk common.base64Decode(exec.local_arena, k) catch |err| switch (err) { error.DataError => return local.rejectPromise(.{ .dom_exception = .{ .err = error.DataError } }), else => |e| return e, }; @@ -309,13 +309,13 @@ fn exportJwk(key: *CryptoKey, exec: *const Execution) !js.Promise { // The `alg` registry value depends on the algorithm and key length. const alg: []const u8 = switch (key._type) { - .aes => try std.fmt.allocPrint(exec.call_arena, "A{d}{s}", .{ + .aes => try std.fmt.allocPrint(exec.local_arena, "A{d}{s}", .{ key._key.len * 8, key._algorithm.name[4..], // strip "AES-" }), .hmac => blk: { const hash: []const u8 = key._algorithm.hash orelse "SHA-"; - break :blk try std.fmt.allocPrint(exec.call_arena, "HS{s}", .{hash[4..]}); // strip "SHA-" + break :blk try std.fmt.allocPrint(exec.local_arena, "HS{s}", .{hash[4..]}); // strip "SHA-" }, else => { log.warn(.not_implemented, "SubtleCrypto.exportKey", .{ .format = "jwk", .type = key._type }); @@ -324,7 +324,7 @@ fn exportJwk(key: *CryptoKey, exec: *const Execution) !js.Promise { }; return local.resolvePromise(JwkSecret{ - .k = try common.base64Encode(exec.call_arena, key._key), + .k = try common.base64Encode(exec.local_arena, key._key), .alg = alg, .ext = key._extractable, .key_ops = try key.getUsages(exec), diff --git a/src/browser/webapi/URL.zig b/src/browser/webapi/URL.zig index dbffa97f6..ace7d6796 100644 --- a/src/browser/webapi/URL.zig +++ b/src/browser/webapi/URL.zig @@ -174,7 +174,7 @@ pub fn getSearch(self: *const URL, exec: *const Execution) ![]const u8 { return ""; } - var buf = std.Io.Writer.Allocating.init(exec.call_arena); + var buf = std.Io.Writer.Allocating.init(exec.local_arena); try buf.writer.writeByte('?'); try search_params.toString(&buf.writer); return buf.written(); @@ -262,7 +262,7 @@ pub fn getOrigin(self: *const URL, exec: *const Execution) ![]const u8 { const origin = U.url_get_origin(self._url); defer origin.deinit(); - return exec.call_arena.dupe(u8, origin.slice()); + return exec.local_arena.dupe(u8, origin.slice()); } pub fn setHref(self: *URL, value: []const u8, exec: *const Execution) !void { @@ -288,7 +288,7 @@ pub fn toString(self: *const URL, exec: *const Execution) ![]const u8 { if (search_params.getSize() == 0) { U.url_set_query_to_null(self._url); } else { - var buf = std.Io.Writer.Allocating.init(exec.call_arena); + var buf = std.Io.Writer.Allocating.init(exec.local_arena); defer buf.deinit(); try search_params.toString(&buf.writer); const query = buf.written(); diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index c1616f5b0..31f79c0c7 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -696,11 +696,11 @@ pub fn postMessage(self: *Window, message: js.Value, target_origin: ?[]const u8, const base64 = @import("encoding/base64.zig"); pub fn btoa(_: *const Window, input: base64.BinInput, frame: *Frame) ![]const u8 { - return base64.encode(frame.call_arena, input); + return base64.encode(frame.local_arena, input); } pub fn atob(_: *const Window, input: base64.BinInput, frame: *Frame) !js.String.OneByte { - const decoded = try base64.decode(frame.call_arena, input); + const decoded = try base64.decode(frame.local_arena, input); return .{ .bytes = decoded }; } diff --git a/src/browser/webapi/WorkerGlobalScope.zig b/src/browser/webapi/WorkerGlobalScope.zig index b860996b4..ab965648e 100644 --- a/src/browser/webapi/WorkerGlobalScope.zig +++ b/src/browser/webapi/WorkerGlobalScope.zig @@ -70,6 +70,7 @@ _http_owner: HttpClient.Owner = .{}, arena: Allocator, call_arena: Allocator, +local_arena: Allocator, url: [:0]const u8, // Same-origin constraint: a worker's origin is inherited from its parent frame. origin: ?[]const u8 = null, @@ -129,6 +130,9 @@ pub fn init( const call_arena = try session.getArena(.small, "WorkerGlobalScope.call_arena"); errdefer session.releaseArena(call_arena); + const local_arena = try session.getArena(.small, "WorkerGlobalScope.local_arena"); + errdefer session.releaseArena(local_arena); + const factory = frame._factory; const self = try factory.eventTargetWithAllocator(arena, WorkerGlobalScope{ .url = url, @@ -136,6 +140,7 @@ pub fn init( .origin = frame.origin, .js = undefined, .call_arena = call_arena, + .local_arena = local_arena, ._frame = frame, ._page = frame._page, ._session = session, @@ -162,6 +167,7 @@ pub fn init( self.js = try session.browser.env.createWorkerContext(self, .{ .call_arena = call_arena, + .local_arena = local_arena, .identity_arena = arena, .identity = &self._identity, }); @@ -191,6 +197,7 @@ pub fn deinit(self: *WorkerGlobalScope) void { } browser.env.destroyContext(self.js); session.releaseArena(self.call_arena); + session.releaseArena(self.local_arena); } pub fn base(self: *const WorkerGlobalScope) [:0]const u8 { diff --git a/src/browser/webapi/XMLSerializer.zig b/src/browser/webapi/XMLSerializer.zig index b7f955aec..683afa4ac 100644 --- a/src/browser/webapi/XMLSerializer.zig +++ b/src/browser/webapi/XMLSerializer.zig @@ -34,7 +34,7 @@ pub fn init() XMLSerializer { pub fn serializeToString(self: *const XMLSerializer, node: *Node, frame: *Frame) ![]const u8 { _ = self; - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); if (node.is(Node.Document)) |doc| { try dump.root(doc, .{ .shadow = .skip }, &buf.writer, frame); } else { diff --git a/src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig b/src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig index de59a2e13..46ff54ebc 100644 --- a/src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig +++ b/src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig @@ -34,7 +34,7 @@ const OffscreenCanvasRenderingContext2D = @This(); _fill_style: color.RGBA = color.RGBA.Named.black, pub fn getFillStyle(self: *const OffscreenCanvasRenderingContext2D, exec: *Execution) ![]const u8 { - var w = std.Io.Writer.Allocating.init(exec.call_arena); + var w = std.Io.Writer.Allocating.init(exec.local_arena); try self._fill_style.format(&w.writer); return w.written(); } diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig index 88acb1667..ad1114f6e 100644 --- a/src/browser/webapi/collections/DOMTokenList.zig +++ b/src/browser/webapi/collections/DOMTokenList.zig @@ -45,7 +45,7 @@ const Lookup = std.StringArrayHashMapUnmanaged(void); const WHITESPACE = " \t\n\r\x0C"; pub fn length(self: *const DOMTokenList, frame: *Frame) !u32 { - const tokens = try self.getTokens(frame.call_arena); + const tokens = try self.getTokens(frame.local_arena); return @intCast(tokens.count()); } @@ -53,7 +53,7 @@ pub fn length(self: *const DOMTokenList, frame: *Frame) !u32 { pub fn item(self: *const DOMTokenList, index: usize, frame: *Frame) !?[]const u8 { var i: usize = 0; - const allocator = frame.call_arena; + const allocator = frame.local_arena; var seen: std.StringArrayHashMapUnmanaged(void) = .empty; var it = std.mem.tokenizeAny(u8, self.getValue(), WHITESPACE); @@ -84,7 +84,7 @@ pub fn add(self: *DOMTokenList, tokens: []const []const u8, frame: *Frame) !void try validateToken(token); } - const allocator = frame.call_arena; + const allocator = frame.local_arena; var lookup = try self.getTokens(allocator); try lookup.ensureUnusedCapacity(allocator, tokens.len); @@ -100,7 +100,7 @@ pub fn remove(self: *DOMTokenList, tokens: []const []const u8, frame: *Frame) !v try validateToken(token); } - var lookup = try self.getTokens(frame.call_arena); + var lookup = try self.getTokens(frame.local_arena); for (tokens) |token| { _ = lookup.orderedRemove(token); } @@ -151,8 +151,8 @@ pub fn replace(self: *DOMTokenList, old_token: []const u8, new_token: []const u8 return error.InvalidCharacterError; } - const allocator = frame.call_arena; - var lookup = try self.getTokens(frame.call_arena); + const allocator = frame.local_arena; + var lookup = try self.getTokens(allocator); // Check if old_token exists if (!lookup.contains(old_token)) { diff --git a/src/browser/webapi/crypto/AES.zig b/src/browser/webapi/crypto/AES.zig index 0edc4c22c..3114671ef 100644 --- a/src/browser/webapi/crypto/AES.zig +++ b/src/browser/webapi/crypto/AES.zig @@ -260,7 +260,7 @@ fn ctr( std.mem.writeInt(u128, &wrapped, readBlock(counter) & ~mask, .big); const second = try cbcOrCtr(cipher, key, &wrapped, data[split..], encrypting, false, exec); - const out = try exec.call_arena.alloc(u8, data.len); + const out = try exec.local_arena.alloc(u8, data.len); @memcpy(out[0..split], first); @memcpy(out[split..], second); return out; @@ -308,7 +308,7 @@ fn cbcOrCtr( } // Block ciphers may emit up to one extra block on top of the input. - const out = try exec.call_arena.alloc(u8, data.len + 16); + const out = try exec.local_arena.alloc(u8, data.len + 16); var out_len: c_int = 0; if (cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(data.ptr), @intCast(data.len), encrypting) != 1) { return error.OperationError; @@ -365,7 +365,7 @@ fn gcm( } if (encrypting) { - const out = try exec.call_arena.alloc(u8, data.len + tag_len); + const out = try exec.local_arena.alloc(u8, data.len + tag_len); var out_len: c_int = 0; if (data.len > 0 and cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(data.ptr), @intCast(data.len), true) != 1) { return error.OperationError; @@ -391,7 +391,7 @@ fn gcm( return error.OperationError; } - const out = try exec.call_arena.alloc(u8, ct.len + 16); + const out = try exec.local_arena.alloc(u8, ct.len + 16); var out_len: c_int = 0; if (ct.len > 0 and cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(ct.ptr), @intCast(ct.len), false) != 1) { return error.OperationError; diff --git a/src/browser/webapi/crypto/EC.zig b/src/browser/webapi/crypto/EC.zig index afa38259b..ab1f753ba 100644 --- a/src/browser/webapi/crypto/EC.zig +++ b/src/browser/webapi/crypto/EC.zig @@ -236,7 +236,7 @@ pub fn deriveBits( if (crypto.EVP_PKEY_derive(ctx, null, &secret_len) != 1 or secret_len == 0) { return error.OperationError; } - const secret = try exec.call_arena.alloc(u8, secret_len); + const secret = try exec.local_arena.alloc(u8, secret_len); if (crypto.EVP_PKEY_derive(ctx, secret.ptr, &secret_len) != 1) { return error.OperationError; } diff --git a/src/browser/webapi/crypto/HMAC.zig b/src/browser/webapi/crypto/HMAC.zig index a897a02db..625ff7cbf 100644 --- a/src/browser/webapi/crypto/HMAC.zig +++ b/src/browser/webapi/crypto/HMAC.zig @@ -151,7 +151,7 @@ pub fn sign( return resolver.promise(); } - const buffer = try exec.call_arena.alloc(u8, crypto.EVP_MD_size(crypto_key.getDigest())); + const buffer = try exec.local_arena.alloc(u8, crypto.EVP_MD_size(crypto_key.getDigest())); var out_len: u32 = 0; // Try to sign. _ = crypto.HMAC( @@ -163,7 +163,6 @@ pub fn sign( buffer.ptr, &out_len, ) orelse { - exec.call_arena.free(buffer); // Failure. resolver.rejectError("HMAC.sign", .{ .dom_exception = .{ .err = error.InvalidAccessError } }); return resolver.promise(); diff --git a/src/browser/webapi/crypto/KDF.zig b/src/browser/webapi/crypto/KDF.zig index 98aa87b63..5f312f1df 100644 --- a/src/browser/webapi/crypto/KDF.zig +++ b/src/browser/webapi/crypto/KDF.zig @@ -64,7 +64,7 @@ pub fn pbkdf2( ) Error![]const u8 { try checkBaseKey(base_key, "PBKDF2", usage_ok); const digest = crypto.findDigest(params.hash.name()) catch return error.NotSupported; - const out = try exec.call_arena.alloc(u8, try outputLen(length)); + const out = try exec.local_arena.alloc(u8, try outputLen(length)); // A zero-length derivation is valid and yields an empty buffer; the C // routines reject a zero output length, so short-circuit here. if (out.len == 0) { @@ -97,7 +97,7 @@ pub fn hkdf( ) Error![]const u8 { try checkBaseKey(base_key, "HKDF", usage_ok); const digest = crypto.findDigest(params.hash.name()) catch return error.NotSupported; - const out = try exec.call_arena.alloc(u8, try outputLen(length)); + const out = try exec.local_arena.alloc(u8, try outputLen(length)); // A zero-length derivation is valid and yields an empty buffer; the C // routines reject a zero output length, so short-circuit here. if (out.len == 0) { diff --git a/src/browser/webapi/crypto/X25519.zig b/src/browser/webapi/crypto/X25519.zig index 4b8b74e17..631b6687c 100644 --- a/src/browser/webapi/crypto/X25519.zig +++ b/src/browser/webapi/crypto/X25519.zig @@ -138,8 +138,8 @@ pub fn deriveBits( return error.Internal; } - const derived_key = try exec.call_arena.alloc(u8, 32); - errdefer exec.call_arena.free(derived_key); + const derived_key = try exec.local_arena.alloc(u8, 32); + errdefer exec.local_arena.free(derived_key); var out_key_len: usize = derived_key.len; const result = crypto.EVP_PKEY_derive(ctx, derived_key.ptr, &out_key_len); diff --git a/src/browser/webapi/css/CSSStyleDeclaration.zig b/src/browser/webapi/css/CSSStyleDeclaration.zig index 393243187..c0f2f7f49 100644 --- a/src/browser/webapi/css/CSSStyleDeclaration.zig +++ b/src/browser/webapi/css/CSSStyleDeclaration.zig @@ -151,7 +151,7 @@ fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value: const normalized = normalizePropertyName(property_name, &frame.buf); // Normalize the value for canonical serialization - const normalized_value = try normalizePropertyValue(frame.call_arena, normalized, value); + const normalized_value = try normalizePropertyValue(frame.local_arena, normalized, value); // Find existing property if (self.findProperty(.wrap(normalized))) |existing| { @@ -206,7 +206,7 @@ pub fn setFloat(self: *CSSStyleDeclaration, value_: ?[]const u8, frame: *Frame) } pub fn getCssText(self: *const CSSStyleDeclaration, frame: *Frame) ![]const u8 { - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try self.format(&buf.writer); return buf.written(); } diff --git a/src/browser/webapi/css/CSSStyleRule.zig b/src/browser/webapi/css/CSSStyleRule.zig index e3450b2b4..f28f0d011 100644 --- a/src/browser/webapi/css/CSSStyleRule.zig +++ b/src/browser/webapi/css/CSSStyleRule.zig @@ -38,7 +38,7 @@ pub fn getStyle(self: *CSSStyleRule, frame: *Frame) !*CSSStyleProperties { pub fn getCssText(self: *CSSStyleRule, frame: *Frame) ![]const u8 { const style_props = try self.getStyle(frame); const style = style_props.asCSSStyleDeclaration(); - var buf = std.Io.Writer.Allocating.init(frame.call_arena); + var buf = std.Io.Writer.Allocating.init(frame.local_arena); try buf.writer.print("{s} {{ ", .{self._selector_text}); try style.format(&buf.writer); try buf.writer.writeAll(" }"); diff --git a/src/browser/webapi/css/CSSStyleSheet.zig b/src/browser/webapi/css/CSSStyleSheet.zig index c21c5d000..dd08118ef 100644 --- a/src/browser/webapi/css/CSSStyleSheet.zig +++ b/src/browser/webapi/css/CSSStyleSheet.zig @@ -66,7 +66,7 @@ pub fn getCssRules(self: *CSSStyleSheet, frame: *Frame) !*CSSRuleList { if (self.getOwnerNode()) |owner| { if (owner.is(Element.Html.Style)) |style| { - const text = try style.asNode().getTextContentAlloc(frame.call_arena); + const text = try style.asNode().getTextContentAlloc(frame.local_arena); try self.replaceSync(text, frame); } } diff --git a/src/browser/webapi/element/DOMStringMap.zig b/src/browser/webapi/element/DOMStringMap.zig index 7fd23f34d..a7fff6551 100644 --- a/src/browser/webapi/element/DOMStringMap.zig +++ b/src/browser/webapi/element/DOMStringMap.zig @@ -32,17 +32,17 @@ const DOMStringMap = @This(); _element: *Element, fn getProperty(self: *DOMStringMap, name: String, frame: *Frame) !?String { - const attr_name = try camelToKebab(frame.call_arena, name); + const attr_name = try camelToKebab(frame.local_arena, name); return try self._element.getAttribute(attr_name, frame); } fn setProperty(self: *DOMStringMap, name: String, value: String, frame: *Frame) !void { - const attr_name = try camelToKebab(frame.call_arena, name); + const attr_name = try camelToKebab(frame.local_arena, name); return self._element.setAttributeSafe(attr_name, value, frame); } fn deleteProperty(self: *DOMStringMap, name: String, frame: *Frame) !void { - const attr_name = try camelToKebab(frame.call_arena, name); + const attr_name = try camelToKebab(frame.local_arena, name); try self._element.removeAttribute(attr_name, frame); } diff --git a/src/browser/webapi/element/Html.zig b/src/browser/webapi/element/Html.zig index eb55b4bc6..d643a43c4 100644 --- a/src/browser/webapi/element/Html.zig +++ b/src/browser/webapi/element/Html.zig @@ -1598,7 +1598,7 @@ fn mergeTextNodes(left_node: *Node, right_node: *Node, frame: *Frame) !bool { // both nodes are Text nodes - const merged = try std.mem.concat(frame.call_arena, u8, &.{ left.getData().str(), right.getData().str() }); + const merged = try std.mem.concat(frame.local_arena, u8, &.{ left.getData().str(), right.getData().str() }); // set the left node to the merged value try left.setData(merged, frame); @@ -1610,7 +1610,7 @@ fn mergeTextNodes(left_node: *Node, right_node: *Node, frame: *Frame) !bool { } fn renderedTextFragment(value: []const u8, frame: *Frame) ![]Node.NodeOrText { - const arena = frame.call_arena; + const arena = frame.local_arena; var nodes: std.ArrayList(Node.NodeOrText) = .empty; var rest = value; diff --git a/src/browser/webapi/element/html/Anchor.zig b/src/browser/webapi/element/html/Anchor.zig index 0a55c457f..8d36299d5 100644 --- a/src/browser/webapi/element/html/Anchor.zig +++ b/src/browser/webapi/element/html/Anchor.zig @@ -64,7 +64,7 @@ pub fn setTarget(self: *Anchor, value: []const u8, frame: *Frame) !void { pub fn getOrigin(self: *Anchor, frame: *Frame) ![]const u8 { const href = try getResolvedHref(self, frame) orelse return ""; - return (try URL.getOrigin(frame.call_arena, href)) orelse "null"; + return (try URL.getOrigin(frame.local_arena, href)) orelse "null"; } pub fn getHost(self: *Anchor, frame: *Frame) ![]const u8 { diff --git a/src/browser/webapi/element/html/Area.zig b/src/browser/webapi/element/html/Area.zig index b58f5fe44..0deaa70ad 100644 --- a/src/browser/webapi/element/html/Area.zig +++ b/src/browser/webapi/element/html/Area.zig @@ -51,7 +51,7 @@ pub fn setHref(self: *Area, value: []const u8, frame: *Frame) !void { pub fn getOrigin(self: *Area, frame: *Frame) ![]const u8 { const href = try getResolvedHref(self, frame) orelse return ""; - return (try URL.getOrigin(frame.call_arena, href)) orelse "null"; + return (try URL.getOrigin(frame.local_arena, href)) orelse "null"; } pub fn getHost(self: *Area, frame: *Frame) ![]const u8 { diff --git a/src/browser/webapi/element/html/Base.zig b/src/browser/webapi/element/html/Base.zig index ee47c0453..3e25dc8f4 100644 --- a/src/browser/webapi/element/html/Base.zig +++ b/src/browser/webapi/element/html/Base.zig @@ -32,7 +32,7 @@ pub fn getHref(self: *Base, frame: *Frame) ![]const u8 { return ""; } const owner = element.asConstNode().ownerFrame(frame); - return URL.resolve(frame.call_arena, owner.url, href, .{}); + return URL.resolve(frame.local_arena, owner.url, href, .{}); } pub fn setHref(self: *Base, value: []const u8, frame: *Frame) !void { diff --git a/src/browser/webapi/element/html/Canvas.zig b/src/browser/webapi/element/html/Canvas.zig index 05acdee2f..775afa65f 100644 --- a/src/browser/webapi/element/html/Canvas.zig +++ b/src/browser/webapi/element/html/Canvas.zig @@ -49,7 +49,7 @@ pub fn getWidth(self: *const Canvas) u32 { } pub fn setWidth(self: *Canvas, value: u32, frame: *Frame) !void { - const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value}); + const str = try std.fmt.allocPrint(frame.local_arena, "{d}", .{value}); try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(str), frame); } @@ -59,7 +59,7 @@ pub fn getHeight(self: *const Canvas) u32 { } pub fn setHeight(self: *Canvas, value: u32, frame: *Frame) !void { - const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value}); + const str = try std.fmt.allocPrint(frame.local_arena, "{d}", .{value}); try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(str), frame); } diff --git a/src/browser/webapi/element/html/Input.zig b/src/browser/webapi/element/html/Input.zig index c665daa96..3518f0d2a 100644 --- a/src/browser/webapi/element/html/Input.zig +++ b/src/browser/webapi/element/html/Input.zig @@ -302,7 +302,7 @@ pub fn getValueForJS(self: *const Input, frame: *Frame) ![]const u8 { if (fl._files.len == 0) { return ""; } - return try std.fmt.allocPrint(frame.call_arena, "C:\\fakepath\\{s}", .{fl._files[0]._name}); + return try std.fmt.allocPrint(frame.local_arena, "C:\\fakepath\\{s}", .{fl._files[0]._name}); } pub fn getValidationMessage(self: *Input, frame: *Frame) []const u8 { diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index 0c818caa8..1f76c9ab6 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -691,10 +691,14 @@ pub const BrowserContext = struct { const call_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.call_arena"); errdefer browser.arena_pool.release(call_arena); + const local_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.local_arena"); + errdefer browser.arena_pool.release(local_arena); + const world = try arena.create(IsolatedWorld); world.* = .{ .arena = arena, .call_arena = call_arena, + .local_arena = local_arena, .context = null, .browser = browser, .name = try arena.dupe(u8, world_name), @@ -1118,6 +1122,7 @@ const ScriptOnNewDocument = struct { const IsolatedWorld = struct { arena: Allocator, call_arena: Allocator, + local_arena: Allocator, browser: *Browser, name: []const u8, context: ?*js.Context = null, @@ -1130,6 +1135,7 @@ const IsolatedWorld = struct { pub fn deinit(self: *IsolatedWorld) void { self.removeContext(); self.browser.arena_pool.release(self.call_arena); + self.browser.arena_pool.release(self.local_arena); self.browser.arena_pool.release(self.arena); } @@ -1155,6 +1161,7 @@ const IsolatedWorld = struct { .identity = &self.identity, .identity_arena = self.arena, .call_arena = self.call_arena, + .local_arena = self.local_arena, .debug_name = "IsolatedContext", }); self.context = ctx;