From bdce49a4b5b458815209fa8259f4f007cd8c199a Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Thu, 30 Jul 2026 12:57:53 +0800 Subject: [PATCH] refactor: Introduce Arena wrapper Introduces a ArenaAllocator wrappre (lp.Arena). This is groundwork for better memory tracking and reporting memory usage to v8. This is almost purely a mechanical change to lay a foundation for a follow up PR that will address https://github.com/lightpanda-io/browser/issues/3027 Some code became a bit leaner: a pooled arena can release itself (it has a reference to the ArenaPool). Some code became uglier: The Frame has a `_local_arena: *lp.Arena` and a `local_arena: Allocator` (same with call_arena, and same with a few other types) so that consumers aren't impacted (they continue to use `frame.local_arena`). --- src/Arena.zig | 79 ++++++++++++ src/ArenaPool.zig | 103 +++++++-------- src/Inbox.zig | 51 ++++---- src/browser/Factory.zig | 20 +-- src/browser/Frame.zig | 82 ++++++------ src/browser/Page.zig | 20 ++- src/browser/Runner.zig | 6 +- src/browser/ScriptManager.zig | 14 +-- src/browser/ScriptManagerBase.zig | 20 ++- src/browser/Session.zig | 29 ++--- src/browser/StyleManager.zig | 54 ++++---- src/browser/frame/parse.zig | 4 +- src/browser/frame/preload.zig | 8 +- src/browser/frame/user_input.zig | 6 +- src/browser/js/Context.zig | 16 +-- src/browser/js/Env.zig | 4 +- src/browser/js/Execution.zig | 6 +- src/browser/js/Module.zig | 2 +- src/browser/js/Origin.zig | 11 +- src/browser/js/Value.zig | 2 +- src/browser/js/bridge.zig | 2 +- src/browser/webapi/AbstractRange.zig | 6 +- src/browser/webapi/Blob.zig | 23 ++-- src/browser/webapi/BroadcastChannel.zig | 4 +- src/browser/webapi/CryptoKey.zig | 10 +- src/browser/webapi/DOMMatrix.zig | 4 +- src/browser/webapi/DOMMatrixReadOnly.zig | 12 +- src/browser/webapi/DOMParser.zig | 4 +- src/browser/webapi/DOMPoint.zig | 4 +- src/browser/webapi/DOMPointReadOnly.zig | 12 +- src/browser/webapi/DataTransfer.zig | 20 +-- .../webapi/DedicatedWorkerGlobalScope.zig | 10 +- src/browser/webapi/Document.zig | 6 +- src/browser/webapi/Event.zig | 17 ++- src/browser/webapi/File.zig | 8 +- src/browser/webapi/FileReader.zig | 12 +- src/browser/webapi/History.zig | 8 +- src/browser/webapi/IntersectionObserver.zig | 21 ++-- src/browser/webapi/MutationObserver.zig | 21 ++-- src/browser/webapi/Notification.zig | 12 +- src/browser/webapi/Permissions.zig | 11 +- src/browser/webapi/Range.zig | 4 +- src/browser/webapi/ResizeObserver.zig | 11 +- .../webapi/SharedWorkerGlobalScope.zig | 31 +++-- src/browser/webapi/StaticRange.zig | 2 +- src/browser/webapi/Timers.zig | 7 +- src/browser/webapi/WebDriver.zig | 7 +- src/browser/webapi/Window.zig | 7 +- src/browser/webapi/Worker.zig | 29 +++-- src/browser/webapi/WorkerGlobalScope.zig | 26 ++-- src/browser/webapi/XPathExpression.zig | 17 ++- src/browser/webapi/XPathResult.zig | 21 ++-- src/browser/webapi/animation/Animation.zig | 10 +- src/browser/webapi/collections/ChildNodes.zig | 10 +- .../webapi/collections/DOMStringList.zig | 7 +- src/browser/webapi/css/FontFace.zig | 11 +- src/browser/webapi/css/FontFaceSet.zig | 13 +- src/browser/webapi/encoding/TextDecoder.zig | 12 +- .../webapi/event/BeforeUnloadEvent.zig | 10 +- src/browser/webapi/event/CloseEvent.zig | 10 +- src/browser/webapi/event/CompositionEvent.zig | 6 +- .../webapi/event/CookieChangeEvent.zig | 20 +-- src/browser/webapi/event/CustomEvent.zig | 8 +- .../webapi/event/DeviceMotionEvent.zig | 4 +- .../webapi/event/DeviceOrientationEvent.zig | 4 +- src/browser/webapi/event/DragEvent.zig | 4 +- src/browser/webapi/event/ErrorEvent.zig | 10 +- src/browser/webapi/event/FocusEvent.zig | 10 +- src/browser/webapi/event/FormDataEvent.zig | 10 +- src/browser/webapi/event/GamepadEvent.zig | 4 +- src/browser/webapi/event/HashChangeEvent.zig | 10 +- src/browser/webapi/event/InputEvent.zig | 10 +- src/browser/webapi/event/KeyboardEvent.zig | 17 ++- src/browser/webapi/event/MessageEvent.zig | 9 +- src/browser/webapi/event/MouseEvent.zig | 11 +- .../NavigationCurrentEntryChangeEvent.zig | 9 +- .../webapi/event/PageTransitionEvent.zig | 10 +- src/browser/webapi/event/PointerEvent.zig | 4 +- src/browser/webapi/event/PopStateEvent.zig | 10 +- src/browser/webapi/event/ProgressEvent.zig | 10 +- .../webapi/event/PromiseRejectionEvent.zig | 4 +- src/browser/webapi/event/StorageEvent.zig | 12 +- src/browser/webapi/event/SubmitEvent.zig | 10 +- src/browser/webapi/event/TextEvent.zig | 6 +- src/browser/webapi/event/ToggleEvent.zig | 10 +- src/browser/webapi/event/TouchEvent.zig | 4 +- src/browser/webapi/event/UIEvent.zig | 6 +- src/browser/webapi/event/WheelEvent.zig | 4 +- src/browser/webapi/navigation/Navigation.zig | 16 +-- src/browser/webapi/net/EventSource.zig | 27 ++-- src/browser/webapi/net/Fetch.zig | 8 +- src/browser/webapi/net/FormData.zig | 119 +++++++++++------- src/browser/webapi/net/Request.zig | 15 ++- src/browser/webapi/net/Response.zig | 22 ++-- src/browser/webapi/net/URLSearchParams.zig | 26 ++-- src/browser/webapi/net/WebSocket.zig | 53 ++++---- src/browser/webapi/net/XMLHttpRequest.zig | 25 ++-- src/browser/webapi/net/body_init.zig | 12 +- src/browser/webapi/selector/List.zig | 9 +- src/browser/webapi/selector/Selector.zig | 10 +- src/browser/webapi/storage/CookieStore.zig | 7 +- src/browser/webapi/storage/idb/IDBCursor.zig | 14 +-- .../webapi/storage/idb/IDBDatabase.zig | 10 +- src/browser/webapi/storage/idb/IDBIndex.zig | 14 +-- .../webapi/storage/idb/IDBObjectStore.zig | 34 ++--- .../webapi/storage/idb/IDBTransaction.zig | 33 +++-- .../storage/idb/IDBVersionChangeEvent.zig | 10 +- src/browser/webapi/svg/Angle.zig | 8 +- src/browser/webapi/svg/Length.zig | 8 +- src/browser/webapi/svg/Number.zig | 8 +- src/cdp/AXNode.zig | 30 ++--- src/cdp/CDP.zig | 26 ++-- src/cdp/Connection.zig | 6 +- src/cdp/domains/accessibility.zig | 6 +- src/cdp/domains/dom.zig | 10 +- src/cdp/domains/fetch.zig | 2 +- src/cdp/domains/network.zig | 2 +- src/cdp/domains/page.zig | 2 +- src/cookies.zig | 6 +- src/lightpanda.zig | 3 +- src/network/HttpClient.zig | 89 +++++++------ src/network/RobotsGate.zig | 16 ++- src/network/header_parser.zig | 1 - 123 files changed, 971 insertions(+), 961 deletions(-) create mode 100644 src/Arena.zig diff --git a/src/Arena.zig b/src/Arena.zig new file mode 100644 index 000000000..5f57db4c0 --- /dev/null +++ b/src/Arena.zig @@ -0,0 +1,79 @@ +// Copyright (C) 2023-2026 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// An arena checked out of an ArenaPool. Owners hold the *Arena and release it +// when they're done with it; everything downstream takes the plain Allocator +// from allocator(). +// +// Never copy an Arena by value: the Allocator it hands out points back into it. + +const std = @import("std"); +const builtin = @import("builtin"); + +const ArenaPool = @import("ArenaPool.zig"); + +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; + +const Arena = @This(); + +const IS_DEBUG = builtin.mode == .Debug; + +// In Debug, don't pool and don't retain capacity. Both can mask UAF. +pub const SAFETY = IS_DEBUG == true and builtin.is_test == false; + +_arena: ArenaAllocator, +pool: *ArenaPool, +bucket: *ArenaPool.Bucket, + +// Only meaningful while this arena sits in its bucket's free list. +next: ?*Arena, + +debug: if (IS_DEBUG) []const u8 else void = if (IS_DEBUG) "" else {}, + +pub fn allocator(self: *Arena) Allocator { + return self._arena.allocator(); +} + +pub fn release(self: *Arena) void { + self.pool.release(self); +} + +pub fn reset(self: *Arena, retain: usize) void { + _ = self._arena.reset(if (comptime SAFETY) .free_all else .{ .retain_with_limit = retain }); +} + +pub fn resetRetain(self: *Arena) void { + _ = self._arena.reset(if (comptime SAFETY) .free_all else .retain_capacity); +} + +pub fn alloc(self: *Arena, comptime T: type, n: usize) ![]T { + return self.allocator().alloc(T, n); +} + +pub fn create(self: *Arena, comptime T: type) !*T { + return self.allocator().create(T); +} + +pub fn dupe(self: *Arena, comptime T: type, m: []const T) ![]T { + return self.allocator().dupe(T, m); +} + +pub fn dupeZ(self: *Arena, comptime T: type, m: []const T) ![:0]T { + return self.allocator().dupeZ(T, m); +} diff --git a/src/ArenaPool.zig b/src/ArenaPool.zig index 95327a4f2..50ada110d 100644 --- a/src/ArenaPool.zig +++ b/src/ArenaPool.zig @@ -20,6 +20,8 @@ const std = @import("std"); const lp = @import("lightpanda"); const builtin = @import("builtin"); +const Arena = @import("Arena.zig"); + const log = lp.log; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; @@ -27,26 +29,17 @@ const ArenaAllocator = std.heap.ArenaAllocator; const ArenaPool = @This(); const IS_DEBUG = builtin.mode == .Debug; - -// In Debug, disable pooling to better catch UAF. -const SAFETY = IS_DEBUG == true and builtin.is_test == false; +const SAFETY = Arena.SAFETY; pub const BucketSize = enum { tiny, small, medium, large }; -const Bucket = struct { - free_list: ?*Entry = null, +pub const Bucket = struct { + free_list: ?*Arena = null, free_list_len: u16 = 0, free_list_max: u16, retain_bytes: usize, }; -const Entry = struct { - next: ?*Entry, - arena: ArenaAllocator, - bucket: *Bucket, - debug: if (IS_DEBUG) []const u8 else void = if (IS_DEBUG) "" else {}, -}; - pub const Config = struct { tiny: Config.Bucket = .{ .max = 512, .retain = 1024 }, small: Config.Bucket = .{ .max = 128, .retain = 4 * 1024 }, @@ -65,7 +58,7 @@ medium: Bucket, large: Bucket, allocator: Allocator, mutex: std.Io.Mutex = .init, -entry_pool: std.heap.MemoryPool(Entry), +entry_pool: std.heap.MemoryPool(Arena), _leak_track: if (IS_DEBUG) std.StringHashMapUnmanaged(isize) else void = if (IS_DEBUG) .empty else {}, @@ -101,7 +94,7 @@ pub fn deinit(self: *ArenaPool) void { var entry = bucket.free_list; while (entry) |e| { entry = e.next; - e.arena.deinit(); + e._arena.deinit(); } } self.entry_pool.deinit(self.allocator); @@ -110,7 +103,7 @@ pub fn deinit(self: *ArenaPool) void { // Acquire an arena from the pool. // - Pass a BucketSize (.tiny, .small, .medium, .large) for explicit bucket selection // - Pass a usize for automatic bucket selection based on expected size -pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Allocator { +pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !*Arena { const bucket_size: BucketSize = blk: { const T = @TypeOf(size_or_bucket); if (T == BucketSize or T == @TypeOf(.enum_literal)) { @@ -147,7 +140,7 @@ pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Al gop.value_ptr.* += 1; } lp.metrics.arena_hit.incr(bucket_size); - return entry.arena.allocator(); + return entry; } lp.metrics.arena_miss.incr(bucket_size); @@ -155,9 +148,10 @@ pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Al const entry = try self.entry_pool.create(self.allocator); entry.* = .{ .next = null, + .pool = self, .bucket = bucket, .debug = if (IS_DEBUG) debug else {}, - .arena = ArenaAllocator.init(self.allocator), + ._arena = ArenaAllocator.init(self.allocator), }; if (IS_DEBUG) { @@ -167,13 +161,12 @@ pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Al } gop.value_ptr.* += 1; } - return entry.arena.allocator(); + return entry; } -// Universal release - determines bucket from the Entry automatically -pub fn release(self: *ArenaPool, allocator: Allocator) void { - const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); - const entry: *Entry = @fieldParentPtr("arena", arena); +// Prefer Arena.release(). Determines the bucket from the Arena automatically. +pub fn release(self: *ArenaPool, entry: *Arena) void { + const arena = &entry._arena; const bucket = entry.bucket; if (IS_DEBUG) { @@ -208,18 +201,6 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void { bucket.free_list_len += 1; } -pub fn reset(_: *const ArenaPool, allocator: Allocator, retain: usize) void { - const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); - // In Debug, free_all, it's less likely to hide things - _ = arena.reset(if (comptime SAFETY) .free_all else .{ .retain_with_limit = retain }); -} - -pub fn resetRetain(_: *const ArenaPool, allocator: Allocator) void { - const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); - // In Debug, free_all, it's less likely to hide things - _ = arena.reset(if (comptime SAFETY) .free_all else .retain_capacity); -} - const testing = std.testing; test "ArenaPool: basic acquire and release" { var pool = ArenaPool.init(testing.allocator, .{}); @@ -230,17 +211,17 @@ test "ArenaPool: basic acquire and release" { const large = try pool.acquire(.large, "test-large"); // All three must be distinct arenas - try testing.expect(tiny.ptr != medium.ptr); - try testing.expect(medium.ptr != large.ptr); + try testing.expect(tiny != medium); + try testing.expect(medium != large); _ = try tiny.alloc(u8, 64); _ = try medium.alloc(u8, 1024); _ = try large.alloc(u8, 4096); // Universal release works for all buckets - pool.release(tiny); - pool.release(medium); - pool.release(large); + tiny.release(); + medium.release(); + large.release(); try testing.expectEqual(1, pool.tiny.free_list_len); try testing.expectEqual(1, pool.medium.free_list_len); @@ -252,20 +233,20 @@ test "ArenaPool: reuse from correct bucket" { defer pool.deinit(); const tiny1 = try pool.acquire(.tiny, "test"); - pool.release(tiny1); + tiny1.release(); try testing.expectEqual(1, pool.tiny.free_list_len); // Next acquire with .tiny should reuse from tiny bucket const tiny2 = try pool.acquire(.tiny, "test"); try testing.expectEqual(0, pool.tiny.free_list_len); - try testing.expectEqual(tiny1.ptr, tiny2.ptr); + try testing.expectEqual(tiny1, tiny2); // acquire with .medium should NOT get the tiny arena const medium = try pool.acquire(.medium, "test-medium"); - try testing.expect(medium.ptr != tiny2.ptr); + try testing.expect(medium != tiny2); - pool.release(tiny2); - pool.release(medium); + tiny2.release(); + medium.release(); } test "ArenaPool: respects per-bucket max limits" { @@ -282,11 +263,11 @@ test "ArenaPool: respects per-bucket max limits" { const t3 = try pool.acquire(.tiny, "t3"); // Release all 3, but only 1 should be kept (tiny_max = 1) - pool.release(t1); + t1.release(); try testing.expectEqual(1, pool.tiny.free_list_len); - pool.release(t2); + t2.release(); try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t2 discarded - pool.release(t3); + t3.release(); try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t3 discarded // Acquire 3 medium arenas @@ -295,9 +276,9 @@ test "ArenaPool: respects per-bucket max limits" { const m3 = try pool.acquire(.medium, "m3"); // Release all 3, but only 2 should be kept (medium_max = 2) - pool.release(m1); - pool.release(m2); - pool.release(m3); + m1.release(); + m2.release(); + m3.release(); try testing.expectEqual(2, pool.medium.free_list_len); } @@ -311,7 +292,7 @@ test "ArenaPool: reset clears memory without releasing" { @memset(buf, 0xFF); // reset() frees arena memory but keeps the allocator in-flight. - pool.reset(alloc, 0); + alloc.reset(0); // The free list must stay empty; the allocator was not released. try testing.expectEqual(0, pool.medium.free_list_len); @@ -321,7 +302,7 @@ test "ArenaPool: reset clears memory without releasing" { @memset(buf2, 0x00); try testing.expectEqual(@as(u8, 0x00), buf2[0]); - pool.release(alloc); + alloc.release(); } test "ArenaPool: deinit with entries in free list" { @@ -333,8 +314,8 @@ test "ArenaPool: deinit with entries in free list" { const a2 = try pool.acquire(.medium, "test2"); _ = try a1.alloc(u8, 256); _ = try a2.alloc(u8, 512); - pool.release(a1); - pool.release(a2); + a1.release(); + a2.release(); try testing.expectEqual(1, pool.tiny.free_list_len); try testing.expectEqual(1, pool.medium.free_list_len); @@ -351,9 +332,9 @@ test "ArenaPool: small bucket" { const s2 = try pool.acquire(.small, "s2"); const s3 = try pool.acquire(.small, "s3"); - pool.release(s1); - pool.release(s2); - pool.release(s3); + s1.release(); + s2.release(); + s3.release(); try testing.expectEqual(2, pool.small.free_list_len); } @@ -371,10 +352,10 @@ test "ArenaPool: size-based acquire" { // > 16KB -> large const d = try pool.acquire(20000, "fits-large"); - pool.release(a); - pool.release(b); - pool.release(c); - pool.release(d); + a.release(); + b.release(); + c.release(); + d.release(); try testing.expectEqual(1, pool.tiny.free_list_len); try testing.expectEqual(1, pool.small.free_list_len); diff --git a/src/Inbox.zig b/src/Inbox.zig index 1e05820be..e225243bd 100644 --- a/src/Inbox.zig +++ b/src/Inbox.zig @@ -30,9 +30,6 @@ const lp = @import("lightpanda"); const CDP = @import("cdp/CDP.zig"); -const ArenaPool = @import("ArenaPool.zig"); - -const Allocator = std.mem.Allocator; const DoublyLinkedList = std.DoublyLinkedList; const Inbox = @This(); @@ -47,16 +44,16 @@ queue: DoublyLinkedList = .{}, // a syncRequest and we want the following non-nested tick to pick it up again. terminated: bool = false, -pub fn deinit(self: *Inbox, arena_pool: *ArenaPool) void { +pub fn deinit(self: *Inbox) void { self.mutex.lockUncancelable(lp.io); defer self.mutex.unlock(lp.io); while (self.queue.popFirst()) |node| { const msg: *Message = @fieldParentPtr("node", node); - msg.deinit(arena_pool); + msg.deinit(); } } -pub fn push(self: *Inbox, arena: Allocator, payload: Message.Payload) void { +pub fn push(self: *Inbox, arena: *lp.Arena, payload: Message.Payload) void { const msg = arena.create(Message) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), }; @@ -109,7 +106,7 @@ pub fn popIf(self: *Inbox, predicate: *const fn (*Message) bool) ?*Message { } pub const Message = struct { - arena: Allocator, + arena: *lp.Arena, payload: Payload, node: DoublyLinkedList.Node = .{}, @@ -145,8 +142,8 @@ pub const Message = struct { input: CDP.InputMessage, }; - pub fn deinit(self: *const Message, pool: *ArenaPool) void { - pool.release(self.arena); + pub fn deinit(self: *const Message) void { + self.arena.release(); } }; @@ -155,7 +152,7 @@ test "Inbox: push then pop returns FIFO order" { const arena_pool = &testing.test_app.arena_pool; var inbox = Inbox{}; - defer inbox.deinit(&testing.test_app.arena_pool); + defer inbox.deinit(); { const arena = try arena_pool.acquire(.tiny, "inbox test"); @@ -174,17 +171,17 @@ test "Inbox: push then pop returns FIFO order" { { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("first", m.payload.ping); } { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("second", m.payload.ping); } { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual(@as(?anyerror, null), m.payload.disconnect); } try testing.expect(inbox.pop() == null); @@ -203,7 +200,7 @@ test "Inbox: deinit frees remaining items" { inbox.push(arena, .{ .disconnect = error.PeerClosed }); } - inbox.deinit(&testing.test_app.arena_pool); + inbox.deinit(); // Memory leaks would be caught by the test runner. } @@ -221,14 +218,14 @@ fn testIsPing(msg: *Message) bool { test "Inbox: popIf on empty queue returns null" { var inbox = Inbox{}; - defer inbox.deinit(&testing.test_app.arena_pool); + defer inbox.deinit(); try testing.expect(inbox.popIf(testAlwaysTrue) == null); } test "Inbox: popIf with no match leaves queue intact" { const arena_pool = &testing.test_app.arena_pool; var inbox = Inbox{}; - defer inbox.deinit(arena_pool); + defer inbox.deinit(); { const arena = try arena_pool.acquire(.tiny, "popif test"); @@ -244,12 +241,12 @@ test "Inbox: popIf with no match leaves queue intact" { // Original FIFO order preserved. { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("first", m.payload.ping); } { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("second", m.payload.ping); } try testing.expect(inbox.pop() == null); @@ -258,7 +255,7 @@ test "Inbox: popIf with no match leaves queue intact" { test "Inbox: popIf with always-true predicate behaves like pop" { const arena_pool = &testing.test_app.arena_pool; var inbox = Inbox{}; - defer inbox.deinit(arena_pool); + defer inbox.deinit(); { const arena = try arena_pool.acquire(.tiny, "popif test"); @@ -271,12 +268,12 @@ test "Inbox: popIf with always-true predicate behaves like pop" { { const m = inbox.popIf(testAlwaysTrue).?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("a", m.payload.ping); } { const m = inbox.popIf(testAlwaysTrue).?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("b", m.payload.ping); } try testing.expect(inbox.popIf(testAlwaysTrue) == null); @@ -285,7 +282,7 @@ test "Inbox: popIf with always-true predicate behaves like pop" { test "Inbox: popIf cherry-picks middle, preserves order of remainder" { const arena_pool = &testing.test_app.arena_pool; var inbox = Inbox{}; - defer inbox.deinit(arena_pool); + defer inbox.deinit(); { const arena = try arena_pool.acquire(.tiny, "popif test"); @@ -303,19 +300,19 @@ test "Inbox: popIf cherry-picks middle, preserves order of remainder" { // testIsPing skips the disconnect at the head and picks the middle. { const m = inbox.popIf(testIsPing).?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("middle", m.payload.ping); } // Remaining two disconnects pop in original order. { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expect(m.payload.disconnect == null); } { const m = inbox.pop().?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expect(m.payload.disconnect.? == error.PeerClosed); } try testing.expect(inbox.pop() == null); @@ -324,7 +321,7 @@ test "Inbox: popIf cherry-picks middle, preserves order of remainder" { test "Inbox: popIf picks first match in FIFO order" { const arena_pool = &testing.test_app.arena_pool; var inbox = Inbox{}; - defer inbox.deinit(arena_pool); + defer inbox.deinit(); { const arena = try arena_pool.acquire(.tiny, "popif test"); @@ -340,6 +337,6 @@ test "Inbox: popIf picks first match in FIFO order" { } const m = inbox.popIf(testIsPing).?; - defer m.deinit(arena_pool); + defer m.deinit(); try testing.expectEqual("first", m.payload.ping); } diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig index 12a79ad62..4c0a5a4a0 100644 --- a/src/browser/Factory.zig +++ b/src/browser/Factory.zig @@ -77,10 +77,10 @@ pub fn eventTargetWithAllocator(_: *const Factory, allocator: Allocator, child: } // this is a root object -pub fn event(_: *const Factory, arena: Allocator, typ: String, child: anytype) !*@TypeOf(child) { +pub fn event(_: *const Factory, arena: *lp.Arena, typ: String, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, @TypeOf(child) }, - ).allocate(arena); + ).allocate(arena.allocator()); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); @@ -90,10 +90,10 @@ pub fn event(_: *const Factory, arena: Allocator, typ: String, child: anytype) ! return chain.get(1); } -pub fn uiEvent(_: *const Factory, arena: Allocator, typ: String, child: anytype) !*@TypeOf(child) { +pub fn uiEvent(_: *const Factory, arena: *lp.Arena, typ: String, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, UIEvent, @TypeOf(child) }, - ).allocate(arena); + ).allocate(arena.allocator()); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); @@ -104,10 +104,10 @@ pub fn uiEvent(_: *const Factory, arena: Allocator, typ: String, child: anytype) return chain.get(2); } -pub fn mouseEvent(_: *const Factory, arena: Allocator, typ: String, mouse: MouseEvent, child: anytype) !*@TypeOf(child) { +pub fn mouseEvent(_: *const Factory, arena: *lp.Arena, typ: String, mouse: MouseEvent, child: anytype) !*@TypeOf(child) { const chain = try PrototypeChain( &.{ Event, UIEvent, MouseEvent, @TypeOf(child) }, - ).allocate(arena); + ).allocate(arena.allocator()); // Special case: Event has a _type_string field, so we need manual setup const event_ptr = chain.get(0); @@ -236,7 +236,7 @@ fn AutoPrototypeChain(comptime types: []const type) type { }; } -fn eventInit(arena: Allocator, typ: String, value: anytype) !Event { +fn eventInit(arena: *lp.Arena, typ: String, value: anytype) !Event { // Round to 2ms for privacy (browsers do this) // Same (already coarsened) clock as the performance time origin, so the // timeStamp getter can report it relative to that origin. @@ -255,7 +255,7 @@ pub fn blob(_: *const Factory, arena: Allocator, child: anytype) !*@TypeOf(child // Special case: Blob has slice and mime fields, so we need manual setup const chain = try PrototypeChain( &.{ Blob, @TypeOf(child) }, - ).allocate(arena); + ).allocate(arena.allocator()); const blob_ptr = chain.get(0); blob_ptr.* = .{ @@ -270,8 +270,8 @@ pub fn blob(_: *const Factory, arena: Allocator, child: anytype) !*@TypeOf(child return chain.get(1); } -pub fn abstractRange(_: *const Factory, arena: Allocator, child: anytype, frame: *Frame) !*@TypeOf(child) { - const chain = try PrototypeChain(&.{ AbstractRange, @TypeOf(child) }).allocate(arena); +pub fn abstractRange(_: *const Factory, arena: *lp.Arena, child: anytype, frame: *Frame) !*@TypeOf(child) { + const chain = try PrototypeChain(&.{ AbstractRange, @TypeOf(child) }).allocate(arena.allocator()); const doc = frame.document.asNode(); const abstract_range = chain.get(0); diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index 5d40c591e..71865aec6 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -298,10 +298,12 @@ arena: Allocator, // 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: *lp.Arena, 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: *lp.Arena, local_arena: Allocator, parent: ?*Frame, @@ -358,10 +360,10 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void { const session = page.session; const call_arena = try session.getArena(.medium, "call_arena"); - errdefer session.releaseArena(call_arena); + errdefer call_arena.release(); const local_arena = try session.getArena(.medium, "local_arena"); - errdefer session.releaseArena(local_arena); + errdefer local_arena.release(); const factory = &page.factory; const document = (try factory.document(Node.Document.HTMLDocument{ @@ -376,8 +378,10 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void { .parent = parent, .document = document, .window = undefined, - .call_arena = call_arena, - .local_arena = local_arena, + ._call_arena = call_arena, + ._local_arena = local_arena, + .call_arena = call_arena.allocator(), + .local_arena = local_arena.allocator(), ._frame_id = frame_id, ._page = page, ._session = session, @@ -491,7 +495,7 @@ pub fn deinit(self: *Frame) void { const page = self._page; if (self._queued_navigation) |qn| { - page.releaseArena(qn.arena); + qn.arena.release(); } while (self._message_ports.first) |node| { @@ -549,8 +553,8 @@ pub fn deinit(self: *Frame) void { self._script_manager.deinit(); self._style_manager.deinit(); - page.releaseArena(self.call_arena); - page.releaseArena(self.local_arena); + self._call_arena.release(); + self._local_arena.release(); } pub fn trackWorker(self: *Frame, worker: *Worker) !void { @@ -614,14 +618,10 @@ pub fn headersForRequest(self: *Frame, headers: *HttpClient.Headers) !void { } } -pub fn getArena(self: *Frame, size_or_bucket: anytype, debug: []const u8) !Allocator { +pub fn getArena(self: *Frame, size_or_bucket: anytype, debug: []const u8) !*lp.Arena { return self._session.getArena(size_or_bucket, debug); } -pub fn releaseArena(self: *Frame, allocator: Allocator) void { - return self._session.releaseArena(allocator); -} - pub fn isSameOrigin(self: *const Frame, url: [:0]const u8) bool { const current_origin = self.origin orelse return false; @@ -705,11 +705,11 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo return error.BlobNotFound; }; const parse_arena = try self.getArena(.medium, "Frame.parseBlob"); - defer self.releaseArena(parse_arena); + defer parse_arena.release(); // A script executed mid-parse can revoke the blob URL, letting GC // free the buffer under the parser; parse a copy. const html = try parse_arena.dupe(u8, blob._slice); - var parser = Parser.init(parse_arena, self.document.asNode(), self, .{ .allow_declarative_shadow = true }); + var parser = Parser.init(parse_arena.allocator(), self.document.asNode(), self, .{ .allow_declarative_shadow = true }); parser.parse(html); } else { self.document.injectBlank(self) catch |err| { @@ -805,7 +805,7 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo try transfer.req.headers.add(hdr); } if (opts.referer) |ref| { - const ref_header = try std.mem.concatWithSentinel(transfer.arena, u8, &.{ "Referer: ", ref }, 0); + const ref_header = try std.mem.concatWithSentinel(transfer.arena.allocator(), u8, &.{ "Referer: ", ref }, 0); try transfer.req.headers.add(ref_header); } } @@ -865,14 +865,14 @@ pub fn scheduleNavigation(self: *Frame, request_url: []const u8, opts: NavigateO return; } const arena = try self._session.getArena(.small, "scheduleNavigation"); - errdefer self._session.releaseArena(arena); + errdefer arena.release(); return self.scheduleNavigationWithArena(arena, request_url, opts, nt); } // Don't name the first parameter "self", because the target of this navigation // might change inside the function. So the code should be explicit about the // frame that it's acting on. -fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url: []const u8, opts: NavigateOpts, nt: Navigation) !void { +fn scheduleNavigationWithArena(originator: *Frame, arena: *lp.Arena, request_url: []const u8, opts: NavigateOpts, nt: Navigation) !void { const resolved_url, const is_about_blank = blk: { if (URL.isCompleteHTTPUrl(request_url)) { break :blk .{ try arena.dupeZ(u8, request_url), false }; @@ -900,7 +900,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url }; const u = try URL.resolve( - arena, + arena.allocator(), frame_base, request_url, .{ .encoding = originator.charset }, @@ -926,7 +926,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url std.mem.eql(u8, target.url, resolved_url) and std.mem.indexOfScalar(u8, resolved_url, '#') != null) { - session.releaseArena(arena); + arena.release(); return; } @@ -949,7 +949,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url try target.queueHashChange(old_url, target.url); // don't defer this, the caller is responsible for freeing it on error - session.releaseArena(arena); + arena.release(); return; } @@ -997,7 +997,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url }; if (target._queued_navigation) |existing| { - session.releaseArena(existing.arena); + existing.arena.release(); } target._queued_navigation = qn; @@ -1535,7 +1535,7 @@ fn frameDataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { } switch (self._parse_state) { - .html => |*html| try html.buffer.appendSlice(html.arena, data), + .html => |*html| try html.buffer.appendSlice(html.arena.allocator(), data), .text => |*buf| { // we have to escape the data... var v = data; @@ -1596,15 +1596,15 @@ fn frameDoneCallback(ctx: *anyopaque) !void { }; const parse_arena = try self.getArena(.medium, "Frame.parse"); - defer self.releaseArena(parse_arena); + defer parse_arena.release(); - var parser = Parser.init(parse_arena, self.document.asNode(), self, .{ .allow_declarative_shadow = true }); + var parser = Parser.init(parse_arena.allocator(), self.document.asNode(), self, .{ .allow_declarative_shadow = true }); switch (self._parse_state) { .html => |*html| { { defer { - self.releaseArena(html.arena); + html.arena.release(); self._parse_state = .complete; } @@ -1629,7 +1629,7 @@ fn frameDoneCallback(ctx: *anyopaque) !void { self._parse_state = .{ .raw_done = buf.items }; // Use empty an HTML containing the image. - const html = try std.mem.concat(parse_arena, u8, &.{ + const html = try std.mem.concat(parse_arena.allocator(), u8, &.{ "", @@ -1656,7 +1656,7 @@ fn frameDoneCallback(ctx: *anyopaque) !void { }, .err => |err| { // Generate a pseudo HTML page indicating the failure. - const html = try std.mem.concat(parse_arena, u8, &.{ + const html = try std.mem.concat(parse_arena.allocator(), u8, &.{ "

Navigation failed

Reason: ", @errorName(err), "

", @@ -2177,9 +2177,9 @@ pub fn loadExternalStylesheet(self: *Frame, link: *Element.Html.Link, href: []co const element = link.asElement(); const arena = try session.getArena(.medium, "Frame.loadExternalStylesheet"); - defer session.releaseArena(arena); + defer arena.release(); - const resolved = URL.resolve(arena, self.base(), href, .{ .encoding = self.charset }) catch |err| { + const resolved = URL.resolve(arena.allocator(), self.base(), href, .{ .encoding = self.charset }) catch |err| { log.warn(.http, "external stylesheet resolve", .{ .err = err, .href = href }); try self.fireElementEvent(element, comptime .wrap("error")); return; @@ -2208,7 +2208,7 @@ pub fn loadExternalStylesheet(self: *Frame, link: *Element.Html.Link, href: []co sm.is_evaluating = true; defer sm.endEvaluationWindow(was_evaluating); - var response = http_client.syncRequest(arena, .{ + var response = http_client.syncRequest(arena.allocator(), .{ .url = resolved, .method = .GET, .frame_id = self._frame_id, @@ -2223,7 +2223,7 @@ pub fn loadExternalStylesheet(self: *Frame, link: *Element.Html.Link, href: []co log.warn(.http, "external stylesheet fetch", .{ .err = err, .url = resolved }); return self.fireElementEvent(element, comptime .wrap("error")); }; - defer response.deinit(arena); + defer response.deinit(arena.allocator()); if (response.status < 200 or response.status >= 300) { log.info(.http, "external stylesheet status", .{ .status = response.status, .url = resolved }); @@ -2967,7 +2967,7 @@ const ParseState = union(enum) { complete, err: anyerror, html: struct { - arena: Allocator, + arena: *lp.Arena, buffer: std.ArrayList(u8), }, text: std.ArrayList(u8), @@ -2976,9 +2976,9 @@ const ParseState = union(enum) { raw_done: []const u8, download: Download, - fn deinit(self: *ParseState, frame: *Frame) void { + fn deinit(self: *ParseState, _: *Frame) void { switch (self.*) { - .html => |html| frame.releaseArena(html.arena), + .html => |html| html.arena.release(), // Only reached when a frame is torn down mid-download (the normal // completion path in frameDoneCallback already closes the file and // transitions to .complete). @@ -3143,7 +3143,7 @@ const Navigation = union(NavigationType) { }; pub const QueuedNavigation = struct { - arena: Allocator, + arena: *lp.Arena, url: [:0]const u8, opts: NavigateOpts, is_about_blank: bool, @@ -3365,7 +3365,7 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For const is_post = std.mem.eql(u8, method, "post"); const arena = try self._session.getArena(.medium, "submitForm"); - errdefer self._session.releaseArena(arena); + errdefer arena.release(); // Get charset from accept-charset attribute or fall back to document charset const charset: []const u8 = blk: { @@ -3394,8 +3394,8 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For break :blk .urlencode; }; - var buf = std.Io.Writer.Allocating.init(arena); - try form_data.write(.{ .encoding = encoding, .charset = charset, .allocator = arena }, &buf.writer); + var buf = std.Io.Writer.Allocating.init(arena.allocator()); + try form_data.write(.{ .encoding = encoding, .charset = charset, .allocator = arena.allocator() }, &buf.writer); var action = blk: { if (submit_button) |s| { @@ -3413,13 +3413,13 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For opts.body = buf.written(); opts.header = switch (encoding) { .urlencode => "Content-Type: application/x-www-form-urlencoded", - .formdata => |b| try std.fmt.allocPrintSentinel(arena, "Content-Type: multipart/form-data; boundary={s}", .{b}, 0), + .formdata => |b| try std.fmt.allocPrintSentinel(arena.allocator(), "Content-Type: multipart/form-data; boundary={s}", .{b}, 0), // Per WHATWG HTML §4.10.21.6, text/plain submissions include the form's // resolved encoding (accept-charset or document charset). - .plaintext => try std.fmt.allocPrintSentinel(arena, "Content-Type: text/plain; charset={s}", .{charset}, 0), + .plaintext => try std.fmt.allocPrintSentinel(arena.allocator(), "Content-Type: text/plain; charset={s}", .{charset}, 0), }; } else { - action = try URL.concatQueryString(arena, action, buf.written()); + action = try URL.concatQueryString(arena.allocator(), action, buf.written()); } return self.scheduleNavigationWithArena(arena, action, opts, .{ .form = target_frame }); diff --git a/src/browser/Page.zig b/src/browser/Page.zig index b34ff0acd..647772f27 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -72,6 +72,7 @@ factory: Factory, // The arena for this Page's lifetime. Document / Frame / Factory / DOM // objects allocate out of this. +_frame_arena: *lp.Arena, frame_arena: Allocator, // Origin map for same-origin context sharing. Entries live for the Page's @@ -150,13 +151,14 @@ pub fn getViewport(self: *const Page) Viewport { // Initialize a Page and its root Frame. pub fn init(self: *Page, session: *Session, frame_id: u32) !void { const frame_arena = try session.arena_pool.acquire(.large, "Page.frame_arena"); - errdefer session.arena_pool.release(frame_arena); + errdefer frame_arena.release(); self.* = .{ .session = session, .frame = undefined, - .frame_arena = frame_arena, - .factory = Factory.init(frame_arena), + ._frame_arena = frame_arena, + .frame_arena = frame_arena.allocator(), + .factory = Factory.init(frame_arena.allocator()), .globals = .init(session.browser.app.allocator), }; self.queued_navigation = &self.queued_navigation_1; @@ -228,7 +230,7 @@ pub fn deinit(self: *Page) void { self.origins = .empty; } - session.arena_pool.release(self.frame_arena); + self._frame_arena.release(); } pub fn recordJsError(self: *Page, err: anyerror) void { @@ -236,14 +238,10 @@ pub fn recordJsError(self: *Page, err: anyerror) void { lp.metrics.js_errors.incr(if (err == error.JsException) .js_exception else .other); } -pub fn getArena(self: *Page, size_or_bucket: anytype, debug: []const u8) !Allocator { +pub fn getArena(self: *Page, size_or_bucket: anytype, debug: []const u8) !*lp.Arena { return self.session.getArena(size_or_bucket, debug); } -pub fn releaseArena(self: *Page, allocator: Allocator) void { - return self.session.releaseArena(allocator); -} - pub fn getOrCreateOrigin(self: *Page, key_: ?[]const u8) !*js.Origin { const session = self.session; const key = key_ orelse { @@ -255,7 +253,7 @@ pub fn getOrCreateOrigin(self: *Page, key_: ?[]const u8) !*js.Origin { return js.Origin.init(session.browser.app, session.browser.env.isolate, &opaque_origin); }; - const gop = try self.origins.getOrPut(session.arena, key); + const gop = try self.origins.getOrPut(session.arena.allocator(), key); if (gop.found_existing) { const origin = gop.value_ptr.*; origin.rc += 1; @@ -320,7 +318,7 @@ pub fn scheduleNavigation(self: *Page, frame: *Frame) !void { } } - return list.append(self.session.arena, frame); + return list.append(self.session.arena.allocator(), frame); } pub fn findFrameByFrameId(self: *Page, frame_id: u32) ?*Frame { diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index 0c4065a4f..10fb9dcea 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -79,7 +79,7 @@ pub fn waitForFrameCDP(self: *Runner, frame_id: u32, timeout_ms: u32, until: lp. pub fn waitForAll(self: *Runner, timeout_ms: u32, opts: WaitForFrameOpts) !void { const session = self.session; const arena = try session.getArena(.tiny, "Runner.waitForAll"); - defer session.releaseArena(arena); + defer arena.release(); var pages_to_wait: usize = 0; for (session.pages.items) |page| { @@ -332,10 +332,10 @@ fn _tick(self: *Runner, comptime is_cdp: bool, timeout_ms: u32, conditions: []Wa pub fn waitForSelector(self: *Runner, frame_id: u32, input: [:0]const u8, timeout_ms: u32) !*Node.Element { const session = self.session; const arena = try session.getArena(.small, "Runner.waitForSelector"); - defer session.releaseArena(arena); + defer arena.release(); const timer: std.Io.Timestamp = .now(lp.io, .boot); - const selector = try Selector.parseLeaky(arena, input); + const selector = try Selector.parseLeaky(arena.allocator(), input); while (true) { if (session.isCancelled()) { diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index a0a62575f..4c72e1036 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -113,7 +113,7 @@ pub fn preloadScript(self: *ScriptManager, element: ?*Element.Html, url: []const const frame = self.frame; const arena = try frame.getArena(.large, "SM.preloadScript"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const owned_url = try arena.dupeZ(u8, url); @@ -242,10 +242,10 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e const arena = try frame.getArena(.large, "SM.addFromElement"); errdefer if (handover == false) { - frame.releaseArena(arena); + arena.release(); }; - const remote_url = try URL.resolve(arena, base_url, src, .{ .encoding = frame.charset }); + const remote_url = try URL.resolve(arena.allocator(), base_url, src, .{ .encoding = frame.charset }); script_element._executed = true; const mode: Script.Extra.FrameExtra.Mode = blk: { @@ -304,7 +304,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e // The adopted Script has its own arena; ours held only the URL // resolution, which nothing below needs. handover = true; - frame.releaseArena(arena); + arena.release(); if (pre.complete and mode == .async) { // The fetch already finished, so no doneCallback will move it @@ -354,7 +354,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e script.status = pre.status; script.complete = true; } else { - const response = try self.base.client.syncRequest(arena, .{ + const response = try self.base.client.syncRequest(arena.allocator(), .{ .url = remote_url, .method = .GET, .frame_id = frame._frame_id, @@ -430,7 +430,7 @@ fn addInlineScript(self: *ScriptManager, script_element: *Element.Html.Script, k const frame = self.frame; const arena = try frame.getArena(source_len + @sizeOf(Script) + 1, "SM.addInlineScript"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const source = blk: { const buf = try arena.alloc(u8, source_len + 1); @@ -627,7 +627,7 @@ test "ScriptManager: waitForPreload stops when teardown is pending" { .raw = try message_arena.dupe(u8, "{}"), .input = .{ .method = "Target.closeTarget" }, } }); - defer client.inbox.pop().?.deinit(client.arena_pool); + defer client.inbox.pop().?.deinit(); try testing.expect(sm.waitForPreload(url) == null); } diff --git a/src/browser/ScriptManagerBase.zig b/src/browser/ScriptManagerBase.zig index 8b69d3e61..a2b0c1e3a 100644 --- a/src/browser/ScriptManagerBase.zig +++ b/src/browser/ScriptManagerBase.zig @@ -185,14 +185,10 @@ pub fn getHeaders(self: *ScriptManagerBase) !http.Headers { return headers; } -fn acquireArena(self: *ScriptManagerBase, size_or_bucket: anytype, debug: []const u8) !Allocator { +fn acquireArena(self: *ScriptManagerBase, size_or_bucket: anytype, debug: []const u8) !*lp.Arena { return self.owner.session().getArena(size_or_bucket, debug); } -fn releaseArena(self: *ScriptManagerBase, arena: Allocator) void { - self.owner.session().releaseArena(arena); -} - pub fn scriptList(self: *ScriptManagerBase, script: *const Script) *std.DoublyLinkedList { return switch (script.extra) { .import, .import_async => &self.async_scripts, @@ -239,7 +235,7 @@ pub fn preloadImport(self: *ScriptManagerBase, url: [:0]const u8, referrer: []co errdefer _ = self.imported_modules.remove(url); const arena = try self.acquireArena(.large, "SM.preloadImport"); - errdefer self.releaseArena(arena); + errdefer arena.release(); const script = try arena.create(Script); script.* = .{ @@ -431,7 +427,7 @@ pub fn getAsyncImport(self: *ScriptManagerBase, url: [:0]const u8, cb: ImportAsy } const arena = try self.acquireArena(.large, "SM.getAsyncImport"); - errdefer self.releaseArena(arena); + errdefer arena.release(); const script = try arena.create(Script); script.* = .{ @@ -614,7 +610,7 @@ pub const Script = struct { status: u16 = 0, source: Source, url: []const u8, - arena: Allocator, + arena: *lp.Arena, extra: Extra, node: std.DoublyLinkedList.Node, manager: *ScriptManagerBase, @@ -691,7 +687,7 @@ pub const Script = struct { }; pub fn deinit(self: *Script) void { - self.manager.releaseArena(self.arena); + self.arena.release(); } pub fn startCallback(transfer: *HttpClient.Transfer) !void { @@ -754,7 +750,7 @@ pub const Script = struct { lp.assert(self.source.remote.capacity == 0, "ScriptManagerBase.Header buffer", .{ .capacity = self.source.remote.capacity }); var buffer: std.ArrayList(u8) = .empty; if (transfer.getContentLength()) |cl| { - try buffer.ensureTotalCapacity(self.arena, cl); + try buffer.ensureTotalCapacity(self.arena.allocator(), cl); } self.source = .{ .remote = buffer }; return .proceed; @@ -769,7 +765,7 @@ pub const Script = struct { } fn _dataCallback(self: *Script, _: *HttpClient.Transfer, data: []const u8) !void { - try self.source.remote.appendSlice(self.arena, data); + try self.source.remote.appendSlice(self.arena.allocator(), data); } pub fn doneCallback(ctx: *anyopaque) !void { @@ -1148,7 +1144,7 @@ test "ScriptManagerBase: waitForImport stops when teardown is pending" { .raw = try message_arena.dupe(u8, "{}"), .input = .{ .method = "Target.disposeBrowserContext" }, } }); - defer client.inbox.pop().?.deinit(client.arena_pool); + defer client.inbox.pop().?.deinit(); try testing.expectError(error.SyncWaitInterrupted, sm.waitForImport(url)); } diff --git a/src/browser/Session.zig b/src/browser/Session.zig index 2dc42438b..be676f250 100644 --- a/src/browser/Session.zig +++ b/src/browser/Session.zig @@ -39,7 +39,6 @@ const SharedWorkerGlobalScope = @import("webapi/SharedWorkerGlobalScope.zig"); const log = lp.log; const ArenaPool = App.ArenaPool; -const Allocator = std.mem.Allocator; const IS_DEBUG = builtin.mode == .Debug; // A Session represents a browsing context group (cookie jar, session storage, @@ -50,7 +49,7 @@ const IS_DEBUG = builtin.mode == .Debug; const Session = @This(); browser: *Browser, -arena: Allocator, +arena: *lp.Arena, history: History, navigation: *Navigation, storage_shed: storage.Shed, @@ -161,9 +160,9 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi const arena_pool = browser.arena_pool; const arena = try arena_pool.acquire(.small, "Session"); - errdefer arena_pool.release(arena); + errdefer arena.release(); - const navigation = try Factory.chainedWithAllocator(arena, .{ + const navigation = try Factory.chainedWithAllocator(arena.allocator(), .{ EventTarget{ ._type = undefined }, Navigation{ ._proto = undefined }, }); @@ -211,7 +210,7 @@ pub fn deinit(self: *Session) void { self.bridge_store.deinit(allocator); } self._console_messages.deinit(); - self.arena_pool.release(self.arena); + self.arena.release(); } /// Register the console listener so `drainConsoleMessages` returns output. Idempotent. @@ -285,7 +284,7 @@ fn allocatePage(self: *Session, frame_id: u32) !*Page { // Tear down and free a Page allocated via allocatePage. fn queuePageDestruction(self: *Session, page: *Page) void { - self._page_destruction_queue.append(self.arena, page) catch @panic("OOM"); + self._page_destruction_queue.append(self.arena.allocator(), page) catch @panic("OOM"); } fn retire(self: *Session, page: *Page) void { @@ -348,7 +347,7 @@ fn installNewActivePage(self: *Session, frame_id: u32) !*Frame { const page = try self.allocatePage(frame_id); errdefer self.queuePageDestruction(page); - try self.pages.append(self.arena, page); + try self.pages.append(self.arena.allocator(), page); errdefer _ = self.pages.pop(); const frame = &page.frame; @@ -390,7 +389,7 @@ pub fn closePage(self: *Session, frame_id: u32) void { // We queue then process so that there is a single place (processDestroyQueues) // where pages get destroyed. pub fn closeAllPages(self: *Session) void { - self._page_destruction_queue.ensureUnusedCapacity(self.arena, self.pages.items.len) catch @panic("OOM"); + self._page_destruction_queue.ensureUnusedCapacity(self.arena.allocator(), self.pages.items.len) catch @panic("OOM"); for (self.pages.items) |page| { page.frame.abortTransfers(); self._page_destruction_queue.appendAssumeCapacity(page); @@ -399,14 +398,10 @@ pub fn closeAllPages(self: *Session) void { self.processDestroyQueues(); } -pub fn getArena(self: *Session, size_or_bucket: anytype, debug: []const u8) !Allocator { +pub fn getArena(self: *Session, size_or_bucket: anytype, debug: []const u8) !*lp.Arena { return self.arena_pool.acquire(size_or_bucket, debug); } -pub fn releaseArena(self: *Session, allocator: Allocator) void { - self.arena_pool.release(allocator); -} - // The live page for a top-level browsing context, by its root frame id. pub fn livePage(self: *Session, frame_id: u32) ?*Page { for (self.pages.items) |page| { @@ -594,7 +589,7 @@ fn processPageQueuedNavigation(self: *Session, page: *Page) !void { if (qn.is_about_blank) { // Defer about:blank to second pass - try about_blank_queue.append(self.arena, frame); + try about_blank_queue.append(self.arena.allocator(), frame); continue; } @@ -643,7 +638,7 @@ fn processPageQueuedNavigation(self: *Session, page: *Page) !void { fn processFrameNavigation(self: *Session, frame: *Frame, qn: *QueuedNavigation) !void { frame._queued_navigation = null; - defer self.releaseArena(qn.arena); + defer qn.arena.release(); // A popup whose window was close()'d is parked in page.closed_frames and // torn down at Page.deinit. It must never be navigated. This navigation @@ -782,7 +777,7 @@ fn processRootQueuedNavigation(self: *Session, page: *Page) !void { // The qn arena is consumed here regardless of success — frame.navigate // dupes the URL into the page's own arena, so we can release the qn // arena as soon as navigate returns. - defer self.arena_pool.release(qn.arena); + defer qn.arena.release(); if (is_synthetic) { return self.replaceRootImmediate(current_frame._frame_id, qn.url, qn.opts); @@ -844,7 +839,7 @@ pub fn initiateRootNavigation(self: *Session, frame_id: u32, url: [:0]const u8, live.replacement = page; errdefer live.replacement = null; - try self.pages.append(self.arena, page); + try self.pages.append(self.arena.allocator(), page); errdefer _ = self.pages.pop(); if (comptime IS_DEBUG) { diff --git a/src/browser/StyleManager.zig b/src/browser/StyleManager.zig index 25ad4f29e..b45ea556a 100644 --- a/src/browser/StyleManager.zig +++ b/src/browser/StyleManager.zig @@ -53,7 +53,7 @@ const RuleList = std.MultiArrayList(VisibilityRule); frame: *Frame, -arena: Allocator, +arena: *lp.Arena, // Bucketed rules for fast lookup - keyed by rightmost selector part id_rules: std.StringHashMapUnmanaged(RuleList) = .empty, @@ -89,7 +89,7 @@ pub fn init(frame: *Frame) !StyleManager { } pub fn deinit(self: *StyleManager) void { - self.frame.releaseArena(self.arena); + self.arena.release(); } const IS_DEBUG = builtin.mode == .Debug; @@ -118,7 +118,7 @@ fn parseSheet(self: *StyleManager, build_arena: Allocator, sheet: *CSSStyleSheet const owner_node = sheet.getOwnerNode() orelse return; if (owner_node.is(Element.Html.Style)) |style| { - const text = try style.asNode().getTextContentAlloc(self.arena); + const text = try style.asNode().getTextContentAlloc(self.arena.allocator()); var it = CssParser.parseStylesheet(text); while (it.next()) |parsed_rule| { switch (parsed_rule) { @@ -475,7 +475,7 @@ fn addRawRule(self: *StyleManager, build_arena: Allocator, selector_text: []cons if (!props.isRelevant()) return; - const selectors = SelectorParser.parseList(self.arena, selector_text) catch return; + const selectors = SelectorParser.parseList(self.arena.allocator(), selector_text) catch return; for (selectors) |selector| { const rightmost = if (selector.segments.len > 0) selector.segments[selector.segments.len - 1].compound else selector.first; const bucket_key = getBucketKey(rightmost) orelse continue; @@ -489,22 +489,22 @@ fn addRawRule(self: *StyleManager, build_arena: Allocator, selector_text: []cons switch (bucket_key) { .id => |id| { - const gop = try self.id_rules.getOrPut(self.arena, id); + const gop = try self.id_rules.getOrPut(self.arena.allocator(), id); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .class => |class| { - const gop = try self.class_rules.getOrPut(self.arena, class); + const gop = try self.class_rules.getOrPut(self.arena.allocator(), class); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .tag => |tag| { - const gop = try self.tag_rules.getOrPut(self.arena, tag); + const gop = try self.tag_rules.getOrPut(self.arena.allocator(), tag); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .other => { - try self.other_rules.append(self.arena, rule); + try self.other_rules.append(self.arena.allocator(), rule); }, } } @@ -536,7 +536,7 @@ fn rebuildIfDirty(self: *StyleManager) !void { self.layer_ids = .empty; self.next_anon_layer = 0; self.rule_layers = .empty; - self.frame.releaseArena(build_arena); + build_arena.release(); } self.dirty = false; @@ -546,31 +546,31 @@ fn rebuildIfDirty(self: *StyleManager) !void { const tag_rules_count = self.tag_rules.count(); const other_rules_count = self.other_rules.len; - self.frame._session.arena_pool.resetRetain(self.arena); + self.arena.resetRetain(); self.next_doc_order = 1; self.id_rules = .empty; - try self.id_rules.ensureTotalCapacity(self.arena, id_rules_count); + try self.id_rules.ensureTotalCapacity(self.arena.allocator(), id_rules_count); self.class_rules = .empty; - try self.class_rules.ensureTotalCapacity(self.arena, class_rules_count); + try self.class_rules.ensureTotalCapacity(self.arena.allocator(), class_rules_count); self.tag_rules = .empty; - try self.tag_rules.ensureTotalCapacity(self.arena, tag_rules_count); + try self.tag_rules.ensureTotalCapacity(self.arena.allocator(), tag_rules_count); self.other_rules = .{}; - try self.other_rules.ensureTotalCapacity(self.arena, other_rules_count); + try self.other_rules.ensureTotalCapacity(self.arena.allocator(), other_rules_count); const sheets = self.frame.document._style_sheets orelse return; for (sheets._sheets.items) |sheet| { - self.parseSheet(build_arena, sheet) catch |err| { + self.parseSheet(build_arena.allocator(), sheet) catch |err| { log.err(.browser, "StyleManager parseSheet", .{ .err = err }); return err; }; } - try self.finalizeLayerRanks(build_arena); + try self.finalizeLayerRanks(build_arena.allocator()); } // Check if an element is hidden based on options. @@ -946,7 +946,7 @@ fn addRule(self: *StyleManager, build_arena: Allocator, style_rule: *CSSStyleRul } // Parse the selector list - const selectors = SelectorParser.parseList(self.arena, selector_text) catch return; + const selectors = SelectorParser.parseList(self.arena.allocator(), selector_text) catch return; if (selectors.len == 0) { return; } @@ -974,22 +974,22 @@ fn addRule(self: *StyleManager, build_arena: Allocator, style_rule: *CSSStyleRul // Add to appropriate bucket switch (bucket_key) { .id => |id| { - const gop = try self.id_rules.getOrPut(self.arena, id); + const gop = try self.id_rules.getOrPut(self.arena.allocator(), id); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .class => |class| { - const gop = try self.class_rules.getOrPut(self.arena, class); + const gop = try self.class_rules.getOrPut(self.arena.allocator(), class); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .tag => |tag| { - const gop = try self.tag_rules.getOrPut(self.arena, tag); + const gop = try self.tag_rules.getOrPut(self.arena.allocator(), tag); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(self.arena, rule); + try gop.value_ptr.append(self.arena.allocator(), rule); }, .other => { - try self.other_rules.append(self.arena, rule); + try self.other_rules.append(self.arena.allocator(), rule); }, } } diff --git a/src/browser/frame/parse.zig b/src/browser/frame/parse.zig index aa690755c..5ff8b9ba2 100644 --- a/src/browser/frame/parse.zig +++ b/src/browser/frame/parse.zig @@ -103,7 +103,7 @@ fn htmlAsChildrenInner(frame: *Frame, node: *Node, html: []const u8, opts: Fragm // XML. pub fn xmlDocument(frame: *Frame, xml: []const u8) !?*Document.XMLDocument { const arena = try frame.getArena(.medium, "parse.xmlDocument"); - defer frame.releaseArena(arena); + defer arena.release(); const previous_parse_mode = frame._parse_mode; frame._parse_mode = .fragment; @@ -111,7 +111,7 @@ pub fn xmlDocument(frame: *Frame, xml: []const u8) !?*Document.XMLDocument { const doc = try frame._factory.document(Document.XMLDocument{ ._proto = undefined }); const doc_node = doc.asNode(); - var parser = Parser.init(arena, doc_node, frame, .{}); + var parser = Parser.init(arena.allocator(), doc_node, frame, .{}); parser.parseXML(xml); if (parser.terminated) { return error.ExecutionTerminated; diff --git a/src/browser/frame/preload.zig b/src/browser/frame/preload.zig index 08071f003..9bc80aeee 100644 --- a/src/browser/frame/preload.zig +++ b/src/browser/frame/preload.zig @@ -38,9 +38,9 @@ pub fn scriptHint(frame: *Frame, element: ?*Element.Html, href: []const u8) bool } const arena = frame.getArena(.small, "preload.scriptHint") catch return false; - defer frame.releaseArena(arena); + defer arena.release(); - const resolved = URL.resolve(arena, frame.base(), href, .{ .encoding = frame.charset }) catch return false; + const resolved = URL.resolve(arena.allocator(), frame.base(), href, .{ .encoding = frame.charset }) catch return false; if (!isRemoteScheme(resolved)) { return false; } @@ -79,9 +79,9 @@ pub fn prescan(frame: *Frame, html: []const u8) void { return; } const arena = frame.getArena(.small, "preload.prescan") catch return; - defer frame.releaseArena(arena); + defer arena.release(); - var scan = Prescan{ .frame = frame, .base = frame.base(), .arena = arena }; + var scan = Prescan{ .frame = frame, .base = frame.base(), .arena = arena.allocator() }; Parser.prescan(html, frame.charset, &scan, Prescan.callback); } diff --git a/src/browser/frame/user_input.zig b/src/browser/frame/user_input.zig index a1734bc82..534b6ae95 100644 --- a/src/browser/frame/user_input.zig +++ b/src/browser/frame/user_input.zig @@ -274,7 +274,7 @@ pub fn findClickActivationTarget(target: *Node, bubbles: bool) ?*Node { fn runJavascriptUrl(frame: *Frame, source: []const u8) !void { const arena = try frame.getArena(.tiny, "javascript-url"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const task = try arena.create(JavascriptUrlTask); task.* = .{ @@ -292,7 +292,7 @@ fn runJavascriptUrl(frame: *Frame, source: []const u8) !void { const JavascriptUrlTask = struct { frame: *Frame, - arena: std.mem.Allocator, + arena: *lp.Arena, source: []const u8, fn run(ptr: *anyopaque) !?u32 { @@ -320,7 +320,7 @@ const JavascriptUrlTask = struct { } fn deinit(self: *JavascriptUrlTask) void { - self.frame.releaseArena(self.arena); + self.arena.release(); } }; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 0641a06ab..f5f90499f 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -93,7 +93,7 @@ heap_profiler: ?*v8.HeapProfiler = null, templates: []*const v8.FunctionTemplate, // Arena for the lifetime of the context -arena: Allocator, +arena: *lp.Arena, // The call_arena for this context. For main world contexts this is // frame.call_arena. For isolated world contexts this is a separate arena @@ -205,7 +205,7 @@ pub fn deinit(self: *Context) void { } const env = self.env; - defer env.app.arena_pool.release(self.arena); + defer self.arena.release(); // Unlink any IndexedDB gate participants first: the session-scoped engine // must never wake a waiter into this scheduler once it's torn down. @@ -353,7 +353,7 @@ pub fn module(self: *Context, comptime want_result: bool, local: *const js.Local // gop will _always_ initiated if cacheable == true var gop: std.StringHashMapUnmanaged(ModuleEntry).GetOrPutResult = undefined; if (cacheable) { - gop = try self.module_cache.getOrPut(arena, url); + gop = try self.module_cache.getOrPut(arena.allocator(), url); if (gop.found_existing) { if (gop.value_ptr.module) |cache_mod| { if (gop.value_ptr.module_promise == null) { @@ -514,7 +514,7 @@ fn compileModule(local: *const js.Local, src: []const u8, name: []const u8) !js. // we always want to track its identity (so that, if this module imports other // modules, we can resolve the full URL), and preload any dependent modules. fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: *const js.Local) !void { - try self.module_identifier.putNoClobber(self.arena, mod.getIdentityHash(), url); + try self.module_identifier.putNoClobber(self.arena.allocator(), mod.getIdentityHash(), url); // Non-async modules are blocking. We can download them in parallel, but // they need to be processed serially. So we want to get the list of @@ -534,7 +534,7 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: * return err; }, }; - const nested_gop = try self.module_cache.getOrPut(self.arena, normalized_specifier); + const nested_gop = try self.module_cache.getOrPut(self.arena.allocator(), normalized_specifier); if (!nested_gop.found_existing) { const owned_specifier = try self.arena.dupeZ(u8, normalized_specifier); nested_gop.key_ptr.* = owned_specifier; @@ -636,7 +636,7 @@ pub fn dynamicModuleCallback( }; const normalized_specifier = self.script_manager.resolveSpecifier( - self.arena, // might need to survive until the module is loaded + self.arena.allocator(), // might need to survive until the module is loaded resource, specifier, ) catch |err| switch (err) { @@ -749,7 +749,7 @@ fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]co }; const normalized_specifier = try self.script_manager.resolveSpecifier( - self.arena, + self.arena.allocator(), referrer_path, specifier, ); @@ -804,7 +804,7 @@ const DynamicModuleResolveState = struct { }; fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8, local: *const js.Local) !js.Promise { - const gop = try self.module_cache.getOrPut(self.arena, specifier); + const gop = try self.module_cache.getOrPut(self.arena.allocator(), specifier); if (gop.found_existing) { if (gop.value_ptr.resolver_promise) |rp| { return local.toLocal(rp); diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index fa64dc0d4..0407af56f 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -255,7 +255,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context const is_frame = T == *Frame; const context_arena = try self.app.arena_pool.acquire(.medium, params.debug_name); - errdefer self.app.arena_pool.release(context_arena); + errdefer context_arena.release(); const isolate = self.isolate; var hs: js.HandleScope = undefined; @@ -340,7 +340,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context .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), + .scheduler = .init(context_arena.allocator()), .identity = params.identity, .identity_arena = params.identity_arena, .execution = undefined, diff --git a/src/browser/js/Execution.zig b/src/browser/js/Execution.zig index 020175adc..96f641900 100644 --- a/src/browser/js/Execution.zig +++ b/src/browser/js/Execution.zig @@ -76,14 +76,10 @@ pub fn dupeString(self: *const Execution, value: []const u8) ![]const u8 { return self.arena.dupe(u8, value); } -pub fn getArena(self: *const Execution, size_or_bucket: anytype, debug: []const u8) !Allocator { +pub fn getArena(self: *const Execution, size_or_bucket: anytype, debug: []const u8) !*lp.Arena { return self.page.getArena(size_or_bucket, debug); } -pub fn releaseArena(self: *const Execution, allocator: Allocator) void { - self.page.releaseArena(allocator); -} - pub fn headersForRequest(self: *const Execution, headers: *HttpClient.Headers) !void { return switch (self.js.global) { inline else => |g| g.headersForRequest(headers), diff --git a/src/browser/js/Module.zig b/src/browser/js/Module.zig index 2a331ccbf..c7bc46cce 100644 --- a/src/browser/js/Module.zig +++ b/src/browser/js/Module.zig @@ -91,7 +91,7 @@ pub fn persist(self: Module) !Global { var ctx = self.local.ctx; var global: v8.Global = undefined; v8.v8__Global__New(ctx.isolate.handle, self.handle, &global); - try ctx.global_modules.append(ctx.arena, global); + try ctx.global_modules.append(ctx.arena.allocator(), global); return .{ .handle = global }; } diff --git a/src/browser/js/Origin.zig b/src/browser/js/Origin.zig index fce37ce0c..93a724557 100644 --- a/src/browser/js/Origin.zig +++ b/src/browser/js/Origin.zig @@ -24,18 +24,17 @@ // separately via js.Identity - Session has the main world Identity, and // IsolatedWorlds have their own Identity instances. -const std = @import("std"); +const lp = @import("lightpanda"); const js = @import("js.zig"); const App = @import("../../App.zig"); const v8 = js.v8; -const Allocator = std.mem.Allocator; const Origin = @This(); rc: usize = 1, -arena: Allocator, +arena: *lp.Arena, // The key, e.g. lightpanda.io:443 key: []const u8, @@ -46,7 +45,7 @@ security_token: v8.Global, pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin { const arena = try app.arena_pool.acquire(.tiny, "Origin"); - errdefer app.arena_pool.release(arena); + errdefer arena.release(); var hs: js.HandleScope = undefined; hs.init(isolate); @@ -67,7 +66,7 @@ pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin { return self; } -pub fn deinit(self: *Origin, app: *App) void { +pub fn deinit(self: *Origin, _: *App) void { v8.v8__Global__Reset(&self.security_token); - app.arena_pool.release(self.arena); + self.arena.release(); } diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index d68452c51..e77eb3d58 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -601,7 +601,7 @@ const CloneDelegate = struct { const handle = message orelse break :blk null; const str = js.String{ .local = local, .handle = handle }; // the exception can outlive this call; dupe onto the context arena - break :blk str.toSliceWithAlloc(local.ctx.arena) catch null; + break :blk str.toSliceWithAlloc(local.ctx.arena.allocator()) catch null; }; throwDataCloneException(local, msg); } diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 40fd0a7a2..bd2088395 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -782,7 +782,7 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 fn logUnknownProperty(local: *const js.Local, key: []const u8) !void { const ctx = local.ctx; - const gop = try ctx.unknown_properties.getOrPut(ctx.arena, key); + const gop = try ctx.unknown_properties.getOrPut(ctx.arena.allocator(), key); if (gop.found_existing) { gop.value_ptr.count += 1; } else { diff --git a/src/browser/webapi/AbstractRange.zig b/src/browser/webapi/AbstractRange.zig index 2898e1ff3..99bcb5f67 100644 --- a/src/browser/webapi/AbstractRange.zig +++ b/src/browser/webapi/AbstractRange.zig @@ -26,15 +26,13 @@ const Node = @import("Node.zig"); const Range = @import("Range.zig"); const StaticRange = @import("StaticRange.zig"); -const Allocator = std.mem.Allocator; - const AbstractRange = @This(); pub const _prototype_root = true; _rc: lp.RC = .{}, _type: Type, -_arena: Allocator, +_arena: *lp.Arena, _end_offset: u32, _start_offset: u32, _frame_loader_id: u32, @@ -55,7 +53,7 @@ pub fn deinit(self: *AbstractRange, page: *Page) void { frame._live_ranges.remove(&self._range_link); } } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *AbstractRange, page: *Page) void { diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index b6b74d011..85bfaa705 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -26,7 +26,6 @@ const Mime = @import("../Mime.zig"); const Writer = std.Io.Writer; const Execution = js.Execution; -const Allocator = std.mem.Allocator; /// https://w3c.github.io/FileAPI/#blob-section /// https://developer.mozilla.org/en-US/docs/Web/API/Blob @@ -37,7 +36,7 @@ pub const _prototype_root = true; _type: Type, _rc: lp.RC, -_arena: Allocator, +_arena: *lp.Arena, /// Immutable slice of blob. /// Note that another blob may hold a pointer/slice to this, @@ -82,7 +81,7 @@ const InitOptions = struct { pub fn init(parts_: ?[]const js.Value, opts_: ?InitOptions, page: *Page) !*Blob { const session = page.session; const arena = try session.getArena(.large, "Blob"); - errdefer session.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Blob); self.* = try buildValue(arena, parts_, opts_ orelse .{}); @@ -92,13 +91,13 @@ pub fn init(parts_: ?[]const js.Value, opts_: ?InitOptions, page: *Page) !*Blob // The Blob value for `parts`, with everything it references copied to // `arena`. Callers either arena.create it (init) or embed it as the root of // a {Blob, File} chain. -pub fn buildValue(arena: Allocator, parts_: ?[]const js.Value, opts: InitOptions) !Blob { - const mime = try Mime.serialize(arena, opts.type); +pub fn buildValue(arena: *lp.Arena, parts_: ?[]const js.Value, opts: InitOptions) !Blob { + const mime = try Mime.serialize(arena.allocator(), opts.type); const data = blk: { if (parts_) |blob_parts| { const use_native_endings = std.mem.eql(u8, opts.endings, "native"); - var w: Writer.Allocating = .init(arena); + var w: Writer.Allocating = .init(arena.allocator()); for (blob_parts) |js_val| { const part = try js_val.toStringSmart(); try writePartWithEndings(part, use_native_endings, &w.writer); @@ -118,28 +117,28 @@ pub fn buildValue(arena: Allocator, parts_: ?[]const js.Value, opts: InitOptions }; } -pub fn buildValueFromBytes(arena: Allocator, data: []const u8, content_type: []const u8) !Blob { +pub fn buildValueFromBytes(arena: *lp.Arena, data: []const u8, content_type: []const u8) !Blob { return .{ ._rc = .{}, ._arena = arena, ._type = .generic, ._slice = try arena.dupe(u8, data), - ._mime = try Mime.serialize(arena, content_type), + ._mime = try Mime.serialize(arena.allocator(), content_type), }; } /// Creates a new Blob from raw byte slices (for internal Zig use). pub fn initFromBytes(data: []const u8, content_type: []const u8, page: *Page) !*Blob { const arena = try page.getArena(data.len + content_type.len + 256, "Blob"); - errdefer page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Blob); self.* = try buildValueFromBytes(arena, data, content_type); return self; } -pub fn deinit(self: *Blob, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Blob, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *Blob, page: *Page) void { @@ -160,7 +159,7 @@ pub fn structuredDeserialize(reader: *js.StructuredReader, page: *Page) !*Blob { const data = try reader.readBytes(); const arena = try page.getArena(data.len + mime.len + 256, "Blob.clone"); - errdefer page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Blob); self.* = .{ diff --git a/src/browser/webapi/BroadcastChannel.zig b/src/browser/webapi/BroadcastChannel.zig index d6d252fa3..7da78b72d 100644 --- a/src/browser/webapi/BroadcastChannel.zig +++ b/src/browser/webapi/BroadcastChannel.zig @@ -174,12 +174,12 @@ const PostMessageCallback = struct { // (never realloc) and teardown is deferred to the next tick, so walking // each channel list live during dispatch is safe. const arena = try page.getArena(.tiny, "BroadcastChannel.postMessage"); - defer page.releaseArena(arena); + defer arena.release(); // Opaque origins have no string form and are unique per execution, so // the sender is the only same-origin context const executions = if (origin) |o| - try page.executionsForOrigin(arena, o) + try page.executionsForOrigin(arena.allocator(), o) else (&self.exec)[0..1]; diff --git a/src/browser/webapi/CryptoKey.zig b/src/browser/webapi/CryptoKey.zig index 2df7f3000..73b30c0b6 100644 --- a/src/browser/webapi/CryptoKey.zig +++ b/src/browser/webapi/CryptoKey.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const crypto = @import("../../sys/libcrypto.zig"); @@ -24,14 +23,13 @@ const js = @import("../js/js.zig"); const Page = @import("../Page.zig"); const Execution = js.Execution; -const Allocator = std.mem.Allocator; /// Represents a cryptographic key obtained from one of the SubtleCrypto methods /// generateKey(), deriveKey(), importKey(), or unwrapKey(). const CryptoKey = @This(); _rc: lp.RC = .{}, -_arena: Allocator = undefined, +_arena: *lp.Arena = undefined, /// Algorithm being used. _type: Type, /// Whether this is a secret (symmetric), public, or private key. Surfaced as @@ -105,7 +103,7 @@ pub const Usages = struct { /// are expected to be static strings. Takes ownership of `_vary.pkey`. pub fn init(exec: *const Execution, key: CryptoKey) !*CryptoKey { const arena = try exec.getArena(.tiny, "CryptoKey"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(CryptoKey); self.* = key; @@ -117,12 +115,12 @@ pub fn init(exec: *const Execution, key: CryptoKey) !*CryptoKey { return self; } -pub fn deinit(self: *CryptoKey, page: *Page) void { +pub fn deinit(self: *CryptoKey, _: *Page) void { switch (self._vary) { .pkey => |pkey| crypto.EVP_PKEY_free(pkey), .none, .digest => {}, } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *CryptoKey, page: *Page) void { diff --git a/src/browser/webapi/DOMMatrix.zig b/src/browser/webapi/DOMMatrix.zig index 30dd0072a..381036bc0 100644 --- a/src/browser/webapi/DOMMatrix.zig +++ b/src/browser/webapi/DOMMatrix.zig @@ -36,9 +36,9 @@ pub fn init(init_: ?js.Value, exec: *const js.Execution) !*DOMMatrix { pub fn create(m: [16]f64, is_2d: bool, page: *Page) !*DOMMatrix { const arena = try page.getArena(.tiny, "DOMMatrix"); - errdefer page.releaseArena(arena); + errdefer arena.release(); - const self = try Factory.chainedWithAllocator(arena, .{ + const self = try Factory.chainedWithAllocator(arena.allocator(), .{ RO.buildValue(arena, m, is_2d), DOMMatrix{ ._proto = undefined }, }); diff --git a/src/browser/webapi/DOMMatrixReadOnly.zig b/src/browser/webapi/DOMMatrixReadOnly.zig index d25d8a0fb..4b8e8d972 100644 --- a/src/browser/webapi/DOMMatrixReadOnly.zig +++ b/src/browser/webapi/DOMMatrixReadOnly.zig @@ -23,15 +23,13 @@ const js = @import("../js/js.zig"); const Page = @import("../Page.zig"); const DOMMatrix = @import("DOMMatrix.zig"); -const Allocator = std.mem.Allocator; - const DOMMatrixReadOnly = @This(); pub const _prototype_root = true; _type: Type, _rc: lp.RC, -_arena: Allocator, +_arena: *lp.Arena, // Stored column-major, matching the spec's mAB naming where A is the column // and B is the row: @@ -68,8 +66,8 @@ pub fn init(init_: ?js.Value, exec: *const js.Execution) !*DOMMatrixReadOnly { return createBare(parsed.m, parsed.is_2d, exec.page); } -pub fn deinit(self: *DOMMatrixReadOnly, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *DOMMatrixReadOnly, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *DOMMatrixReadOnly) void { @@ -82,14 +80,14 @@ pub fn releaseRef(self: *DOMMatrixReadOnly, page: *Page) void { pub fn createBare(m: [16]f64, is_2d: bool, page: *Page) !*DOMMatrixReadOnly { const arena = try page.getArena(.tiny, "DOMMatrix"); - errdefer page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(DOMMatrixReadOnly); self.* = buildValue(arena, m, is_2d); return self; } -pub fn buildValue(arena: std.mem.Allocator, m: [16]f64, is_2d: bool) DOMMatrixReadOnly { +pub fn buildValue(arena: *lp.Arena, m: [16]f64, is_2d: bool) DOMMatrixReadOnly { return .{ ._rc = .{}, ._arena = arena, diff --git a/src/browser/webapi/DOMParser.zig b/src/browser/webapi/DOMParser.zig index 3a8088bd1..bd1babfb1 100644 --- a/src/browser/webapi/DOMParser.zig +++ b/src/browser/webapi/DOMParser.zig @@ -52,7 +52,7 @@ pub fn parseFromString( return switch (target_mime) { .@"text/html" => { const arena = try frame.getArena(.medium, "DOMParser.parseFromString"); - defer frame.releaseArena(arena); + defer arena.release(); // DOMParser builds a detached Document. Borrow the same fragment // parse-mode that `Frame.parse` uses so frame-side hooks @@ -76,7 +76,7 @@ pub fn parseFromString( } // Parse HTML into the document - var parser = Parser.init(arena, doc.asNode(), frame, .{}); + var parser = Parser.init(arena.allocator(), doc.asNode(), frame, .{}); parser.parse(normalized); if (parser.terminated) { return error.ExecutionTerminated; diff --git a/src/browser/webapi/DOMPoint.zig b/src/browser/webapi/DOMPoint.zig index 198673330..7692f96e2 100644 --- a/src/browser/webapi/DOMPoint.zig +++ b/src/browser/webapi/DOMPoint.zig @@ -33,9 +33,9 @@ pub fn init(x_: ?f64, y_: ?f64, z_: ?f64, w_: ?f64, exec: *const js.Execution) ! pub fn create(x: f64, y: f64, z: f64, w: f64, page: *Page) !*DOMPoint { const arena = try page.getArena(.tiny, "DOMPoint"); - errdefer page.releaseArena(arena); + errdefer arena.release(); - const self = try Factory.chainedWithAllocator(arena, .{ + const self = try Factory.chainedWithAllocator(arena.allocator(), .{ RO.buildValue(arena, x, y, z, w), DOMPoint{ ._proto = undefined }, }); diff --git a/src/browser/webapi/DOMPointReadOnly.zig b/src/browser/webapi/DOMPointReadOnly.zig index 277c57b2b..f1d453871 100644 --- a/src/browser/webapi/DOMPointReadOnly.zig +++ b/src/browser/webapi/DOMPointReadOnly.zig @@ -24,15 +24,13 @@ const Page = @import("../Page.zig"); const DOMPoint = @import("DOMPoint.zig"); const Matrix = @import("DOMMatrixReadOnly.zig"); -const Allocator = std.mem.Allocator; - const DOMPointReadOnly = @This(); pub const _prototype_root = true; _type: Type, _rc: lp.RC, -_arena: Allocator, +_arena: *lp.Arena, _x: f64, _y: f64, @@ -72,8 +70,8 @@ pub fn init(x_: ?f64, y_: ?f64, z_: ?f64, w_: ?f64, exec: *const js.Execution) ! return createBare(x_ orelse 0, y_ orelse 0, z_ orelse 0, w_ orelse 1, exec.page); } -pub fn deinit(self: *DOMPointReadOnly, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *DOMPointReadOnly, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *DOMPointReadOnly) void { @@ -86,14 +84,14 @@ pub fn releaseRef(self: *DOMPointReadOnly, page: *Page) void { pub fn createBare(x: f64, y: f64, z: f64, w: f64, page: *Page) !*DOMPointReadOnly { const arena = try page.getArena(.tiny, "DOMPoint"); - errdefer page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(DOMPointReadOnly); self.* = buildValue(arena, x, y, z, w); return self; } -pub fn buildValue(arena: Allocator, x: f64, y: f64, z: f64, w: f64) DOMPointReadOnly { +pub fn buildValue(arena: *lp.Arena, x: f64, y: f64, z: f64, w: f64) DOMPointReadOnly { return .{ ._rc = .{}, ._arena = arena, diff --git a/src/browser/webapi/DataTransfer.zig b/src/browser/webapi/DataTransfer.zig index 8db7e5dec..2d23a4d83 100644 --- a/src/browser/webapi/DataTransfer.zig +++ b/src/browser/webapi/DataTransfer.zig @@ -47,7 +47,7 @@ pub fn registerTypes() []const type { }; } -_arena: Allocator, +_arena: *lp.Arena, // Refcounted so the GC weak-finalizer (or page teardown) releases the pooled // arena exactly once; mirrors Blob's lifecycle. _rc: lp.RC = .{}, @@ -61,7 +61,7 @@ _effect_allowed: []const u8 = "uninitialized", pub fn init(frame: *Frame) !*DataTransfer { const arena = try frame.getArena(.medium, "DataTransfer"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const fl = try frame._factory.create(FileList{}); try frame.trackFileList(fl); @@ -77,8 +77,8 @@ pub fn init(frame: *Frame) !*DataTransfer { return self; } -pub fn deinit(self: *DataTransfer, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *DataTransfer, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *DataTransfer) void { @@ -112,7 +112,7 @@ pub fn getData(self: *const DataTransfer, format: []const u8, frame: *Frame) ![] } pub fn setData(self: *DataTransfer, format: []const u8, data: []const u8) !void { - const norm = try normalizeFormat(self._arena, format); + const norm = try normalizeFormat(self._arena.allocator(), format); const owned_data = try self._arena.dupe(u8, data); for (self._items.items) |it| { if (it._kind == .string and std.mem.eql(u8, it._type, norm)) { @@ -122,7 +122,7 @@ pub fn setData(self: *DataTransfer, format: []const u8, data: []const u8) !void } const it = try self._arena.create(DataTransferItem); it.* = .{ ._data_transfer = self, ._kind = .string, ._type = norm, ._payload = .{ .string = owned_data } }; - try self._items.append(self._arena, it); + try self._items.append(self._arena.allocator(), it); } pub fn clearData(self: *DataTransfer, format_: ?[]const u8, frame: *Frame) !void { @@ -158,11 +158,11 @@ pub fn addItem(self: *DataTransfer, data: js.Value, type_: ?[]const u8, frame: * return try self.addFileItem(file, frame); } else |_| {} - const owned_data = try data.toStringSliceWithAlloc(self._arena); - const norm = try normalizeFormat(self._arena, type_ orelse ""); + const owned_data = try data.toStringSliceWithAlloc(self._arena.allocator()); + const norm = try normalizeFormat(self._arena.allocator(), type_ orelse ""); const it = try self._arena.create(DataTransferItem); it.* = .{ ._data_transfer = self, ._kind = .string, ._type = norm, ._payload = .{ .string = owned_data } }; - try self._items.append(self._arena, it); + try self._items.append(self._arena.allocator(), it); return it; } @@ -170,7 +170,7 @@ fn addFileItem(self: *DataTransfer, file: *File, frame: *Frame) !*DataTransferIt file._proto.acquireRef(); const it = try self._arena.create(DataTransferItem); it.* = .{ ._data_transfer = self, ._kind = .file, ._type = file._proto.getType(), ._payload = .{ .file = file } }; - try self._items.append(self._arena, it); + try self._items.append(self._arena.allocator(), it); try self.rebuildFiles(frame); return it; } diff --git a/src/browser/webapi/DedicatedWorkerGlobalScope.zig b/src/browser/webapi/DedicatedWorkerGlobalScope.zig index c1704477b..249bf4aa0 100644 --- a/src/browser/webapi/DedicatedWorkerGlobalScope.zig +++ b/src/browser/webapi/DedicatedWorkerGlobalScope.zig @@ -33,8 +33,6 @@ const WorkerGlobalScope = @import("WorkerGlobalScope.zig"); const MessageEvent = @import("event/MessageEvent.zig"); -const Allocator = std.mem.Allocator; - const DedicatedWorkerGlobalScope = @This(); pub const Proto = WorkerGlobalScope; @@ -54,7 +52,7 @@ _pending_messages: std.ArrayList(?js.Value.Global) = .empty, pub fn init(worker: *Worker, url: [:0]const u8) !*DedicatedWorkerGlobalScope { return WorkerGlobalScope.init( - worker._arena, + worker._arena.allocator(), url, .dedicated, DedicatedWorkerGlobalScope{ @@ -152,7 +150,7 @@ fn scheduleMessage(self: *DedicatedWorkerGlobalScope, cloned_data: ?js.Value.Glo const session = wgs._session; const message_arena = try session.getArena(.tiny, "DedicatedWorkerGlobalScope.receiveMessage"); - errdefer session.releaseArena(message_arena); + errdefer message_arena.release(); const callback = try message_arena.create(ReceiveMessageCallback); callback.* = .{ @@ -184,7 +182,7 @@ pub fn drainPendingMessages(self: *DedicatedWorkerGlobalScope) void { const ReceiveMessageCallback = struct { data: ?js.Value.Global, - arena: Allocator, + arena: *lp.Arena, worker_scope: *DedicatedWorkerGlobalScope, fn cancelled(ctx: *anyopaque) void { @@ -196,7 +194,7 @@ const ReceiveMessageCallback = struct { } fn deinit(self: *ReceiveMessageCallback) void { - self.worker_scope._proto._session.releaseArena(self.arena); + self.arena.release(); } fn run(ctx: *anyopaque) !?u32 { diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index 367ba2e87..8e28a65c6 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -1056,9 +1056,9 @@ fn writeInternal(self: *Document, text: []const []const u8, append_newline: bool defer frame._parse_mode = previous_parse_mode; const arena = try frame.getArena(.medium, "Document.write"); - defer frame.releaseArena(arena); + defer arena.release(); - var parser = Parser.init(arena, fragment_node, frame, .{ .allow_declarative_shadow = true }); + var parser = Parser.init(arena.allocator(), fragment_node, frame, .{ .allow_declarative_shadow = true }); parser.parseFragment(html); // Extract children from wrapper HTML element (html5ever wraps fragments) @@ -1070,7 +1070,7 @@ fn writeInternal(self: *Document, text: []const []const u8, append_newline: bool var it = if (first.is(Element.Html.Html) == null) fragment_node.childrenIterator() else first.childrenIterator(); while (it.next()) |child| { - try children_to_insert.append(arena, child); + try children_to_insert.append(arena.allocator(), child); } if (children_to_insert.items.len == 0) { diff --git a/src/browser/webapi/Event.zig b/src/browser/webapi/Event.zig index 5dc4f6251..25a6ae733 100644 --- a/src/browser/webapi/Event.zig +++ b/src/browser/webapi/Event.zig @@ -27,13 +27,12 @@ const EventTarget = @import("EventTarget.zig"); const String = lp.String; const Execution = js.Execution; -const Allocator = std.mem.Allocator; pub const Event = @This(); pub const _prototype_root = true; _type: Type, -_arena: Allocator, +_arena: *lp.Arena, _bubbles: bool = false, _cancelable: bool = false, _composed: bool = false, @@ -107,18 +106,18 @@ pub const Options = struct { pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event { const arena = try page.getArena(.tiny, "Event"); - errdefer page.releaseArena(arena); - const str = try String.init(arena, typ, .{}); + errdefer arena.release(); + const str = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, str, opts_, false); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*Event { const arena = try page.getArena(.tiny, "Event.trusted"); - errdefer page.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, opts_, true); } -fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, comptime trusted: bool) !*Event { +fn initWithTrusted(arena: *lp.Arena, typ: String, opts_: ?Options, comptime trusted: bool) !*Event { const opts = opts_ orelse Options{}; // Same (already coarsened) clock as the performance time origin, so the @@ -150,7 +149,7 @@ pub fn initEvent( } self._initialized = true; - self._type_string = try String.init(self._arena, event_string, .{}); + self._type_string = try String.init(self._arena.allocator(), event_string, .{}); self._bubbles = bubbles orelse false; self._cancelable = cancelable orelse false; self._stop_propagation = false; @@ -162,8 +161,8 @@ pub fn acquireRef(self: *Event) void { self._rc.acquire(); } -pub fn deinit(self: *Event, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Event, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *Event, page: *Page) void { diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index a99f34cfa..ff5b4fa50 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -47,9 +47,9 @@ pub fn init( const opts = opts_ orelse InitOptions{}; const session = page.session; const arena = try session.getArena(.large, "Blob"); - errdefer session.releaseArena(arena); + errdefer arena.release(); - const file = try Factory.chainedWithAllocator(arena, .{ + const file = try Factory.chainedWithAllocator(arena.allocator(), .{ try Blob.buildValue(arena, parts_, .{ .type = opts.type, .endings = opts.endings, @@ -90,9 +90,9 @@ pub fn structuredDeserialize(reader: *js.StructuredReader, page: *Page) !*File { const last_modified = try reader.readUint64(); const arena = try page.getArena(data.len + mime.len + name.len + 256, "Blob.clone"); - errdefer page.releaseArena(arena); + errdefer arena.release(); - const file = try Factory.chainedWithAllocator(arena, .{ + const file = try Factory.chainedWithAllocator(arena.allocator(), .{ Blob{ ._rc = .{}, ._arena = arena, diff --git a/src/browser/webapi/FileReader.zig b/src/browser/webapi/FileReader.zig index 1c0098bdc..e7e8f82ef 100644 --- a/src/browser/webapi/FileReader.zig +++ b/src/browser/webapi/FileReader.zig @@ -38,7 +38,7 @@ pub const Proto = EventTarget; _rc: lp.RC = .{}, _exec: *Execution, _proto: *EventTarget, -_arena: Allocator, +_arena: *lp.Arena, _ready_state: ReadyState = .empty, _result: ?Result = null, @@ -67,16 +67,16 @@ const Result = union(enum) { pub fn init(exec: *Execution) !*FileReader { const arena = try exec.getArena(.tiny, "FileReader"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); - return exec._factory.eventTargetWithAllocator(arena, FileReader{ + return exec._factory.eventTargetWithAllocator(arena.allocator(), FileReader{ ._exec = exec, ._arena = arena, ._proto = undefined, }); } -pub fn deinit(self: *FileReader, page: *Page) void { +pub fn deinit(self: *FileReader, _: *Page) void { if (self._on_abort) |func| func.release(); if (self._on_error) |func| func.release(); if (self._on_load) |func| func.release(); @@ -84,7 +84,7 @@ pub fn deinit(self: *FileReader, page: *Page) void { if (self._on_load_start) |func| func.release(); if (self._on_progress) |func| func.release(); - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *FileReader, page: *Page) void { @@ -219,7 +219,7 @@ fn readInternal(self: *FileReader, blob: *Blob, read_type: ReadType) !void { .data_url => blk: { // Create data URL with base64 encoding const mime = if (blob._mime.len > 0) blob._mime else "application/octet-stream"; - const data_url = try encodeDataURL(self._arena, mime, data); + const data_url = try encodeDataURL(self._arena.allocator(), mime, data); break :blk .{ .string = data_url }; }, }; diff --git a/src/browser/webapi/History.zig b/src/browser/webapi/History.zig index f8eed85cc..25bc81cdf 100644 --- a/src/browser/webapi/History.zig +++ b/src/browser/webapi/History.zig @@ -54,11 +54,11 @@ pub fn pushState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const u8 const session = frame._session; const arena = session.arena; const url = if (_url) |u| - try @import("../URL.zig").resolve(arena, frame.url, u, .{}) + try @import("../URL.zig").resolve(arena.allocator(), frame.url, u, .{}) else try arena.dupeZ(u8, frame.url); - const json = state.toJson(arena) catch return error.DataClone; + const json = state.toJson(arena.allocator()) catch return error.DataClone; _ = try session.navigation.pushEntry(url, .{ .source = .history, .value = json }, frame, true); frame.url = url; @@ -76,11 +76,11 @@ pub fn replaceState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const const session = frame._session; const arena = session.arena; const url = if (_url) |u| - try @import("../URL.zig").resolve(arena, frame.url, u, .{}) + try @import("../URL.zig").resolve(arena.allocator(), frame.url, u, .{}) else try arena.dupeZ(u8, frame.url); - const json = state.toJson(arena) catch return error.DataClone; + const json = state.toJson(arena.allocator()) catch return error.DataClone; _ = try session.navigation.replaceEntry(url, .{ .source = .history, .value = json }, frame, true); frame.url = url; diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index f9932fdd4..de6e7c624 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -28,7 +28,6 @@ const Element = @import("Element.zig"); const DOMRect = @import("DOMRect.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; pub fn registerTypes() []const type { return &.{ @@ -40,7 +39,7 @@ pub fn registerTypes() []const type { const IntersectionObserver = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _callback: js.Function.Global, _observing: std.ArrayList(*Element) = .empty, _root: ?*Element = null, @@ -67,7 +66,7 @@ pub const ObserverInit = struct { pub fn init(callback: js.Function.Global, options: ?ObserverInit, frame: *Frame) !*IntersectionObserver { const arena = try frame.getArena(.small, "IntersectionObserver"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const opts = options orelse ObserverInit{}; const root_margin = if (opts.rootMargin) |rm| try arena.dupe(u8, rm) else "0px"; @@ -113,7 +112,7 @@ pub fn deinit(self: *IntersectionObserver, page: *Page) void { // FinalizerCallback. We 100% own them. entry.deinit(page); } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *IntersectionObserver, page: *Page) void { @@ -132,12 +131,12 @@ pub fn observe(self: *IntersectionObserver, target: *Element, frame: *Frame) !vo } } - try self._observing.append(self._arena, target); + try self._observing.append(self._arena.allocator(), target); if (self._observing.items.len == 1) { try Frame.observers.registerIntersectionObserver(frame, self); } - try self._tracked.put(self._arena, target, {}); + try self._tracked.put(self._arena.allocator(), target, {}); // Check intersection for this new target and schedule delivery try self.checkIntersection(target, frame); @@ -266,7 +265,7 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, frame: *Fram // document to fake a position (O(node count)) — so it only runs here. const data = try self.calculateIntersection(target, has_parent, frame); const arena = try frame.getArena(.tiny, "IntersectionObserverEntry"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const entry = try arena.create(IntersectionObserverEntry); entry.* = .{ @@ -279,7 +278,7 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, frame: *Fram ._bounding_client_rect = try DOMRect.create(data.bounding_client_rect, frame._factory), ._intersection_ratio = data.intersection_ratio, }; - try self._pending_entries.append(self._arena, entry); + try self._pending_entries.append(self._arena.allocator(), entry); _ = self._tracked.removeByPtr(tracked.key_ptr); } @@ -317,7 +316,7 @@ pub fn deliverEntries(self: *IntersectionObserver, frame: *Frame) !void { pub const IntersectionObserverEntry = struct { _rc: lp.RC = .{}, - _arena: Allocator, + _arena: *lp.Arena, _time: f64, _target: *Element, _bounding_client_rect: *DOMRect, @@ -326,8 +325,8 @@ pub const IntersectionObserverEntry = struct { _intersection_ratio: f64, _is_intersecting: bool, - pub fn deinit(self: *IntersectionObserverEntry, page: *Page) void { - page.releaseArena(self._arena); + pub fn deinit(self: *IntersectionObserverEntry, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *IntersectionObserverEntry, page: *Page) void { diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig index b63f30c86..a11782c19 100644 --- a/src/browser/webapi/MutationObserver.zig +++ b/src/browser/webapi/MutationObserver.zig @@ -28,7 +28,6 @@ const Element = @import("Element.zig"); const log = lp.log; const String = lp.String; -const Allocator = std.mem.Allocator; pub fn registerTypes() []const type { return &.{ @@ -40,7 +39,7 @@ pub fn registerTypes() []const type { const MutationObserver = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _callback: js.Function.Global, _observing: std.ArrayList(Observing) = .empty, _pending_records: std.ArrayList(*MutationRecord) = .empty, @@ -76,7 +75,7 @@ pub const ObserveOptions = struct { pub fn init(callback: js.Function.Global, frame: *Frame) !*MutationObserver { const arena = try frame.getArena(.small, "MutationObserver"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(MutationObserver); self.* = .{ ._arena = arena, @@ -92,7 +91,7 @@ pub fn deinit(self: *MutationObserver, page: *Page) void { record.deinit(page); } self._callback.release(); - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *MutationObserver, page: *Page) void { @@ -165,7 +164,7 @@ pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions, } } - try self._observing.append(arena, .{ + try self._observing.append(arena.allocator(), .{ .target = target, .options = store_options, }); @@ -242,7 +241,7 @@ pub fn notifyAttributeChange( ._next_sibling = null, }; - try self._pending_records.append(self._arena, record); + try self._pending_records.append(self._arena.allocator(), record); try Frame.observers.scheduleMutationDelivery(frame); break; @@ -286,7 +285,7 @@ pub fn notifyCharacterDataChange( ._next_sibling = null, }; - try self._pending_records.append(self._arena, record); + try self._pending_records.append(self._arena.allocator(), record); try Frame.observers.scheduleMutationDelivery(frame); break; @@ -330,7 +329,7 @@ pub fn notifyChildListChange( ._next_sibling = next_sibling, }; - try self._pending_records.append(self._arena, record); + try self._pending_records.append(self._arena.allocator(), record); try Frame.observers.scheduleMutationDelivery(frame); break; @@ -360,7 +359,7 @@ pub const MutationRecord = struct { _rc: lp.RC = .{}, _type: Type, _target: *Node, - _arena: Allocator, + _arena: *lp.Arena, _attribute_name: ?[]const u8, _old_value: ?[]const u8, _added_nodes: []const *Node, @@ -374,8 +373,8 @@ pub const MutationRecord = struct { characterData, }; - pub fn deinit(self: *MutationRecord, session: *Page) void { - session.releaseArena(self._arena); + pub fn deinit(self: *MutationRecord, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *MutationRecord, session: *Page) void { diff --git a/src/browser/webapi/Notification.zig b/src/browser/webapi/Notification.zig index 2660fdb6b..d719bfed8 100644 --- a/src/browser/webapi/Notification.zig +++ b/src/browser/webapi/Notification.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../js/js.zig"); @@ -25,14 +24,13 @@ const Page = @import("../Page.zig"); const EventTarget = @import("EventTarget.zig"); const Execution = js.Execution; -const Allocator = std.mem.Allocator; const Notification = @This(); pub const Proto = EventTarget; _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _proto: *EventTarget, _title: []const u8, _body: []const u8 = "", @@ -61,10 +59,10 @@ const Options = struct { pub fn init(title: []const u8, options_: ?Options, exec: *const Execution) !*Notification { const arena = try exec.getArena(.small, "Notification"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const options = options_ orelse Options{}; - return exec._factory.eventTargetWithAllocator(arena, Notification{ + return exec._factory.eventTargetWithAllocator(arena.allocator(), Notification{ ._arena = arena, ._proto = undefined, ._title = try arena.dupe(u8, title), @@ -81,8 +79,8 @@ pub fn init(title: []const u8, options_: ?Options, exec: *const Execution) !*Not }); } -pub fn deinit(self: *Notification, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Notification, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *Notification, page: *Page) void { diff --git a/src/browser/webapi/Permissions.zig b/src/browser/webapi/Permissions.zig index c297d139c..c06779e5a 100644 --- a/src/browser/webapi/Permissions.zig +++ b/src/browser/webapi/Permissions.zig @@ -16,15 +16,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../js/js.zig"); const Page = @import("../Page.zig"); const Execution = js.Execution; -const Allocator = std.mem.Allocator; - pub fn registerTypes() []const type { return &.{ Permissions, PermissionStatus }; } @@ -48,7 +45,7 @@ const QueryDescriptor = struct { // 'prompt' (the default safe value — neither granted nor denied) when unset. pub fn query(_: *const Permissions, qd: QueryDescriptor, exec: *const Execution) !js.Promise { const arena = try exec.getArena(.tiny, "PermissionStatus"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const state = exec.session.browser.permissions.get(qd.name) orelse .prompt; const status = try arena.create(PermissionStatus); @@ -62,12 +59,12 @@ pub fn query(_: *const Permissions, qd: QueryDescriptor, exec: *const Execution) const PermissionStatus = struct { _rc: lp.RC = .{}, - _arena: Allocator, + _arena: *lp.Arena, _name: []const u8, _state: State, - pub fn deinit(self: *PermissionStatus, page: *Page) void { - page.releaseArena(self._arena); + pub fn deinit(self: *PermissionStatus, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *PermissionStatus, page: *Page) void { diff --git a/src/browser/webapi/Range.zig b/src/browser/webapi/Range.zig index 3daf83824..30da788d6 100644 --- a/src/browser/webapi/Range.zig +++ b/src/browser/webapi/Range.zig @@ -41,7 +41,7 @@ pub fn init(frame: *Frame) !*Range { // frame's main document. pub fn initIn(container: *Node, frame: *Frame) !*Range { const arena = try frame.getArena(.medium, "Range"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const range = try frame._factory.abstractRange(arena, Range{ ._proto = undefined }, frame); range._proto._start_container = container; range._proto._end_container = container; @@ -328,7 +328,7 @@ pub fn intersectsNode(self: *const Range, node: *Node) bool { pub fn cloneRange(self: *const Range, frame: *Frame) !*Range { const arena = try frame.getArena(.medium, "Range.clone"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const clone = try frame._factory.abstractRange(arena, Range{ ._proto = undefined }, frame); clone._proto._end_offset = self._proto._end_offset; diff --git a/src/browser/webapi/ResizeObserver.zig b/src/browser/webapi/ResizeObserver.zig index 6dac4a385..f74ee3201 100644 --- a/src/browser/webapi/ResizeObserver.zig +++ b/src/browser/webapi/ResizeObserver.zig @@ -33,7 +33,6 @@ const DOMRect = @import("DOMRect.zig"); const Factory = @import("../Factory.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; pub fn registerTypes() []const type { return &.{ @@ -46,7 +45,7 @@ pub fn registerTypes() []const type { const ResizeObserver = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _callback: js.Function.Global, _observations: std.ArrayList(Observation) = .empty, @@ -63,7 +62,7 @@ const Options = struct { pub fn init(callback: js.Function.Global, frame: *Frame) !*ResizeObserver { const arena = try frame.getArena(.small, "ResizeObserver"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(ResizeObserver); self.* = .{ @@ -73,9 +72,9 @@ pub fn init(callback: js.Function.Global, frame: *Frame) !*ResizeObserver { return self; } -pub fn deinit(self: *ResizeObserver, page: *Page) void { +pub fn deinit(self: *ResizeObserver, _: *Page) void { self._callback.release(); - page.releaseArena(self._arena); + self._arena.release(); } pub fn acquireRef(self: *ResizeObserver) void { @@ -95,7 +94,7 @@ pub fn observe(self: *ResizeObserver, target: *Element, options_: ?Options, fram } } - try self._observations.append(self._arena, .{ .target = target }); + try self._observations.append(self._arena.allocator(), .{ .target = target }); if (self._observations.items.len == 1) { try Frame.observers.registerResizeObserver(frame, self); } diff --git a/src/browser/webapi/SharedWorkerGlobalScope.zig b/src/browser/webapi/SharedWorkerGlobalScope.zig index 9a30447b1..b1013d6c2 100644 --- a/src/browser/webapi/SharedWorkerGlobalScope.zig +++ b/src/browser/webapi/SharedWorkerGlobalScope.zig @@ -30,7 +30,6 @@ const WorkerGlobalScope = @import("WorkerGlobalScope.zig"); const MessageEvent = @import("event/MessageEvent.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; const SharedWorkerGlobalScope = @This(); @@ -38,7 +37,7 @@ const SharedWorkerGlobalScope = @This(); pub const Proto = WorkerGlobalScope; _proto: *WorkerGlobalScope, -_arena: Allocator, +_arena: *lp.Arena, _url: [:0]const u8, _name: []const u8, @@ -55,7 +54,7 @@ _loader_id: u32, _closed: bool = false, _script_loaded: bool = false, -_script_arena: ?Allocator = null, +_script_arena: ?*lp.Arena = null, _script_buffer: std.ArrayList(u8) = .empty, _http_transfer: ?*Transfer = null, @@ -68,13 +67,13 @@ pub fn init(frame: *Frame, url: [:0]const u8, name: []const u8, worker_type: Wor const session = frame._session; const arena = try session.getArena(.small, "SharedWorker"); - errdefer session.releaseArena(arena); + errdefer arena.release(); const owned_url = try arena.dupeZ(u8, url); const frame_id = session.nextFrameId(); const loader_id = session.nextLoaderId(); const self = try WorkerGlobalScope.init( - arena, + arena.allocator(), owned_url, .shared, SharedWorkerGlobalScope{ @@ -143,7 +142,7 @@ pub fn deinit(self: *SharedWorkerGlobalScope) void { self.releaseScriptArena(); self.unregister(); self._proto.deinit(); - self._proto._session.releaseArena(self._arena); + self._arena.release(); } pub fn register(self: *SharedWorkerGlobalScope, lookup_key: []const u8) !void { @@ -152,7 +151,7 @@ pub fn register(self: *SharedWorkerGlobalScope, lookup_key: []const u8) !void { const key = try self._arena.dupe(u8, lookup_key); const session = self._proto._session; - try session.shared_workers.put(session.arena, key, self); + try session.shared_workers.put(session.arena.allocator(), key, self); self._registry_key = key; } @@ -172,7 +171,7 @@ pub fn connect(self: *SharedWorkerGlobalScope, client_exec: *js.Execution) !*Mes MessagePort.entangle(client_port, worker_port); if (self._script_loaded == false) { - try self._pending_connects.append(self._arena, worker_port); + try self._pending_connects.append(self._arena.allocator(), worker_port); return client_port; } @@ -214,7 +213,7 @@ fn httpHeaderCallback(transfer: *Transfer) !Transfer.HeaderResult { } if (transfer.getContentLength()) |cl| { - try self._script_buffer.ensureTotalCapacity(self._script_arena.?, cl); + try self._script_buffer.ensureTotalCapacity(self._script_arena.?.allocator(), cl); } return .proceed; @@ -222,7 +221,7 @@ fn httpHeaderCallback(transfer: *Transfer) !Transfer.HeaderResult { fn httpDataCallback(transfer: *Transfer, data: []const u8) !void { const self: *SharedWorkerGlobalScope = @ptrCast(@alignCast(transfer.req.ctx)); - try self._script_buffer.appendSlice(self._script_arena.?, data); + try self._script_buffer.appendSlice(self._script_arena.?.allocator(), data); } fn httpDoneCallback(ctx: *anyopaque) !void { @@ -297,7 +296,7 @@ fn loadInitialScript(self: *SharedWorkerGlobalScope, script: []const u8) !void { } js_context.page.recordJsError(err); - const caught = try_catch.caughtOrError(self._script_arena.?, err); + const caught = try_catch.caughtOrError(self._script_arena.?.allocator(), err); log.err(.browser, "shared worker script error", .{ .url = self._url, .caught = caught }); return; }, @@ -307,7 +306,7 @@ fn loadInitialScript(self: *SharedWorkerGlobalScope, script: []const u8) !void { } js_context.page.recordJsError(err); - const caught = try_catch.caughtOrError(self._script_arena.?, err); + const caught = try_catch.caughtOrError(self._script_arena.?.allocator(), err); log.err(.browser, "shared worker module error", .{ .url = self._url, .caught = caught }); return; }, @@ -322,7 +321,7 @@ fn releaseScriptArena(self: *SharedWorkerGlobalScope) void { const arena = self._script_arena orelse return; self._script_arena = null; self._script_buffer = .empty; - self._proto._session.releaseArena(arena); + arena.release(); } fn drainPendingConnects(self: *SharedWorkerGlobalScope) void { @@ -339,7 +338,7 @@ fn scheduleConnect(self: *SharedWorkerGlobalScope, port: *MessagePort) !void { const session = wgs._session; const connect_arena = try session.getArena(.tiny, "SharedWorkerGlobalScope.connect"); - errdefer session.releaseArena(connect_arena); + errdefer connect_arena.release(); const callback = try connect_arena.create(ConnectCallback); callback.* = .{ @@ -357,7 +356,7 @@ fn scheduleConnect(self: *SharedWorkerGlobalScope, port: *MessagePort) !void { const ConnectCallback = struct { port: *MessagePort, - arena: Allocator, + arena: *lp.Arena, worker_scope: *SharedWorkerGlobalScope, fn cancelled(ctx: *anyopaque) void { @@ -366,7 +365,7 @@ const ConnectCallback = struct { } fn deinit(self: *ConnectCallback) void { - self.worker_scope._proto._session.releaseArena(self.arena); + self.arena.release(); } fn run(ctx: *anyopaque) !?u32 { diff --git a/src/browser/webapi/StaticRange.zig b/src/browser/webapi/StaticRange.zig index 823ca4241..518ad847a 100644 --- a/src/browser/webapi/StaticRange.zig +++ b/src/browser/webapi/StaticRange.zig @@ -50,7 +50,7 @@ pub fn init(opts: StaticRangeInit, frame: *Frame) !*StaticRange { } const arena = try frame.getArena(.medium, "StaticRange"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const static_range = try frame._factory.abstractRange(arena, StaticRange{ ._proto = undefined }, frame); const proto = static_range._proto; diff --git a/src/browser/webapi/Timers.zig b/src/browser/webapi/Timers.zig index c3cdd876a..a3d6d4e59 100644 --- a/src/browser/webapi/Timers.zig +++ b/src/browser/webapi/Timers.zig @@ -27,7 +27,6 @@ const lp = @import("lightpanda"); const js = @import("../js/js.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; const CLAMP_MS = 4; const CLAMP_NESTING = 5; @@ -97,7 +96,7 @@ pub fn schedule( } const arena = try exec.getArena(.tiny, "Timers.schedule"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const timer_id = self._timer_id +% 1; self._timer_id = timer_id; @@ -191,7 +190,7 @@ const ScheduleCallback = struct { mode: Mode, exec: *js.Execution, timers: *Timers, - arena: Allocator, + arena: *lp.Arena, removed: bool = false, params: []const js.Value.Global, @@ -205,7 +204,7 @@ const ScheduleCallback = struct { for (self.params) |param| { param.release(); } - self.exec.releaseArena(self.arena); + self.arena.release(); } fn run(ptr: *anyopaque) !?u32 { diff --git a/src/browser/webapi/WebDriver.zig b/src/browser/webapi/WebDriver.zig index d0a0158e6..57ae22def 100644 --- a/src/browser/webapi/WebDriver.zig +++ b/src/browser/webapi/WebDriver.zig @@ -35,7 +35,6 @@ const TouchEvent = @import("event/TouchEvent.zig"); const EventManagerBase = @import("../EventManagerBase.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; // This type is only included when the binary is built with the -Dwpt_extensions flag const WebDriver = @This(); @@ -135,7 +134,7 @@ pub fn actionSequence(_: *const WebDriver, sources: js.Value, frame: *Frame) !js } const arena = try frame.getArena(.tiny, "WebDriver.actionSequence"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const persisted = try sources.persist(); errdefer persisted.release(); @@ -164,7 +163,7 @@ pub fn actionSequence(_: *const WebDriver, sources: js.Value, frame: *Frame) !js const ActionSequence = struct { frame: *Frame, - arena: Allocator, + arena: *lp.Arena, sources: js.Value.Global, resolver: js.PromiseResolver.Global, @@ -211,7 +210,7 @@ const ActionSequence = struct { fn deinit(self: *ActionSequence) void { self.sources.release(); self.resolver.release(); - self.frame.releaseArena(self.arena); + self.arena.release(); } }; diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 542b65a68..ac6e4a850 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -55,7 +55,6 @@ const Notification = @import("../../Notification.zig"); const log = lp.log; const IS_DEBUG = builtin.mode == .Debug; -const Allocator = std.mem.Allocator; const Execution = js.Execution; pub fn registerTypes() []const type { @@ -816,7 +815,7 @@ pub fn postMessage(self: *Window, message: js.Value, target_origin: ?[]const u8, const source_window = target_frame.js.getIncumbent().window; const arena = try target_frame.getArena(.medium, "Window.postMessage"); - errdefer target_frame.releaseArena(arena); + errdefer arena.release(); // StructuredSerialize runs synchronously (per spec): clone the message into // the target window's realm now. The receiver gets a fresh, independent copy @@ -1079,13 +1078,13 @@ pub const Access = union(enum) { const PostMessageCallback = struct { frame: *Frame, source: *Window, - arena: Allocator, + arena: *lp.Arena, origin: []const u8, message: js.Value.Global, ports: []const *MessagePort, fn deinit(self: *PostMessageCallback) void { - self.frame.releaseArena(self.arena); + self.arena.release(); } // Called by the scheduler if the task is dropped before it runs. `run` and diff --git a/src/browser/webapi/Worker.zig b/src/browser/webapi/Worker.zig index f4370b93d..941a24415 100644 --- a/src/browser/webapi/Worker.zig +++ b/src/browser/webapi/Worker.zig @@ -31,7 +31,6 @@ const ErrorEvent = @import("event/ErrorEvent.zig"); const DedicatedWorkerGlobalScope = @import("DedicatedWorkerGlobalScope.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; const Worker = @This(); @@ -51,13 +50,13 @@ _loader_id: u32, _proto: *EventTarget, _frame: *Frame, -_arena: Allocator, +_arena: *lp.Arena, _worker_scope: *DedicatedWorkerGlobalScope, _url: [:0]const u8, _type: WorkerType = .classic, _script_loaded: bool = false, -_script_arena: ?Allocator = null, +_script_arena: ?*lp.Arena = null, _script_buffer: std.ArrayList(u8) = .empty, _http_transfer: ?*Transfer = null, @@ -74,10 +73,10 @@ pub fn init(url: []const u8, options: ?WorkerOptions, frame: *Frame) !*Worker { const session = frame._session; const arena = try session.getArena(.small, "Worker"); - errdefer session.releaseArena(arena); + errdefer arena.release(); - const resolved_url = try URL.resolve(arena, frame.base(), url, .{ .encoding = frame.charset }); - const self = try frame._page.factory.eventTargetWithAllocator(arena, Worker{ + const resolved_url = try URL.resolve(arena.allocator(), frame.base(), url, .{ .encoding = frame.charset }); + const self = try frame._page.factory.eventTargetWithAllocator(arena.allocator(), Worker{ ._arena = arena, ._proto = undefined, ._frame = frame, @@ -152,7 +151,7 @@ pub fn deinit(self: *Worker) void { } self.releaseScriptArena(); self._worker_scope.deinit(); - self._frame._session.releaseArena(self._arena); + self._arena.release(); } pub fn asEventTarget(self: *Worker) *EventTarget { @@ -172,7 +171,7 @@ fn httpHeaderCallback(transfer: *Transfer) !Transfer.HeaderResult { } if (transfer.getContentLength()) |cl| { - try self._script_buffer.ensureTotalCapacity(self._script_arena.?, cl); + try self._script_buffer.ensureTotalCapacity(self._script_arena.?.allocator(), cl); } return .proceed; @@ -180,7 +179,7 @@ fn httpHeaderCallback(transfer: *Transfer) !Transfer.HeaderResult { fn httpDataCallback(transfer: *Transfer, data: []const u8) !void { const self: *Worker = @ptrCast(@alignCast(transfer.req.ctx)); - try self._script_buffer.appendSlice(self._script_arena.?, data); + try self._script_buffer.appendSlice(self._script_arena.?.allocator(), data); } fn httpDoneCallback(ctx: *anyopaque) !void { @@ -244,7 +243,7 @@ fn loadInitialScript(self: *Worker, script: []const u8) !void { } js_context.page.recordJsError(err); - const caught = try_catch.caughtOrError(self._script_arena.?, err); + const caught = try_catch.caughtOrError(self._script_arena.?.allocator(), err); log.err(.browser, "worker script error", .{ .url = self._url, .caught = caught }); self.fireErrorEvent(caught.exception orelse @errorName(err), null); return; @@ -255,7 +254,7 @@ fn loadInitialScript(self: *Worker, script: []const u8) !void { } js_context.page.recordJsError(err); - const caught = try_catch.caughtOrError(self._script_arena.?, err); + const caught = try_catch.caughtOrError(self._script_arena.?.allocator(), err); log.err(.browser, "worker module error", .{ .url = self._url, .caught = caught }); self.fireErrorEvent(caught.exception orelse @errorName(err), null); return; @@ -295,7 +294,7 @@ fn releaseScriptArena(self: *Worker) void { const arena = self._script_arena orelse return; self._script_arena = null; self._script_buffer = .empty; - self._frame._session.releaseArena(arena); + arena.release(); } // Fire an error event on the Worker object (parent context) @@ -356,7 +355,7 @@ pub fn receiveMessage(self: *Worker, data: js.Value) !void { }; const message_arena = try frame.getArena(.tiny, "Worker.receiveMessage"); - errdefer frame.releaseArena(message_arena); + errdefer message_arena.release(); const callback = try message_arena.create(ReceiveMessageCallback); callback.* = .{ @@ -411,7 +410,7 @@ fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global { const ReceiveMessageCallback = struct { data: anyerror!js.Value.Global, - arena: Allocator, + arena: *lp.Arena, worker: *Worker, fn cancelled(ctx: *anyopaque) void { @@ -423,7 +422,7 @@ const ReceiveMessageCallback = struct { } fn deinit(self: *ReceiveMessageCallback) void { - self.worker._frame._session.releaseArena(self.arena); + self.arena.release(); } fn run(ctx: *anyopaque) !?u32 { diff --git a/src/browser/webapi/WorkerGlobalScope.zig b/src/browser/webapi/WorkerGlobalScope.zig index dc2fc02c5..84ae1ba34 100644 --- a/src/browser/webapi/WorkerGlobalScope.zig +++ b/src/browser/webapi/WorkerGlobalScope.zig @@ -73,6 +73,8 @@ _identity: JS.Identity = .{}, _http_owner: HttpClient.Owner, arena: Allocator, +_call_arena: *lp.Arena, +_local_arena: *lp.Arena, call_arena: Allocator, local_arena: Allocator, url: [:0]const u8, @@ -135,10 +137,10 @@ pub fn init( const session = frame._session; const call_arena = try session.getArena(.small, "WorkerGlobalScope.call_arena"); - errdefer session.releaseArena(call_arena); + errdefer call_arena.release(); const local_arena = try session.getArena(.small, "WorkerGlobalScope.local_arena"); - errdefer session.releaseArena(local_arena); + errdefer local_arena.release(); const factory = frame._factory; const leaf = try Factory.chainedWithAllocator(arena, .{ @@ -148,8 +150,10 @@ pub fn init( .arena = arena, .origin = frame.origin, .js = undefined, - .call_arena = call_arena, - .local_arena = local_arena, + ._call_arena = call_arena, + ._local_arena = local_arena, + .call_arena = call_arena.allocator(), + .local_arena = local_arena.allocator(), ._frame = frame, ._page = frame._page, ._session = session, @@ -181,8 +185,8 @@ pub fn init( ); self.js = try session.browser.env.createWorkerContext(self, .{ - .call_arena = call_arena, - .local_arena = local_arena, + .call_arena = call_arena.allocator(), + .local_arena = local_arena.allocator(), .identity_arena = arena, .identity = &self._identity, }); @@ -216,8 +220,8 @@ pub fn deinit(self: *WorkerGlobalScope) void { page.revokeBlobUrlsFor(self._frame_id); browser.env.destroyContext(self.js); - session.releaseArena(self.call_arena); - session.releaseArena(self.local_arena); + self._call_arena.release(); + self._local_arena.release(); } pub fn base(self: *const WorkerGlobalScope) [:0]const u8 { @@ -393,11 +397,11 @@ pub fn importScripts(self: *WorkerGlobalScope, urls: []const [:0]const u8) !void const session = self._session; const arena = try session.getArena(.large, "importScript"); - defer session.releaseArena(arena); + defer arena.release(); for (urls) |url| { - defer session.arena_pool.resetRetain(arena); - try self.importScript(arena, url); + defer arena.resetRetain(); + try self.importScript(arena.allocator(), url); } } diff --git a/src/browser/webapi/XPathExpression.zig b/src/browser/webapi/XPathExpression.zig index 17afdb6a6..c2b5ca6ec 100644 --- a/src/browser/webapi/XPathExpression.zig +++ b/src/browser/webapi/XPathExpression.zig @@ -22,7 +22,6 @@ //! arena for its own result data so multiple evaluations don't grow //! the AST arena. -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../js/js.zig"); @@ -38,24 +37,22 @@ const xpath = struct { const Evaluator = @import("../xpath/Evaluator.zig"); }; -const Allocator = std.mem.Allocator; - const XPathExpression = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _expr: *const xpath.Ast.Expr, pub fn init(expression: []const u8, frame: *Frame) !*XPathExpression { const arena = try frame.getArena(.tiny, "XPathExpression"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); // The AST borrows string slices from its input (literals, names, // var refs, function names). `expression` is materialized in the JS // call_arena and is reclaimed when the top-level call returns, so // dupe into our long-lived arena before parsing. const owned = try arena.dupe(u8, expression); - const expr = try xpath.Parser.parse(arena, owned); + const expr = try xpath.Parser.parse(arena.allocator(), owned); const xe = try arena.create(XPathExpression); xe.* = .{ ._arena = arena, ._expr = expr }; return xe; @@ -74,14 +71,14 @@ pub fn evaluate( _ = result; const arena = try frame.getArena(.medium, "XPathResult"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); - const eval_result = try xpath.Evaluator.evaluate(arena, self._expr, context_node, frame); + const eval_result = try xpath.Evaluator.evaluate(arena.allocator(), self._expr, context_node, frame); return XPathResult.fromResult(arena, requested_type orelse XPathResult.ANY_TYPE, eval_result); } -pub fn deinit(self: *XPathExpression, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *XPathExpression, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *XPathExpression) void { diff --git a/src/browser/webapi/XPathResult.zig b/src/browser/webapi/XPathResult.zig index 7a407f677..d65cd28f4 100644 --- a/src/browser/webapi/XPathResult.zig +++ b/src/browser/webapi/XPathResult.zig @@ -32,7 +32,6 @@ //! `InvalidStateError` is what decision #4 captures and what most //! legacy XPath consumers expect. -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../js/js.zig"); @@ -49,8 +48,6 @@ const xpath = struct { const Evaluator = @import("../xpath/Evaluator.zig"); }; -const Allocator = std.mem.Allocator; - const XPathResult = @This(); // WHATWG type constants. ANY_TYPE is a request flag — at construction @@ -76,7 +73,7 @@ const Value = union(enum) { }; _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _type: u16, _value: Value, _iter_pos: usize = 0, @@ -93,15 +90,15 @@ pub fn fromExpression( frame: *Frame, ) !*XPathResult { const arena = try frame.getArena(.medium, "XPathResult"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); // The AST borrows string slices from its input (literals, names, // var refs, function names). `expression` is materialized in the JS // call_arena and is reclaimed when the top-level call returns, so // dupe into our long-lived arena before parsing. const owned = try arena.dupe(u8, expression); - const expr = try xpath.Parser.parse(arena, owned); - const result = try xpath.Evaluator.evaluate(arena, expr, context_node, frame); + const expr = try xpath.Parser.parse(arena.allocator(), owned); + const result = try xpath.Evaluator.evaluate(arena.allocator(), expr, context_node, frame); return fromResult(arena, requested_type, result); } @@ -110,7 +107,7 @@ pub fn fromExpression( /// it on deinit. Used by `XPathExpression.evaluate` (which has its own /// AST cache and only allocates a fresh result arena). pub fn fromResult( - arena: Allocator, + arena: *lp.Arena, requested_type: u16, result: xpath.result.Result, ) !*XPathResult { @@ -121,8 +118,8 @@ pub fn fromResult( .boolean => |b| .{ .boolean = b }, .node_set => |ns| .{ .nodes = ns }, }, - NUMBER_TYPE => .{ .number = try xpath.result.toNumber(arena, result) }, - STRING_TYPE => .{ .string = try xpath.result.toString(arena, result) }, + NUMBER_TYPE => .{ .number = try xpath.result.toNumber(arena.allocator(), result) }, + STRING_TYPE => .{ .string = try xpath.result.toString(arena.allocator(), result) }, BOOLEAN_TYPE => .{ .boolean = xpath.result.toBoolean(result) }, UNORDERED_NODE_ITERATOR_TYPE, ORDERED_NODE_ITERATOR_TYPE, @@ -159,8 +156,8 @@ pub fn fromResult( // ----- lifecycle ----- -pub fn deinit(self: *XPathResult, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *XPathResult, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *XPathResult) void { diff --git a/src/browser/webapi/animation/Animation.zig b/src/browser/webapi/animation/Animation.zig index 2139d52b8..e21289458 100644 --- a/src/browser/webapi/animation/Animation.zig +++ b/src/browser/webapi/animation/Animation.zig @@ -16,14 +16,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); const Frame = @import("../../Frame.zig"); const log = lp.log; -const Allocator = std.mem.Allocator; const Animation = @This(); @@ -36,7 +34,7 @@ const PlayState = enum { _rc: lp.RC = .{}, _frame: *Frame, -_arena: Allocator, +_arena: *lp.Arena, _effect: ?js.Object.Global = null, _timeline: ?js.Object.Global = null, @@ -53,7 +51,7 @@ _playState: PlayState = .idle, // TODO add support for effect and timeline pub fn init(frame: *Frame) !*Animation { const arena = try frame.getArena(.tiny, "Animation"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Animation); self.* = .{ @@ -64,8 +62,8 @@ pub fn init(frame: *Frame) !*Animation { return self; } -pub fn deinit(self: *Animation, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Animation, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *Animation, page: *Page) void { diff --git a/src/browser/webapi/collections/ChildNodes.zig b/src/browser/webapi/collections/ChildNodes.zig index 176497593..9609451ad 100644 --- a/src/browser/webapi/collections/ChildNodes.zig +++ b/src/browser/webapi/collections/ChildNodes.zig @@ -16,7 +16,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); +const lp = @import("lightpanda"); const Page = @import("../../Page.zig"); const Frame = @import("../../Frame.zig"); @@ -29,7 +29,7 @@ const GenericIterator = @import("iterator.zig").Entry; // No need to go through a TreeWalker or add any filtering. const ChildNodes = @This(); -_arena: std.mem.Allocator, +_arena: *lp.Arena, _last_index: usize, _last_length: ?u32, _last_node: ?*Node, @@ -42,7 +42,7 @@ pub const EntryIterator = GenericIterator(Iterator, null); pub fn init(node: *Node, frame: *Frame) !*ChildNodes { const arena = try frame.getArena(.small, "ChildNodes"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(ChildNodes); self.* = .{ @@ -56,8 +56,8 @@ pub fn init(node: *Node, frame: *Frame) !*ChildNodes { return self; } -pub fn deinit(self: *const ChildNodes, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *const ChildNodes, _: *Page) void { + self._arena.release(); } pub fn length(self: *ChildNodes, frame: *const Frame) !u32 { diff --git a/src/browser/webapi/collections/DOMStringList.zig b/src/browser/webapi/collections/DOMStringList.zig index f95284f66..3eca581ca 100644 --- a/src/browser/webapi/collections/DOMStringList.zig +++ b/src/browser/webapi/collections/DOMStringList.zig @@ -28,7 +28,6 @@ pub const ValueIterator = GenericIterator(Iterator, "1"); pub const EntryIterator = GenericIterator(Iterator, null); const Execution = js.Execution; -const Allocator = std.mem.Allocator; // not registered in collections.zig, because this is one of the rare // collections that's also available in Worker @@ -44,15 +43,15 @@ pub fn registerTypes() []const type { pub const DOMStringList = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _items: []const []const u8, pub fn acquireRef(self: *DOMStringList) void { self._rc.acquire(); } -pub fn deinit(self: *DOMStringList, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *DOMStringList, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *DOMStringList, page: *Page) void { diff --git a/src/browser/webapi/css/FontFace.zig b/src/browser/webapi/css/FontFace.zig index e6b5a1166..231e01fa9 100644 --- a/src/browser/webapi/css/FontFace.zig +++ b/src/browser/webapi/css/FontFace.zig @@ -16,26 +16,23 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); const Frame = @import("../../Frame.zig"); -const Allocator = std.mem.Allocator; - const FontFace = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _family: []const u8, pub fn init(family: []const u8, source: []const u8, frame: *Frame) !*FontFace { _ = source; const arena = try frame.getArena(.tiny, "FontFace"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(FontFace); self.* = .{ @@ -45,8 +42,8 @@ pub fn init(family: []const u8, source: []const u8, frame: *Frame) !*FontFace { return self; } -pub fn deinit(self: *FontFace, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *FontFace, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *FontFace, page: *Page) void { diff --git a/src/browser/webapi/css/FontFaceSet.zig b/src/browser/webapi/css/FontFaceSet.zig index 83fd40c62..203082852 100644 --- a/src/browser/webapi/css/FontFaceSet.zig +++ b/src/browser/webapi/css/FontFaceSet.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -28,28 +27,26 @@ const EventTarget = @import("../EventTarget.zig"); const FontFace = @import("FontFace.zig"); -const Allocator = std.mem.Allocator; - const FontFaceSet = @This(); pub const Proto = EventTarget; _rc: lp.RC = .{}, _proto: *EventTarget, -_arena: Allocator, +_arena: *lp.Arena, pub fn init(frame: *Frame) !*FontFaceSet { const arena = try frame.getArena(.tiny, "FontFaceSet"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); - return frame._factory.eventTargetWithAllocator(arena, FontFaceSet{ + return frame._factory.eventTargetWithAllocator(arena.allocator(), FontFaceSet{ ._proto = undefined, ._arena = arena, }); } -pub fn deinit(self: *FontFaceSet, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *FontFaceSet, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *FontFaceSet, page: *Page) void { diff --git a/src/browser/webapi/encoding/TextDecoder.zig b/src/browser/webapi/encoding/TextDecoder.zig index d73cccbf4..89969febf 100644 --- a/src/browser/webapi/encoding/TextDecoder.zig +++ b/src/browser/webapi/encoding/TextDecoder.zig @@ -24,13 +24,11 @@ const Page = @import("../../Page.zig"); const html5ever = @import("../../parser/html5ever.zig"); -const Allocator = std.mem.Allocator; - const TextDecoder = @This(); _rc: lp.RC = .{}, _fatal: bool, -_arena: Allocator, +_arena: *lp.Arena, _ignore_bom: bool, _bom_seen: bool, _decoder: ?*anyopaque, // Persistent streaming decoder @@ -58,7 +56,7 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder { } const arena = try page.getArena(.large, "TextDecoder"); - errdefer page.releaseArena(arena); + errdefer arena.release(); const opts = opts_ orelse InitOpts{}; const self = try arena.create(TextDecoder); @@ -75,11 +73,11 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder { return self; } -pub fn deinit(self: *TextDecoder, page: *Page) void { +pub fn deinit(self: *TextDecoder, _: *Page) void { if (self._decoder) |decoder| { html5ever.encoding_decoder_free(decoder); } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *TextDecoder, page: *Page) void { @@ -104,7 +102,7 @@ pub fn getEncoding(self: *TextDecoder) ![]const u8 { if (self._lowercase_name.len > 0) { return self._lowercase_name; } - self._lowercase_name = try std.ascii.allocLowerString(self._arena, self._encoding_name); + self._lowercase_name = try std.ascii.allocLowerString(self._arena.allocator(), self._encoding_name); return self._lowercase_name; } diff --git a/src/browser/webapi/event/BeforeUnloadEvent.zig b/src/browser/webapi/event/BeforeUnloadEvent.zig index 32acac2a2..1eeed333a 100644 --- a/src/browser/webapi/event/BeforeUnloadEvent.zig +++ b/src/browser/webapi/event/BeforeUnloadEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -25,7 +24,6 @@ const Frame = @import("../../Frame.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; // https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-beforeunloadevent-interface const BeforeUnloadEvent = @This(); @@ -39,18 +37,18 @@ const Options = Event.inheritOptions(BeforeUnloadEvent, struct {}); pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*BeforeUnloadEvent { const arena = try frame.getArena(.tiny, "BeforeUnloadEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*BeforeUnloadEvent { const arena = try frame.getArena(.tiny, "BeforeUnloadEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*BeforeUnloadEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*BeforeUnloadEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/CloseEvent.zig b/src/browser/webapi/event/CloseEvent.zig index da534f155..5ac111ef8 100644 --- a/src/browser/webapi/event/CloseEvent.zig +++ b/src/browser/webapi/event/CloseEvent.zig @@ -16,14 +16,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const Page = @import("../../Page.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const CloseEvent = @This(); @@ -43,18 +41,18 @@ const Options = Event.inheritOptions(CloseEvent, CloseEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*CloseEvent { const arena = try page.getArena(.tiny, "CloseEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*CloseEvent { const arena = try page.getArena(.tiny, "CloseEvent.trusted"); - errdefer page.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, page); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*CloseEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*CloseEvent { const opts = _opts orelse Options{}; const event = try page.factory.event( diff --git a/src/browser/webapi/event/CompositionEvent.zig b/src/browser/webapi/event/CompositionEvent.zig index 9db47912d..750c324c7 100644 --- a/src/browser/webapi/event/CompositionEvent.zig +++ b/src/browser/webapi/event/CompositionEvent.zig @@ -41,8 +41,8 @@ const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions); pub fn init(typ: []const u8, opts_: ?Options, frame: *Frame) !*CompositionEvent { const arena = try frame.getArena(.tiny, "CompositionEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = opts_ orelse Options{}; const event = try frame._factory.uiEvent( @@ -82,7 +82,7 @@ pub fn initCompositionEvent( const arena = event._arena; event._initialized = true; - event._type_string = try String.init(arena, typ, .{}); + event._type_string = try String.init(arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; ui._view = view; diff --git a/src/browser/webapi/event/CookieChangeEvent.zig b/src/browser/webapi/event/CookieChangeEvent.zig index 6714a705d..9ef0781d7 100644 --- a/src/browser/webapi/event/CookieChangeEvent.zig +++ b/src/browser/webapi/event/CookieChangeEvent.zig @@ -47,8 +47,8 @@ const Options = Event.inheritOptions(CookieChangeEvent, CookieChangeEventOptions pub fn init(typ: []const u8, _opts: ?Options, exec: *const Execution) !*CookieChangeEvent { const arena = try exec.getArena(.tiny, "CookieChangeEvent"); - errdefer exec.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; @@ -57,8 +57,8 @@ pub fn init(typ: []const u8, _opts: ?Options, exec: *const Execution) !*CookieCh type_string, CookieChangeEvent{ ._proto = undefined, - ._changed = try cloneListItems(arena, opts.changed), - ._deleted = try cloneListItems(arena, opts.deleted), + ._changed = try cloneListItems(arena.allocator(), opts.changed), + ._deleted = try cloneListItems(arena.allocator(), opts.deleted), }, ); @@ -75,20 +75,20 @@ pub fn initSingle( exec: *const Execution, ) !*CookieChangeEvent { const arena = try exec.getArena(.tiny, "CookieChangeEvent"); - errdefer exec.releaseArena(arena); - const type_string = try String.init(arena, "change", .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), "change", .{}); const item = try arena.create(CookieStore.CookieListItem); item.* = .{ - .name = try String.init(arena, snapshot.name, .{}), + .name = try String.init(arena.allocator(), snapshot.name, .{}), // Deletions report no value (the `deleted` accessor serializes the // resulting null as undefined); changes carry the new value. - .value = if (kind == .deleted) null else try String.init(arena, snapshot.value, .{}), + .value = if (kind == .deleted) null else try String.init(arena.allocator(), snapshot.value, .{}), .domain = if (snapshot.domain.len > 0 and snapshot.domain[0] == '.') - try String.init(arena, snapshot.domain[1..], .{}) + try String.init(arena.allocator(), snapshot.domain[1..], .{}) else null, - .path = try String.init(arena, snapshot.path, .{}), + .path = try String.init(arena.allocator(), snapshot.path, .{}), .expires = null, .secure = snapshot.secure, .sameSite = switch (snapshot.same_site) { diff --git a/src/browser/webapi/event/CustomEvent.zig b/src/browser/webapi/event/CustomEvent.zig index c901ac2fe..ece3b1362 100644 --- a/src/browser/webapi/event/CustomEvent.zig +++ b/src/browser/webapi/event/CustomEvent.zig @@ -43,15 +43,15 @@ const Options = Event.inheritOptions(CustomEvent, CustomEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent { const arena = try page.getArena(.tiny, "CustomEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = opts_ orelse Options{}; const event = try page.factory.event( arena, type_string, CustomEvent{ - ._arena = arena, + ._arena = arena.allocator(), ._proto = undefined, ._detail = opts.detail, }, @@ -76,7 +76,7 @@ pub fn initCustomEvent( // This function can only be called after the constructor has called. // So we assume proto is initialized already by constructor. self._proto._initialized = true; - self._proto._type_string = try String.init(self._proto._arena, event_string, .{}); + self._proto._type_string = try String.init(self._proto._arena.allocator(), event_string, .{}); self._proto._bubbles = bubbles orelse false; self._proto._cancelable = cancelable orelse false; // Detail is stored separately. diff --git a/src/browser/webapi/event/DeviceMotionEvent.zig b/src/browser/webapi/event/DeviceMotionEvent.zig index 910a9a00c..36c19a4cd 100644 --- a/src/browser/webapi/event/DeviceMotionEvent.zig +++ b/src/browser/webapi/event/DeviceMotionEvent.zig @@ -41,8 +41,8 @@ const Options = Event.inheritOptions(DeviceMotionEvent, DeviceMotionEventOptions pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*DeviceMotionEvent { const arena = try frame.getArena(.tiny, "DeviceMotionEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/DeviceOrientationEvent.zig b/src/browser/webapi/event/DeviceOrientationEvent.zig index 627a93592..8cf291946 100644 --- a/src/browser/webapi/event/DeviceOrientationEvent.zig +++ b/src/browser/webapi/event/DeviceOrientationEvent.zig @@ -47,8 +47,8 @@ const Options = Event.inheritOptions(DeviceOrientationEvent, DeviceOrientationEv pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*DeviceOrientationEvent { const arena = try frame.getArena(.tiny, "DeviceOrientationEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/DragEvent.zig b/src/browser/webapi/event/DragEvent.zig index b04eb2587..a614d4704 100644 --- a/src/browser/webapi/event/DragEvent.zig +++ b/src/browser/webapi/event/DragEvent.zig @@ -51,8 +51,8 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, frame: *Frame) !*DragEvent fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, frame: *Frame) !*DragEvent { const arena = try frame.getArena(.medium, "DragEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; diff --git a/src/browser/webapi/event/ErrorEvent.zig b/src/browser/webapi/event/ErrorEvent.zig index 9378ac1ad..1a840b300 100644 --- a/src/browser/webapi/event/ErrorEvent.zig +++ b/src/browser/webapi/event/ErrorEvent.zig @@ -51,25 +51,25 @@ const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent { const arena = try page.getArena(.small, "ErrorEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts_, false, page); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*ErrorEvent { const arena = try page.getArena(.small, "ErrorEvent.trusted"); - errdefer page.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, opts_, true, page); } -fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent { const opts = opts_ orelse Options{}; const event = try page.factory.event( arena, typ, ErrorEvent{ - ._arena = arena, + ._arena = arena.allocator(), ._proto = undefined, ._message = if (opts.message) |str| try arena.dupe(u8, str) else "", ._filename = if (opts.filename) |str| try arena.dupe(u8, str) else "", diff --git a/src/browser/webapi/event/FocusEvent.zig b/src/browser/webapi/event/FocusEvent.zig index e202a1981..4f2deff03 100644 --- a/src/browser/webapi/event/FocusEvent.zig +++ b/src/browser/webapi/event/FocusEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -27,7 +26,6 @@ const EventTarget = @import("../EventTarget.zig"); const UIEvent = @import("UIEvent.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const FocusEvent = @This(); @@ -47,18 +45,18 @@ pub const Options = Event.inheritOptions( pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*FocusEvent { const arena = try frame.getArena(.tiny, "FocusEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*FocusEvent { const arena = try frame.getArena(.tiny, "FocusEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*FocusEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*FocusEvent { const opts = _opts orelse Options{}; const event = try frame._factory.uiEvent( diff --git a/src/browser/webapi/event/FormDataEvent.zig b/src/browser/webapi/event/FormDataEvent.zig index 8a87041c6..93e37b995 100644 --- a/src/browser/webapi/event/FormDataEvent.zig +++ b/src/browser/webapi/event/FormDataEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -27,7 +26,6 @@ const Event = @import("../Event.zig"); const FormData = @import("../net/FormData.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; /// https://developer.mozilla.org/en-US/docs/Web/API/FormDataEvent const FormDataEvent = @This(); @@ -43,18 +41,18 @@ const Options = Event.inheritOptions(FormDataEvent, struct { pub fn init(typ: []const u8, maybe_options: Options, frame: *Frame) !*FormDataEvent { const arena = try frame.getArena(.tiny, "FormDataEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, maybe_options, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*FormDataEvent { const arena = try frame.getArena(.tiny, "FormDataEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, maybe_options: ?Options, trusted: bool, frame: *Frame) !*FormDataEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, maybe_options: ?Options, trusted: bool, frame: *Frame) !*FormDataEvent { const options = maybe_options orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/GamepadEvent.zig b/src/browser/webapi/event/GamepadEvent.zig index b3af82039..86919772d 100644 --- a/src/browser/webapi/event/GamepadEvent.zig +++ b/src/browser/webapi/event/GamepadEvent.zig @@ -38,8 +38,8 @@ const Options = Event.inheritOptions(GamepadEvent, GamepadEventOptions); pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*GamepadEvent { const arena = try frame.getArena(.tiny, "GamepadEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/HashChangeEvent.zig b/src/browser/webapi/event/HashChangeEvent.zig index 2226fd859..b308948d1 100644 --- a/src/browser/webapi/event/HashChangeEvent.zig +++ b/src/browser/webapi/event/HashChangeEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -25,7 +24,6 @@ const Frame = @import("../../Frame.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; // https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent const HashChangeEvent = @This(); @@ -45,18 +43,18 @@ const Options = Event.inheritOptions(HashChangeEvent, HashChangeEventOptions); pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*HashChangeEvent { const arena = try frame.getArena(.tiny, "HashChangeEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*HashChangeEvent { const arena = try frame.getArena(.tiny, "HashChangeEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*HashChangeEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*HashChangeEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/InputEvent.zig b/src/browser/webapi/event/InputEvent.zig index a4d181023..5a48b3733 100644 --- a/src/browser/webapi/event/InputEvent.zig +++ b/src/browser/webapi/event/InputEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -28,7 +27,6 @@ const UIEvent = @import("UIEvent.zig"); const DataTransfer = @import("../DataTransfer.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const InputEvent = @This(); @@ -54,18 +52,18 @@ const Options = Event.inheritOptions( pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*InputEvent { const arena = try frame.getArena(.tiny, "InputEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*InputEvent { const arena = try frame.getArena(.tiny, "InputEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*InputEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*InputEvent { const opts = _opts orelse Options{}; const event = try frame._factory.uiEvent( diff --git a/src/browser/webapi/event/KeyboardEvent.zig b/src/browser/webapi/event/KeyboardEvent.zig index c55847b7d..50bbe5bf8 100644 --- a/src/browser/webapi/event/KeyboardEvent.zig +++ b/src/browser/webapi/event/KeyboardEvent.zig @@ -26,7 +26,6 @@ const Event = @import("../Event.zig"); const UIEvent = @import("UIEvent.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const KeyboardEvent = @This(); @@ -297,18 +296,18 @@ const Options = Event.inheritOptions( pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*KeyboardEvent { const arena = try frame.getArena(.tiny, "KeyboardEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*KeyboardEvent { const arena = try frame.getArena(.tiny, "KeyboardEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*KeyboardEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*KeyboardEvent { const opts = _opts orelse Options{}; const event = try frame._factory.uiEvent( @@ -316,7 +315,7 @@ fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool typ, KeyboardEvent{ ._proto = undefined, - ._key = try Key.fromString(arena, opts.key), + ._key = try Key.fromString(arena.allocator(), opts.key), ._location = opts.location, ._code = if (opts.code) |c| try arena.dupe(u8, c) else "", ._repeat = opts.repeat, @@ -431,11 +430,11 @@ pub fn initKeyboardEvent( const arena = event._arena; event._initialized = true; - event._type_string = try String.init(arena, typ, .{}); + event._type_string = try String.init(arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; ui._view = view; - self._key = try Key.fromString(arena, key orelse ""); + self._key = try Key.fromString(arena.allocator(), key orelse ""); self._location = location orelse 0; self._ctrl_key = ctrl_key orelse false; self._alt_key = alt_key orelse false; @@ -444,7 +443,7 @@ pub fn initKeyboardEvent( } pub fn getModifierState(self: *const KeyboardEvent, str: []const u8) !bool { - const key = try Key.fromString(self._proto._proto._arena, str); + const key = try Key.fromString(self._proto._proto._arena.allocator(), str); switch (key) { .Alt, .AltGraph => return self._alt_key, diff --git a/src/browser/webapi/event/MessageEvent.zig b/src/browser/webapi/event/MessageEvent.zig index 24394f6e2..9b987906e 100644 --- a/src/browser/webapi/event/MessageEvent.zig +++ b/src/browser/webapi/event/MessageEvent.zig @@ -27,7 +27,6 @@ const MessagePort = @import("../MessagePort.zig"); const Window = @import("../Window.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; const MessageEvent = @This(); @@ -65,18 +64,18 @@ const Options = Event.inheritOptions(MessageEvent, MessageEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent { const arena = try page.getArena(.small, "MessageEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts_, false, page); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*MessageEvent { const arena = try page.getArena(.small, "MessageEvent.trusted"); - errdefer page.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, opts_, true, page); } -fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent { const opts = opts_ orelse Options{}; const event = try page.factory.event( diff --git a/src/browser/webapi/event/MouseEvent.zig b/src/browser/webapi/event/MouseEvent.zig index 02c1a3d8e..b7da253b8 100644 --- a/src/browser/webapi/event/MouseEvent.zig +++ b/src/browser/webapi/event/MouseEvent.zig @@ -29,7 +29,6 @@ const UIEvent = @import("UIEvent.zig"); const PointerEvent = @import("PointerEvent.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const MouseEvent = @This(); @@ -89,18 +88,18 @@ pub const Options = Event.inheritOptions( pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*MouseEvent { const arena = try frame.getArena(.tiny, "MouseEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*MouseEvent { const arena = try frame.getArena(.tiny, "MouseEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*MouseEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*MouseEvent { const opts = _opts orelse Options{}; const event = try frame._factory.uiEvent( @@ -242,7 +241,7 @@ pub fn initMouseEvent( } event._initialized = true; - event._type_string = try String.init(event._arena, typ, .{}); + event._type_string = try String.init(event._arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; ui._view = view; diff --git a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig index 2514f6237..4a8c478c2 100644 --- a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig +++ b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig @@ -27,7 +27,6 @@ const NavigationHistoryEntry = @import("../navigation/NavigationHistoryEntry.zig const NavigationType = @import("../navigation/root.zig").NavigationType; const String = lp.String; -const Allocator = std.mem.Allocator; const NavigationCurrentEntryChangeEvent = @This(); @@ -49,19 +48,19 @@ const Options = Event.inheritOptions( pub fn init(typ: []const u8, opts: Options, frame: *Frame) !*NavigationCurrentEntryChangeEvent { const arena = try frame.getArena(.tiny, "NavigationCurrentEntryChangeEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts, false, frame); } pub fn initTrusted(typ: String, opts: Options, frame: *Frame) !*NavigationCurrentEntryChangeEvent { const arena = try frame.getArena(.tiny, "NavigationCurrentEntryChangeEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, opts, true, frame); } fn initWithTrusted( - arena: Allocator, + arena: *lp.Arena, typ: String, opts: Options, trusted: bool, diff --git a/src/browser/webapi/event/PageTransitionEvent.zig b/src/browser/webapi/event/PageTransitionEvent.zig index 0b991ed2d..636b3c7a6 100644 --- a/src/browser/webapi/event/PageTransitionEvent.zig +++ b/src/browser/webapi/event/PageTransitionEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -25,7 +24,6 @@ const Frame = @import("../../Frame.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; // https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent const PageTransitionEvent = @This(); @@ -43,18 +41,18 @@ const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOpt pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*PageTransitionEvent { const arena = try frame.getArena(.tiny, "PageTransitionEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*PageTransitionEvent { const arena = try frame.getArena(.tiny, "PageTransitionEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*PageTransitionEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*PageTransitionEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/PointerEvent.zig b/src/browser/webapi/event/PointerEvent.zig index cefc6da6b..9a35ee1a8 100644 --- a/src/browser/webapi/event/PointerEvent.zig +++ b/src/browser/webapi/event/PointerEvent.zig @@ -97,8 +97,8 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, frame: *Frame) !*PointerEve fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, frame: *Frame) !*PointerEvent { const arena = try frame.getArena(.tiny, "PointerEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.mouseEvent( diff --git a/src/browser/webapi/event/PopStateEvent.zig b/src/browser/webapi/event/PopStateEvent.zig index cc5b91e83..c99d2320b 100644 --- a/src/browser/webapi/event/PopStateEvent.zig +++ b/src/browser/webapi/event/PopStateEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -25,7 +24,6 @@ const Frame = @import("../../Frame.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; // https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent const PopStateEvent = @This(); @@ -43,18 +41,18 @@ const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions); pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*PopStateEvent { const arena = try frame.getArena(.tiny, "PopStateEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*PopStateEvent { const arena = try frame.getArena(.tiny, "PopStateEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*PopStateEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*PopStateEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/ProgressEvent.zig b/src/browser/webapi/event/ProgressEvent.zig index a27e25db0..f051ba936 100644 --- a/src/browser/webapi/event/ProgressEvent.zig +++ b/src/browser/webapi/event/ProgressEvent.zig @@ -16,14 +16,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const Page = @import("../../Page.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const ProgressEvent = @This(); @@ -43,18 +41,18 @@ const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent { const arena = try page.getArena(.tiny, "ProgressEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*ProgressEvent { const arena = try page.getArena(.tiny, "ProgressEvent.trusted"); - errdefer page.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, page); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent { const opts = _opts orelse Options{}; const event = try page.factory.event( diff --git a/src/browser/webapi/event/PromiseRejectionEvent.zig b/src/browser/webapi/event/PromiseRejectionEvent.zig index 658249f5b..de6f856b4 100644 --- a/src/browser/webapi/event/PromiseRejectionEvent.zig +++ b/src/browser/webapi/event/PromiseRejectionEvent.zig @@ -41,8 +41,8 @@ const Options = Event.inheritOptions(PromiseRejectionEvent, PromiseRejectionEven pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*PromiseRejectionEvent { const arena = try page.getArena(.tiny, "PromiseRejectionEvent"); - errdefer page.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = opts_ orelse Options{}; const event = try page.factory.event( diff --git a/src/browser/webapi/event/StorageEvent.zig b/src/browser/webapi/event/StorageEvent.zig index 345db5fde..c25f8084c 100644 --- a/src/browser/webapi/event/StorageEvent.zig +++ b/src/browser/webapi/event/StorageEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -25,7 +24,6 @@ const Frame = @import("../../Frame.zig"); const Event = @import("../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; // https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface const StorageEvent = @This(); @@ -49,18 +47,18 @@ const Options = Event.inheritOptions(StorageEvent, StorageEventOptions); pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*StorageEvent { const arena = try frame.getArena(.tiny, "StorageEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, _opts, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*StorageEvent { const arena = try frame.getArena(.tiny, "StorageEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*StorageEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*StorageEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( @@ -121,7 +119,7 @@ pub fn initStorageEvent( const arena = event._arena; event._initialized = true; - event._type_string = try String.init(arena, typ, .{}); + event._type_string = try String.init(arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; self._key = if (key) |k| try arena.dupe(u8, k) else null; diff --git a/src/browser/webapi/event/SubmitEvent.zig b/src/browser/webapi/event/SubmitEvent.zig index 559f88360..94591985f 100644 --- a/src/browser/webapi/event/SubmitEvent.zig +++ b/src/browser/webapi/event/SubmitEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -26,7 +25,6 @@ const Event = @import("../Event.zig"); const HtmlElement = @import("../element/Html.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; /// https://developer.mozilla.org/en-US/docs/Web/API/SubmitEvent const SubmitEvent = @This(); @@ -44,18 +42,18 @@ const Options = Event.inheritOptions(SubmitEvent, SubmitEventOptions); pub fn init(typ: []const u8, opts_: ?Options, frame: *Frame) !*SubmitEvent { const arena = try frame.getArena(.tiny, "SubmitEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts_, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*SubmitEvent { const arena = try frame.getArena(.tiny, "SubmitEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*SubmitEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*SubmitEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/TextEvent.zig b/src/browser/webapi/event/TextEvent.zig index 96c549213..3bb8ba87e 100644 --- a/src/browser/webapi/event/TextEvent.zig +++ b/src/browser/webapi/event/TextEvent.zig @@ -44,8 +44,8 @@ pub const Options = Event.inheritOptions( pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*TextEvent { const arena = try frame.getArena(.tiny, "TextEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; @@ -86,7 +86,7 @@ pub fn initTextEvent( const arena = event._arena; event._initialized = true; - event._type_string = try String.init(arena, typ, .{}); + event._type_string = try String.init(arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; ui._view = view; diff --git a/src/browser/webapi/event/ToggleEvent.zig b/src/browser/webapi/event/ToggleEvent.zig index 5067d375d..b04c67653 100644 --- a/src/browser/webapi/event/ToggleEvent.zig +++ b/src/browser/webapi/event/ToggleEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); @@ -26,7 +25,6 @@ const Event = @import("../Event.zig"); const HtmlElement = @import("../element/Html.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; /// https://html.spec.whatwg.org/multipage/popover.html#toggleevent const ToggleEvent = @This(); @@ -48,18 +46,18 @@ const Options = Event.inheritOptions(ToggleEvent, ToggleEventOptions); pub fn init(typ: []const u8, opts_: ?Options, frame: *Frame) !*ToggleEvent { const arena = try frame.getArena(.tiny, "ToggleEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts_, false, frame); } pub fn initTrusted(typ: String, _opts: ?Options, frame: *Frame) !*ToggleEvent { const arena = try frame.getArena(.tiny, "ToggleEvent.trusted"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, _opts, true, frame); } -fn initWithTrusted(arena: Allocator, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*ToggleEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, _opts: ?Options, trusted: bool, frame: *Frame) !*ToggleEvent { const opts = _opts orelse Options{}; const event = try frame._factory.event( diff --git a/src/browser/webapi/event/TouchEvent.zig b/src/browser/webapi/event/TouchEvent.zig index e1b2c5fa2..5ce6b8c74 100644 --- a/src/browser/webapi/event/TouchEvent.zig +++ b/src/browser/webapi/event/TouchEvent.zig @@ -60,8 +60,8 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, frame: *Frame) !*TouchEvent fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, frame: *Frame) !*TouchEvent { const arena = try frame.getArena(.tiny, "TouchEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.uiEvent( diff --git a/src/browser/webapi/event/UIEvent.zig b/src/browser/webapi/event/UIEvent.zig index a44766a43..8ee1128d9 100644 --- a/src/browser/webapi/event/UIEvent.zig +++ b/src/browser/webapi/event/UIEvent.zig @@ -58,8 +58,8 @@ pub const Options = Event.inheritOptions( pub fn init(typ: []const u8, _opts: ?Options, frame: *Frame) !*UIEvent { const arena = try frame.getArena(.tiny, "UIEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; const event = try frame._factory.event( @@ -151,7 +151,7 @@ pub fn initUIEvent( } event._initialized = true; - event._type_string = try String.init(event._arena, typ, .{}); + event._type_string = try String.init(event._arena.allocator(), typ, .{}); event._bubbles = bubbles orelse false; event._cancelable = cancelable orelse false; self._view = view; diff --git a/src/browser/webapi/event/WheelEvent.zig b/src/browser/webapi/event/WheelEvent.zig index 6917969ef..a17e7094b 100644 --- a/src/browser/webapi/event/WheelEvent.zig +++ b/src/browser/webapi/event/WheelEvent.zig @@ -62,8 +62,8 @@ pub fn initTrusted(typ: []const u8, _opts: ?Options, frame: *Frame) !*WheelEvent fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, frame: *Frame) !*WheelEvent { const arena = try frame.getArena(.medium, "WheelEvent"); - errdefer frame.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); const opts = _opts orelse Options{}; diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index 82649aac3..8c96b5418 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -213,9 +213,9 @@ pub fn pushEntry( const id = self._next_entry_id; self._next_entry_id += 1; - const id_str = try std.fmt.allocPrint(arena, "{d}", .{id}); + const id_str = try std.fmt.allocPrint(arena.allocator(), "{d}", .{id}); - const entry = try Factory.chainedWithAllocator(arena, .{ + const entry = try Factory.chainedWithAllocator(arena.allocator(), .{ EventTarget{ ._type = undefined }, NavigationHistoryEntry{ ._proto = undefined, @@ -229,7 +229,7 @@ pub fn pushEntry( // we don't always have a current entry... const previous = if (self._entries.items.len > 0) self.getCurrentEntry() else null; - try self._entries.append(arena, entry); + try self._entries.append(arena.allocator(), entry); self._index = index; if (previous != null and should_dispatch) { @@ -260,9 +260,9 @@ pub fn replaceEntry( const id = self._next_entry_id; self._next_entry_id += 1; - const id_str = try std.fmt.allocPrint(arena, "{d}", .{id}); + const id_str = try std.fmt.allocPrint(arena.allocator(), "{d}", .{id}); - const entry = try Factory.chainedWithAllocator(arena, .{ + const entry = try Factory.chainedWithAllocator(arena.allocator(), .{ EventTarget{ ._type = undefined }, NavigationHistoryEntry{ ._proto = undefined, @@ -320,7 +320,7 @@ pub fn navigateInner( const committed = local.createPromiseResolver(); const finished = local.createPromiseResolver(); - var new_url = try URL.resolve(arena, frame.url, url, .{}); + var new_url = try URL.resolve(arena.allocator(), frame.url, url, .{}); const is_same_document = URL.eqlDocument(new_url, frame.url); // In case of navigation to the same document, we force an url duplication. @@ -406,7 +406,7 @@ pub fn navigateInner( pub fn navigate(self: *Navigation, _url: [:0]const u8, _opts: ?NavigateOptions, frame: *Frame) !NavigationReturn { const arena = frame._session.arena; const opts = _opts orelse NavigateOptions{}; - const json = if (opts.state) |state| state.toJson(arena) catch return error.DataClone else null; + const json = if (opts.state) |state| state.toJson(arena.allocator()) catch return error.DataClone else null; const kind: NavigationKind = if (opts.history) |history| if (std.mem.eql(u8, "replace", history)) .{ .replace = json } else .{ .push = json } @@ -469,7 +469,7 @@ pub fn updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions, const previous = self.getCurrentEntry(); self.getCurrentEntry()._state = .{ .source = .navigation, - .value = options.state.toJson(arena) catch return error.DataClone, + .value = options.state.toJson(arena.allocator()) catch return error.DataClone, }; if (self._on_currententrychange) |cec| { diff --git a/src/browser/webapi/net/EventSource.zig b/src/browser/webapi/net/EventSource.zig index 45845960d..cfdb6c172 100644 --- a/src/browser/webapi/net/EventSource.zig +++ b/src/browser/webapi/net/EventSource.zig @@ -33,7 +33,6 @@ const MessageEvent = @import("../event/MessageEvent.zig"); const log = lp.log; const String = lp.String; const Execution = js.Execution; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; // https://html.spec.whatwg.org/multipage/server-sent-events.html @@ -44,7 +43,7 @@ pub const Proto = EventTarget; _rc: lp.RC = .{}, _exec: *const Execution, _proto: *EventTarget, -_arena: Allocator, +_arena: *lp.Arena, _url: [:0]const u8, _with_credentials: bool = false, @@ -96,13 +95,13 @@ const Opts = struct { pub fn init(url: []const u8, opts_: ?Opts, exec: *const Execution) !*EventSource { const arena = try exec.getArena(.medium, "EventSource"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); - const resolved = URL.resolve(arena, exec.base(), url, .{ .encoding = exec.charset.* }) catch { + const resolved = URL.resolve(arena.allocator(), exec.base(), url, .{ .encoding = exec.charset.* }) catch { return error.SyntaxError; }; - const self = try exec._factory.eventTargetWithAllocator(arena, EventSource{ + const self = try exec._factory.eventTargetWithAllocator(arena.allocator(), EventSource{ ._exec = exec, ._arena = arena, ._proto = undefined, @@ -130,7 +129,7 @@ pub fn init(url: []const u8, opts_: ?Opts, exec: *const Execution) !*EventSource return self; } -pub fn deinit(self: *EventSource, page: *Page) void { +pub fn deinit(self: *EventSource, _: *Page) void { self._ready_state = .closed; if (self._transfer) |transfer| { self._transfer = null; @@ -147,7 +146,7 @@ pub fn deinit(self: *EventSource, page: *Page) void { func.release(); } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *EventSource, page: *Page) void { @@ -174,7 +173,7 @@ fn connect(self: *EventSource) !void { self._event_type_buf.clearRetainingCapacity(); self._id_buf.clearRetainingCapacity(); - try self._id_buf.appendSlice(self._arena, self._last_event_id.items); + try self._id_buf.appendSlice(self._arena.allocator(), self._last_event_id.items); var headers = try http_client.newHeaders(); try headers.add("Accept: text/event-stream"); @@ -344,7 +343,7 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { defer ls.deinit(); const final_url = try self._arena.dupeZ(u8, transfer.req.url); - self._event_origin = (URL.getOrigin(self._arena, final_url) catch null) orelse ""; + self._event_origin = (URL.getOrigin(self._arena.allocator(), final_url) catch null) orelse ""; // https://html.spec.whatwg.org/multipage/server-sent-events.html#announce-the-connection self._ready_state = .open; @@ -463,7 +462,7 @@ fn bufferLine(self: *EventSource, bytes: []const u8) !void { if (self._line_buf.items.len + bytes.len > self._max_response_size) { return error.ResponseTooLarge; } - return self._line_buf.appendSlice(self._arena, bytes); + return self._line_buf.appendSlice(self._arena.allocator(), bytes); } fn processLine(self: *EventSource) !void { @@ -501,20 +500,20 @@ fn processLine(self: *EventSource) !void { if (self._data_buf.items.len + add_len > self._max_response_size) { return error.ResponseTooLarge; } - try self._data_buf.ensureUnusedCapacity(arena, add_len); + try self._data_buf.ensureUnusedCapacity(arena.allocator(), add_len); self._data_buf.appendSliceAssumeCapacity(value); self._data_buf.appendAssumeCapacity('\n'); } if (std.mem.eql(u8, field, "event")) { self._event_type_buf.clearRetainingCapacity(); - try self._event_type_buf.appendSlice(arena, value); + try self._event_type_buf.appendSlice(arena.allocator(), value); } if (std.mem.eql(u8, field, "id")) { if (std.mem.indexOfScalar(u8, value, 0) == null) { self._id_buf.clearRetainingCapacity(); - try self._id_buf.appendSlice(arena, value); + try self._id_buf.appendSlice(arena.allocator(), value); } } @@ -529,7 +528,7 @@ fn processLine(self: *EventSource) !void { // An empty line completed an event block. fn dispatchPending(self: *EventSource) !void { self._last_event_id.clearRetainingCapacity(); - try self._last_event_id.appendSlice(self._arena, self._id_buf.items); + try self._last_event_id.appendSlice(self._arena.allocator(), self._id_buf.items); const data = self._data_buf.items; if (data.len == 0) { diff --git a/src/browser/webapi/net/Fetch.zig b/src/browser/webapi/net/Fetch.zig index e17ad16aa..fc16c3882 100644 --- a/src/browser/webapi/net/Fetch.zig +++ b/src/browser/webapi/net/Fetch.zig @@ -152,7 +152,7 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { const arena = self._response._arena; if (transfer.getContentLength()) |cl| { - try self._buf.ensureTotalCapacity(arena, cl); + try self._buf.ensureTotalCapacity(arena.allocator(), cl); } const res = self._response; @@ -183,8 +183,8 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { // Determine response type based on origin comparison const exec = self._exec; - const requesting_origin = URL.getOrigin(arena, exec.url.*) catch null; - const response_origin = URL.getOrigin(arena, res._url) catch null; + const requesting_origin = URL.getOrigin(arena.allocator(), exec.url.*) catch null; + const response_origin = URL.getOrigin(arena.allocator(), res._url) catch null; if (requesting_origin) |fo| { if (response_origin) |ro| { @@ -218,7 +218,7 @@ fn httpDataCallback(transfer: *Transfer, data: []const u8) !void { } } - try self._buf.appendSlice(self._response._arena, data); + try self._buf.appendSlice(self._response._arena.allocator(), data); } fn httpDoneCallback(ctx: *anyopaque) !void { diff --git a/src/browser/webapi/net/FormData.zig b/src/browser/webapi/net/FormData.zig index f7207f976..892abbbce 100644 --- a/src/browser/webapi/net/FormData.zig +++ b/src/browser/webapi/net/FormData.zig @@ -40,7 +40,7 @@ const FormData = @This(); _rc: lp.RC, -_arena: Allocator, +_arena: *lp.Arena, _entries: std.ArrayList(Entry), pub const Entry = struct { @@ -77,7 +77,7 @@ pub const Entry = struct { pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormData { const arena = try exec.getArena(.small, "FormData"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const form_data = try arena.create(FormData); form_data.* = .{ @@ -101,7 +101,7 @@ pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormD form._constructing_entry_list = true; defer form._constructing_entry_list = false; - form_data._entries = try collectForm(arena, form, submitter, frame); + form_data._entries = try collectForm(arena.allocator(), form, submitter, frame); // Hold a reference on each entry's File for the FormData's lifetime; released // in deinit. @@ -126,7 +126,7 @@ pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormD // application/x-www-form-urlencoded body back into a FormData. pub fn initFromUrlEncoded(bytes: []const u8, exec: *const Execution) !*FormData { const arena = try exec.getArena(.small, "FormData"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const form_data = try arena.create(FormData); form_data.* = .{ @@ -142,7 +142,7 @@ pub fn initFromUrlEncoded(bytes: []const u8, exec: *const Execution) !*FormData // body back into a FormData. `boundary` is the Content-Type boundary param. pub fn initFromMultipart(bytes: []const u8, boundary: []const u8, exec: *const Execution) !*FormData { const arena = try exec.getArena(.small, "FormData"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const form_data = try arena.create(FormData); form_data.* = .{ @@ -170,7 +170,7 @@ pub fn deinit(self: *FormData, page: *Page) void { } } // Frees the entry list and this FormData itself; do not touch self afterwards. - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *FormData, page: *Page) void { @@ -238,11 +238,11 @@ pub fn append(self: *FormData, name: []const u8, value: EntryValue, filename: ?[ // A Blob that is not a File becomes a File named "blob". break :blk .{ .file = try fileFrom(blob, "blob", exec.page) }; }, - .bytes => |b| .{ .string = try String.init(self._arena, b, .{}) }, + .bytes => |b| .{ .string = try String.init(self._arena.allocator(), b, .{}) }, }; - try self._entries.append(self._arena, .{ - .name = try String.init(self._arena, name, .{}), + try self._entries.append(self._arena.allocator(), .{ + .name = try String.init(self._arena.allocator(), name, .{}), .value = entry_value, }); } @@ -252,9 +252,9 @@ pub fn append(self: *FormData, name: []const u8, value: EntryValue, filename: ?[ // the entry owns that reference and deleteByName releases it. fn fileFrom(source: *Blob, name: []const u8, page: *Page) !*File { const arena = try page.getArena(source._slice.len + source._mime.len + 256, "Blob"); - errdefer page.releaseArena(arena); + errdefer arena.release(); - const file = try Factory.chainedWithAllocator(arena, .{ + const file = try Factory.chainedWithAllocator(arena.allocator(), .{ try Blob.buildValueFromBytes(arena, source._slice, source._mime), File{ ._proto = undefined, @@ -268,9 +268,9 @@ fn fileFrom(source: *Blob, name: []const u8, page: *Page) !*File { } pub fn appendText(self: *FormData, name: []const u8, value: []const u8) !void { - try self._entries.append(self._arena, .{ - .name = try String.init(self._arena, name, .{}), - .value = .{ .string = try String.init(self._arena, value, .{}) }, + try self._entries.append(self._arena.allocator(), .{ + .name = try String.init(self._arena.allocator(), name, .{}), + .value = .{ .string = try String.init(self._arena.allocator(), value, .{}) }, }); } @@ -444,11 +444,11 @@ pub fn parseUrlEncoded(self: *FormData, bytes: []const u8) !void { } if (std.mem.indexOfScalar(u8, pair, '=')) |idx| { try self.appendText( - try urlDecode(self._arena, pair[0..idx]), - try urlDecode(self._arena, pair[idx + 1 ..]), + try urlDecode(self._arena.allocator(), pair[0..idx]), + try urlDecode(self._arena.allocator(), pair[idx + 1 ..]), ); } else { - const key = try urlDecode(self._arena, pair); + const key = try urlDecode(self._arena.allocator(), pair); // Insert with empty value. try self.appendText(key, ""); } @@ -605,7 +605,7 @@ fn parseMultipart(self: *FormData, page: *Page, bytes: []const u8, boundary: []c } const parsed = disposition orelse return error.InvalidFormData; - const name = try decodeMultipartName(self._arena, parsed.name orelse return error.InvalidFormData); + const name = try decodeMultipartName(self._arena.allocator(), parsed.name orelse return error.InvalidFormData); const content_end = indexOfBoundary(cursor.remaining(), boundary) orelse return error.InvalidFormData; const content = cursor.remaining()[0..content_end]; @@ -619,14 +619,14 @@ fn parseMultipart(self: *FormData, page: *Page, bytes: []const u8, boundary: []c const file = try blob._arena.create(File); file.* = .{ ._proto = blob, - ._name = try blob._arena.dupe(u8, try decodeMultipartName(self._arena, filename)), + ._name = try blob._arena.dupe(u8, try decodeMultipartName(self._arena.allocator(), filename)), ._last_modified = @intCast(lp.datetime.milliTimestamp(.real)), }; blob._type = .{ .file = file }; file.acquireRef(); - try self._entries.append(self._arena, .{ - .name = try String.init(self._arena, name, .{}), + try self._entries.append(self._arena.allocator(), .{ + .name = try String.init(self._arena.allocator(), name, .{}), .value = .{ .file = file }, }); } else { @@ -875,10 +875,12 @@ test "WebApi: FormData" { test "FormData: multipart write" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.appendText("name", "John"); @@ -904,10 +906,12 @@ test "FormData: multipart write" { test "FormData: multipart escapes name CR/LF/quote" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.appendText("a\"b\r\nc", "v"); @@ -929,10 +933,12 @@ test "FormData: multipart escapes name CR/LF/quote" { test "FormData: multipart empty body" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; @@ -947,7 +953,7 @@ test "FormData: multipart empty body" { fn buildTestFile(arena: Allocator, page: *@import("../../Page.zig"), name: []const u8, mime: []const u8, body: []const u8) !*File { const blob_arena = try page.getArena(body.len + mime.len + 256, "Blob"); - const file = try Factory.chainedWithAllocator(blob_arena, .{ + const file = try Factory.chainedWithAllocator(blob_arena.allocator(), .{ try Blob.buildValueFromBytes(blob_arena, body, mime), File{ ._proto = undefined, @@ -962,6 +968,8 @@ fn buildTestFile(arena: Allocator, page: *@import("../../Page.zig"), name: []con test "FormData: multipart with file" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); @@ -970,7 +978,7 @@ test "FormData: multipart with file" { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.appendText("field", "value"); @@ -1000,6 +1008,8 @@ test "FormData: multipart with file" { test "FormData: multipart with empty file defaults to octet-stream" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); @@ -1008,7 +1018,7 @@ test "FormData: multipart with empty file defaults to octet-stream" { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd._entries.append(allocator, .{ @@ -1034,6 +1044,8 @@ test "FormData: multipart with empty file defaults to octet-stream" { test "FormData: multipart escapes file name and filename" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); @@ -1042,7 +1054,7 @@ test "FormData: multipart escapes file name and filename" { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd._entries.append(allocator, .{ @@ -1068,6 +1080,8 @@ test "FormData: multipart escapes file name and filename" { test "FormData: file entry collapses to filename in urlencode" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); @@ -1076,7 +1090,7 @@ test "FormData: file entry collapses to filename in urlencode" { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd._entries.append(allocator, .{ @@ -1091,10 +1105,12 @@ test "FormData: file entry collapses to filename in urlencode" { test "FormData: multipart no_file (unselected file input)" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd._entries.append(allocator, .{ @@ -1120,10 +1136,12 @@ test "FormData: multipart no_file (unselected file input)" { test "FormData: no_file entry collapses to empty in urlencode" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd._entries.append(allocator, .{ @@ -1138,10 +1156,12 @@ test "FormData: no_file entry collapses to empty in urlencode" { test "FormData: plaintext write" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.appendText("name", "John"); @@ -1164,10 +1184,12 @@ test "FormData: plaintext write" { test "FormData: plaintext empty body" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; @@ -1178,11 +1200,12 @@ test "FormData: plaintext empty body" { } test "FormData: urlencoded parse" { - const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.parseUrlEncoded("a=1&b=hello+world&c=%26%3D&no_value&&bad=100%zz"); @@ -1197,11 +1220,12 @@ test "FormData: urlencoded parse" { } test "FormData: urlencoded parse exercises the vectorized guard" { - const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; // Values longer than any SIMD vector length, with the lone special @@ -1217,13 +1241,14 @@ test "FormData: urlencoded parse exercises the vectorized guard" { } test "FormData: multipart parse" { - const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.parseMultipart(frame._page, "--BOUNDARY\r\n" ++ @@ -1247,13 +1272,14 @@ test "FormData: multipart parse" { } test "FormData: multipart parse with file" { - const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.parseMultipart(frame._page, "--B\r\n" ++ @@ -1285,7 +1311,8 @@ test "FormData: multipart parse with file" { } test "FormData: multipart parse rejects malformed bodies" { - const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); @@ -1300,7 +1327,7 @@ test "FormData: multipart parse rejects malformed bodies" { for (cases) |case| { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try testing.expectError(error.InvalidFormData, fd.parseMultipart(frame._page, case, "B")); @@ -1309,12 +1336,14 @@ test "FormData: multipart parse rejects malformed bodies" { test "FormData: multipart round-trip" { const allocator = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "FormData test"); + defer arena.release(); const frame = try testing.createFrame(); defer testing.test_session.closeAllPages(); var src = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try src.appendText("username", "alice"); @@ -1332,7 +1361,7 @@ test "FormData: multipart round-trip" { var fd = FormData{ ._rc = .{}, - ._arena = allocator, + ._arena = arena, ._entries = .empty, }; try fd.parseMultipart(frame._page, buf.written(), "BOUNDARY"); diff --git a/src/browser/webapi/net/Request.zig b/src/browser/webapi/net/Request.zig index 36e713a02..dd923b830 100644 --- a/src/browser/webapi/net/Request.zig +++ b/src/browser/webapi/net/Request.zig @@ -34,7 +34,6 @@ const body_init = @import("body_init.zig"); const BodyInit = body_init.BodyInit; const Execution = js.Execution; -const Allocator = std.mem.Allocator; const Request = @This(); @@ -43,7 +42,7 @@ _url: [:0]const u8, _method: http.Method, _headers: ?*Headers, _body: ?[]const u8, -_arena: Allocator, +_arena: *lp.Arena, _cache: Cache, _credentials: Credentials, _redirect: Redirect, @@ -94,10 +93,10 @@ const Cache = enum { pub fn init(input: Input, opts_: ?InitOpts, exec: *const Execution) !*Request { const arena = try exec.getArena(.medium, "Request"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const url = switch (input) { - .url => |u| try URL.resolve(arena, exec.base(), u, .{ .encoding = exec.charset.* }), + .url => |u| try URL.resolve(arena.allocator(), exec.base(), u, .{ .encoding = exec.charset.* }), .request => |r| try arena.dupeZ(u8, r._url), }; @@ -124,7 +123,7 @@ pub fn init(input: Input, opts_: ?InitOpts, exec: *const Execution) !*Request { }; const body = if (opts.body) |b| blk: { - const extracted = try b.extract(arena); + const extracted = try b.extract(arena.allocator()); // Per Fetch §6.5 step 11, the default Content-Type only applies if // the user has not already set one via the headers init dict. if (extracted.content_type) |ct| { @@ -164,8 +163,8 @@ pub fn init(input: Input, opts_: ?InitOpts, exec: *const Execution) !*Request { return self; } -pub fn deinit(self: *Request, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Request, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *Request, page: *Page) void { @@ -345,7 +344,7 @@ pub fn formData(self: *Request, exec: *const Execution) !js.Promise { pub fn clone(self: *const Request, exec: *const Execution) !*Request { const arena = try exec.getArena(if (self._body) |b| b.len else 512, "Request.clone"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const request = try arena.create(Request); request.* = .{ diff --git a/src/browser/webapi/net/Response.zig b/src/browser/webapi/net/Response.zig index 55858fbf7..7d6b5b118 100644 --- a/src/browser/webapi/net/Response.zig +++ b/src/browser/webapi/net/Response.zig @@ -49,7 +49,7 @@ pub const Type = enum { _rc: lp.RC = .{}, _status: u16, -_arena: Allocator, +_arena: *lp.Arena, _headers: *Headers, _body: Body = .empty, _type: Type, @@ -76,7 +76,7 @@ pub const BodyInit = body_init.BodyInit; pub fn init(body_: ?BodyInit, opts_: ?InitOpts, exec: *const Execution) !*Response { const session = exec.session; const arena = try session.getArena(.large, "Response"); - errdefer session.releaseArena(arena); + errdefer arena.release(); const opts = opts_ orelse InitOpts{}; const status_text = if (opts.statusText) |st| try arena.dupe(u8, st) else ""; @@ -87,7 +87,7 @@ pub fn init(body_: ?BodyInit, opts_: ?InitOpts, exec: *const Execution) !*Respon switch (b) { .stream => |stream| break :blk .{ .stream = stream }, else => { - const extracted = try b.extract(arena); + const extracted = try b.extract(arena.allocator()); content_type = extracted.content_type; break :blk .{ .bytes = extracted.bytes }; }, @@ -118,7 +118,7 @@ pub fn init(body_: ?BodyInit, opts_: ?InitOpts, exec: *const Execution) !*Respon pub fn createError(exec: *const Execution) !*Response { const session = exec.session; const arena = try session.getArena(.large, "Response.error"); - errdefer session.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Response); self.* = .{ @@ -143,9 +143,9 @@ pub fn createRedirect(url_: []const u8, status_: ?u16, exec: *const Execution) ! const session = exec.session; const arena = try session.getArena(.large, "Response.redirect"); - errdefer session.releaseArena(arena); + errdefer arena.release(); - const location = try URL.resolve(arena, exec.base(), url_, .{ .encoding = exec.charset.* }); + const location = try URL.resolve(arena.allocator(), exec.base(), url_, .{ .encoding = exec.charset.* }); const headers = try Headers.init(null, exec); try headers.set("location", location, exec); @@ -167,9 +167,9 @@ pub fn createRedirect(url_: []const u8, status_: ?u16, exec: *const Execution) ! pub fn createJson(data: js.Value, opts_: ?InitOpts, exec: *const Execution) !*Response { const session = exec.session; const arena = try session.getArena(.medium, "Response.json"); - errdefer session.releaseArena(arena); + errdefer arena.release(); - const json = data.toJson(arena) catch |err| switch (err) { + const json = data.toJson(arena.allocator()) catch |err| switch (err) { error.JsException => return error.TryCatchRethrow, else => return err, }; @@ -199,12 +199,12 @@ pub fn createJson(data: js.Value, opts_: ?InitOpts, exec: *const Execution) !*Re return self; } -pub fn deinit(self: *Response, page: *Page) void { +pub fn deinit(self: *Response, _: *Page) void { if (self._http_transfer) |resp| { resp.abort(error.Abort); self._http_transfer = null; } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *Response, page: *Page) void { @@ -509,7 +509,7 @@ pub fn clone(self: *const Response, exec: *const Execution) !*Response { .stream => 0, }; const arena = try session.getArena(body_len + self._url.len + 256, "Response.clone"); - errdefer session.releaseArena(arena); + errdefer arena.release(); const body: Body = switch (self._body) { .bytes => |b| .{ .bytes = try arena.dupe(u8, b) }, diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 971299894..fdca29d88 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -42,7 +42,7 @@ pub fn registerTypes() []const type { const URLSearchParams = @This(); _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, _params: KeyValueList, const InitOpts = union(enum) { @@ -53,17 +53,17 @@ const InitOpts = union(enum) { pub fn init(opts_: ?InitOpts, exec: *const Execution) !*URLSearchParams { const arena = try exec.getArena(.small, "URLSearchParams"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const params: KeyValueList = blk: { const opts = opts_ orelse break :blk .empty; switch (opts) { - .query_string => |qs| break :blk try paramsFromString(arena, qs, exec.buf), - .form_data => |fd| break :blk try fd.toKeyValueList(arena), + .query_string => |qs| break :blk try paramsFromString(arena.allocator(), qs, exec.buf), + .form_data => |fd| break :blk try fd.toKeyValueList(arena.allocator()), .value => |js_val| { // Order matters here; Array is also an Object. if (js_val.isArray()) { - break :blk try paramsFromArray(arena, js_val.toArray()); + break :blk try paramsFromArray(arena.allocator(), js_val.toArray()); } if (js_val.isObject()) { // Per the URL spec, an iterable init (URLSearchParams, @@ -73,13 +73,13 @@ pub fn init(opts_: ?InitOpts, exec: *const Execution) !*URLSearchParams { // the prototype-method-leak doesn't just turn into a // silent empty querystring. if (js_val.toZig(*URLSearchParams)) |other| { - break :blk try KeyValueList.copy(arena, other._params); + break :blk try KeyValueList.copy(arena.allocator(), other._params); } else |_| {} // normalizer is null, so frame won't be used - break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, exec.buf); + break :blk try KeyValueList.fromJsObject(arena.allocator(), js_val.toObject(), null, exec.buf); } if (js_val.isString()) |js_str| { - break :blk try paramsFromString(arena, try js_str.toSliceWithAlloc(arena), exec.buf); + break :blk try paramsFromString(arena.allocator(), try js_str.toSliceWithAlloc(arena.allocator()), exec.buf); } return error.InvalidArgument; }, @@ -94,8 +94,8 @@ pub fn init(opts_: ?InitOpts, exec: *const Execution) !*URLSearchParams { return self; } -pub fn deinit(self: *URLSearchParams, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *URLSearchParams, _: *Page) void { + self._arena.release(); } pub fn releaseRef(self: *URLSearchParams, page: *Page) void { @@ -107,7 +107,7 @@ pub fn acquireRef(self: *URLSearchParams) void { } pub fn updateFromString(self: *URLSearchParams, query_string: []const u8, exec: *const Execution) !void { - self._params = try paramsFromString(self._arena, query_string, exec.buf); + self._params = try paramsFromString(self._arena.allocator(), query_string, exec.buf); } pub fn getSize(self: *const URLSearchParams) usize { @@ -127,11 +127,11 @@ pub fn has(self: *const URLSearchParams, name: []const u8) bool { } pub fn set(self: *URLSearchParams, name: []const u8, value: []const u8) !void { - return self._params.set(self._arena, name, value); + return self._params.set(self._arena.allocator(), name, value); } pub fn append(self: *URLSearchParams, name: []const u8, value: []const u8) !void { - return self._params.append(self._arena, name, value); + return self._params.append(self._arena.allocator(), name, value); } pub fn delete(self: *URLSearchParams, name: []const u8, value: ?[]const u8) void { diff --git a/src/browser/webapi/net/WebSocket.zig b/src/browser/webapi/net/WebSocket.zig index 4830fcdc7..72482a883 100644 --- a/src/browser/webapi/net/WebSocket.zig +++ b/src/browser/webapi/net/WebSocket.zig @@ -35,7 +35,6 @@ const MessageEvent = @import("../event/MessageEvent.zig"); const log = lp.log; const Execution = js.Execution; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; const WebSocket = @This(); @@ -45,7 +44,7 @@ pub const Proto = EventTarget; _rc: lp.RC = .{}, _exec: *const Execution, _proto: *EventTarget, -_arena: Allocator, +_arena: *lp.Arena, // Connection state _ready_state: ReadyState = .connecting, @@ -74,7 +73,7 @@ _recv_buffer: std.ArrayList(u8) = .empty, _events: std.ArrayList(RecvEvent) = .empty, // data is dupe'd by the _batch_arena and re-used after every delivery -_batch_arena: ?Allocator = null, +_batch_arena: ?*lp.Arena = null, _batch_bytes: usize = 0, // Linked into the client's ws_dispatch_queue while events await delivery. @@ -153,11 +152,11 @@ pub fn init(url: []const u8, protocols: [][]const u8, exec: *const Execution) !* } const arena = try exec.getArena(.medium, "WebSocket"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const resolved_url = blk: { // Always UTF-8, never the document's charse - const resolved = URL.resolve(arena, exec.base(), url, .{ .encoding = "UTF-8" }) catch |err| switch (err) { + const resolved = URL.resolve(arena.allocator(), exec.base(), url, .{ .encoding = "UTF-8" }) catch |err| switch (err) { error.TypeError => return error.SyntaxError, else => return err, }; @@ -170,10 +169,10 @@ pub fn init(url: []const u8, protocols: [][]const u8, exec: *const Execution) !* // yup, this is what we're supposed to do. if (std.mem.eql(u8, scheme, "http:")) { - break :blk try std.fmt.allocPrintSentinel(arena, "ws{s}", .{resolved["http".len..]}, 0); + break :blk try std.fmt.allocPrintSentinel(arena.allocator(), "ws{s}", .{resolved["http".len..]}, 0); } if (std.mem.eql(u8, scheme, "https:")) { - break :blk try std.fmt.allocPrintSentinel(arena, "wss{s}", .{resolved["https".len..]}, 0); + break :blk try std.fmt.allocPrintSentinel(arena.allocator(), "wss{s}", .{resolved["https".len..]}, 0); } return error.SyntaxError; @@ -181,7 +180,7 @@ pub fn init(url: []const u8, protocols: [][]const u8, exec: *const Execution) !* const http_client = &exec.session.browser.http_client; - const self = try exec._factory.eventTargetWithAllocator(arena, WebSocket{ + const self = try exec._factory.eventTargetWithAllocator(arena.allocator(), WebSocket{ ._exec = exec, ._conn = null, ._arena = arena, @@ -193,7 +192,7 @@ pub fn init(url: []const u8, protocols: [][]const u8, exec: *const Execution) !* // This ensures that if we fail to connect, we have at least 1 event slot // to register the close+error - try self._events.ensureTotalCapacity(arena, 1); + try self._events.ensureTotalCapacity(arena.allocator(), 1); exec.httpOwner().addWS(self); @@ -244,7 +243,7 @@ fn connect(self: *WebSocket, protocols: [][]const u8) !void { errdefer headers.deinit(); if (protocols.len > 0) { - const header = try std.fmt.allocPrintSentinel(arena, "Sec-WebSocket-Protocol: {s}", .{try std.mem.join(arena, ", ", protocols)}, 0); + const header = try std.fmt.allocPrintSentinel(arena.allocator(), "Sec-WebSocket-Protocol: {s}", .{try std.mem.join(arena.allocator(), ", ", protocols)}, 0); try headers.add(header); } @@ -254,13 +253,13 @@ fn connect(self: *WebSocket, protocols: [][]const u8) !void { // protection on WS servers) reject upgrades that arrive without it. // Non-tuple origins (about:blank, data:) serialize to "null", like // Chrome sends for opaque origins. - const origin = (try URL.getOrigin(arena, exec.url.*)) orelse "null"; - const header = try std.fmt.allocPrintSentinel(arena, "Origin: {s}", .{origin}, 0); + const origin = (try URL.getOrigin(arena.allocator(), exec.url.*)) orelse "null"; + const header = try std.fmt.allocPrintSentinel(arena.allocator(), "Origin: {s}", .{origin}, 0); try headers.add(header); } { - var buf: std.Io.Writer.Allocating = .init(arena); + var buf: std.Io.Writer.Allocating = .init(arena.allocator()); try exec.session.cookie_jar.forRequest(resolved_url, &buf.writer, .{ .is_http = true, .is_navigation = false, @@ -312,10 +311,10 @@ pub fn deinit(self: *WebSocket, page: *Page) void { } if (self._batch_arena) |arena| { - page.releaseArena(arena); + arena.release(); } - page.releaseArena(self._arena); + self._arena.release(); } pub fn releaseRef(self: *WebSocket, page: *Page) void { @@ -470,7 +469,7 @@ fn releaseTransport(self: *WebSocket) void { // callback return value — never tear down from here, the pump may still be // inside curl. fn bufferEvent(self: *WebSocket, event: RecvEvent) !void { - try self._events.append(self._arena, event); + try self._events.append(self._arena.allocator(), event); self.enqueueDispatch(); } @@ -518,13 +517,13 @@ fn clearEvents(self: *WebSocket) void { self._batch_bytes = 0; if (self._batch_arena) |arena| { self._batch_arena = null; - self._exec.releaseArena(arena); + arena.release(); } } fn queueMessage(self: *WebSocket, msg: Message) !void { const was_empty = self._send_queue.items.len == 0; - try self._send_queue.append(self._arena, msg); + try self._send_queue.append(self._arena.allocator(), msg); if (was_empty) { // Unpause the send callback so libcurl will request data @@ -587,7 +586,7 @@ pub fn send(self: *WebSocket, data: SendData) !void { switch (data) { .blob => |blob| { const arena = try self._exec.getArena(blob._slice.len, "WebSocket.message"); - errdefer self._exec.releaseArena(arena); + errdefer arena.release(); try self.queueMessage(.{ .binary = .{ .arena = arena, .data = try arena.dupe(u8, blob._slice), @@ -596,17 +595,17 @@ pub fn send(self: *WebSocket, data: SendData) !void { .js_val => |js_val| { if (js_val.isString()) |str| { const arena = try self._exec.getArena(str.len(), "WebSocket.message"); - errdefer self._exec.releaseArena(arena); + errdefer arena.release(); try self.queueMessage(.{ .text = .{ .arena = arena, - .data = try str.toSliceWithAlloc(arena), + .data = try str.toSliceWithAlloc(arena.allocator()), } }); } else { const binary = try js_val.toZig(BinaryData); const buffer = binary.asBuffer(); const arena = try self._exec.getArena(buffer.len, "WebSocket.message"); - errdefer self._exec.releaseArena(arena); + errdefer arena.release(); try self.queueMessage(.{ .binary = .{ .arena = arena, .data = try arena.dupe(u8, buffer), @@ -907,10 +906,10 @@ fn _receivedDataCallback(conn: *http.Connection, data: []const u8) !void { if (meta.len > self._http_client.max_response_size) { return error.MessageTooLarge; } - try self._recv_buffer.ensureTotalCapacity(self._arena, meta.len); + try self._recv_buffer.ensureTotalCapacity(self._arena.allocator(), meta.len); } - try self._recv_buffer.appendSlice(self._arena, data); + try self._recv_buffer.appendSlice(self._arena.allocator(), data); if (meta.bytes_left > 0) { // still more data waiting for this frame @@ -1001,12 +1000,12 @@ const Message = union(enum) { binary: Content, const Content = struct { - arena: Allocator, + arena: *lp.Arena, data: []const u8, }; - fn deinit(self: Message, page: *Page) void { + fn deinit(self: Message, _: *Page) void { switch (self) { - .text, .binary => |msg| page.releaseArena(msg.arena), + .text, .binary => |msg| msg.arena.release(), .close => {}, } } diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 19d30a42e..53a2b9bb4 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -39,7 +39,6 @@ const XMLHttpRequestUpload = @import("XMLHttpRequestUpload.zig"); const log = lp.log; const Execution = js.Execution; -const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; const XMLHttpRequest = @This(); @@ -49,7 +48,7 @@ _rc: lp.RC = .{}, _exec: *const Execution, _proto: *XMLHttpRequestEventTarget, _upload: ?*XMLHttpRequestUpload = null, -_arena: Allocator, +_arena: *lp.Arena, _http_transfer: ?*Transfer = null, // number of inflight requests, we can have multiple, e.g. xhr calling its own @@ -110,8 +109,8 @@ const ResponseType = enum { pub fn init(exec: *const Execution) !*XMLHttpRequest { const arena = try exec.getArena(.large, "XMLHttpRequest"); - errdefer exec.releaseArena(arena); - const self = try exec._factory.xhrEventTarget(arena, XMLHttpRequest{ + errdefer arena.release(); + const self = try exec._factory.xhrEventTarget(arena.allocator(), XMLHttpRequest{ ._exec = exec, ._arena = arena, ._proto = undefined, @@ -120,7 +119,7 @@ pub fn init(exec: *const Execution) !*XMLHttpRequest { return self; } -pub fn deinit(self: *XMLHttpRequest, page: *Page) void { +pub fn deinit(self: *XMLHttpRequest, _: *Page) void { if (self._http_transfer) |resp| { resp.abort(error.Abort); self._http_transfer = null; @@ -134,7 +133,7 @@ pub fn deinit(self: *XMLHttpRequest, page: *Page) void { if (self._upload) |upload| { upload._proto.releaseListeners(); } - page.releaseArena(self._arena); + self._arena.release(); } fn releaseSelfRef(self: *XMLHttpRequest) void { @@ -212,7 +211,7 @@ pub fn open(self: *XMLHttpRequest, method_: []const u8, url: [:0]const u8) !void const exec = self._exec; self._method = try parseMethod(method_); - self._url = try URL.resolve(self._arena, exec.base(), url, .{ .encoding = exec.charset.* }); + self._url = try URL.resolve(self._arena.allocator(), exec.base(), url, .{ .encoding = exec.charset.* }); try self.stateChanged(.opened, exec); } @@ -242,7 +241,7 @@ pub fn send(self: *XMLHttpRequest, body_: ?BodyInit, exec_: *const Execution) !v if (body_) |b| { if (self._method != .GET and self._method != .HEAD) { - const extracted = try b.extract(self._arena); + const extracted = try b.extract(self._arena.allocator()); self._request_body = extracted.bytes; // Per XHR §4.7.6 "send()" step 4, the default Content-Type only // applies if the author hasn't already set one via @@ -322,7 +321,7 @@ pub fn getUpload(self: *XMLHttpRequest) !*XMLHttpRequestUpload { return upload; } const upload = try self._exec._factory.xhrEventTarget( - self._arena, + self._arena.allocator(), XMLHttpRequestUpload{ ._proto = undefined, ._xhr = self }, ); self._upload = upload; @@ -529,14 +528,14 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { var it = transfer.responseHeaderIterator(); while (it.next()) |hdr| { - const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ hdr.name, hdr.value }); - try self._response_headers.append(self._arena, joined); + const joined = try std.fmt.allocPrint(self._arena.allocator(), "{s}: {s}", .{ hdr.name, hdr.value }); + try self._response_headers.append(self._arena.allocator(), joined); } self._response_status = transfer.responseStatus().?; if (transfer.getContentLength()) |cl| { self._response_len = cl; - try self._response_data.ensureTotalCapacity(self._arena, cl); + try self._response_data.ensureTotalCapacity(self._arena.allocator(), cl); } self._response_url = try self._arena.dupeZ(u8, transfer.req.url); @@ -555,7 +554,7 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { fn httpDataCallback(transfer: *Transfer, data: []const u8) !void { const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.req.ctx)); - try self._response_data.appendSlice(self._arena, data); + try self._response_data.appendSlice(self._arena.allocator(), data); try self._proto.dispatch(.progress, .{ .total = self._response_len orelse 0, diff --git a/src/browser/webapi/net/body_init.zig b/src/browser/webapi/net/body_init.zig index d47ecad31..a095e1816 100644 --- a/src/browser/webapi/net/body_init.zig +++ b/src/browser/webapi/net/body_init.zig @@ -144,28 +144,30 @@ test "BodyInit: bytes pass through with text/plain" { test "BodyInit: URLSearchParams emit urlencoded body + content-type" { defer testing.reset(); - const arena = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "body_init test"); + defer arena.release(); const usp = try arena.create(URLSearchParams); usp.* = .{ ._arena = arena, ._params = .empty }; try usp.append("a", "1"); try usp.append("b", "2"); - const r = try (BodyInit{ .url_search_params = usp }).extract(arena); + const r = try (BodyInit{ .url_search_params = usp }).extract(arena.allocator()); try testing.expectString("a=1&b=2", r.bytes); try testing.expectString("application/x-www-form-urlencoded;charset=UTF-8", r.content_type.?); } test "BodyInit: FormData emits multipart with random boundary" { defer testing.reset(); - const arena = testing.arena_allocator; + const arena = try testing.test_app.arena_pool.acquire(.small, "body_init test"); + defer arena.release(); const fd = try arena.create(FormData); fd.* = .{ ._rc = .{}, ._arena = arena, ._entries = .empty }; try fd.appendText("username", "alice"); try fd.appendText("email", "alice@example.com"); - const r = try (BodyInit{ .form_data = fd }).extract(arena); + const r = try (BodyInit{ .form_data = fd }).extract(arena.allocator()); // Body must contain the entries' Content-Disposition lines and end with // the closing boundary marker. @@ -177,7 +179,7 @@ test "BodyInit: FormData emits multipart with random boundary" { try testing.expect(std.mem.indexOf(u8, r.bytes, "Content-Disposition: form-data; name=\"email\"") != null); try testing.expect(std.mem.indexOf(u8, r.bytes, "alice") != null); try testing.expect(std.mem.indexOf(u8, r.bytes, "alice@example.com") != null); - const closer = try std.fmt.allocPrint(arena, "--{s}--\r\n", .{boundary}); + const closer = try std.fmt.allocPrint(arena.allocator(), "--{s}--\r\n", .{boundary}); try testing.expect(std.mem.endsWith(u8, r.bytes, closer)); } diff --git a/src/browser/webapi/selector/List.zig b/src/browser/webapi/selector/List.zig index a5c0f4e30..e2bb92c68 100644 --- a/src/browser/webapi/selector/List.zig +++ b/src/browser/webapi/selector/List.zig @@ -17,6 +17,7 @@ // along with this program. If not, see . const std = @import("std"); +const lp = @import("lightpanda"); const Page = @import("../../Page.zig"); const Frame = @import("../../Frame.zig"); @@ -27,12 +28,10 @@ const Selector = @import("Selector.zig"); const TreeWalker = @import("../TreeWalker.zig").Full; const GenericIterator = @import("../collections/iterator.zig").Entry; -const Allocator = std.mem.Allocator; - const List = @This(); _nodes: []const *Node, -_arena: Allocator, +_arena: *lp.Arena, // For the [somewhat common] case where we just have an #id selector // we can avoid allocating a slice and just use this. _single_node: [1]*Node = undefined, @@ -41,8 +40,8 @@ pub const EntryIterator = GenericIterator(Iterator, null); pub const KeyIterator = GenericIterator(Iterator, "0"); pub const ValueIterator = GenericIterator(Iterator, "1"); -pub fn deinit(self: *const List, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *const List, _: *Page) void { + self._arena.release(); } pub fn collect( diff --git a/src/browser/webapi/selector/Selector.zig b/src/browser/webapi/selector/Selector.zig index e1207ef16..edfaba606 100644 --- a/src/browser/webapi/selector/Selector.zig +++ b/src/browser/webapi/selector/Selector.zig @@ -113,10 +113,10 @@ pub const Cache = struct { } }; -fn collectAll(arena: Allocator, selectors: []const Selector, root: *Node, frame: *Frame) !*List { +fn collectAll(arena: *lp.Arena, selectors: []const Selector, root: *Node, frame: *Frame) !*List { var nodes: std.AutoArrayHashMapUnmanaged(*Node, void) = .empty; for (selectors) |selector| { - try List.collect(arena, root, selector, &nodes, frame); + try List.collect(arena.allocator(), root, selector, &nodes, frame); } const list = try arena.create(List); @@ -142,7 +142,7 @@ pub fn querySelector(root: *Node, input: []const u8, frame: *Frame) !?*Node.Elem pub fn querySelectorAll(root: *Node, input: []const u8, frame: *Frame) !*List { const arena = try frame.getArena(.small, "querySelectorAll"); - errdefer frame.releaseArena(arena); + errdefer arena.release(); return collectAll(arena, try cachedParse(frame._session.browser, input), root, frame); } @@ -171,8 +171,8 @@ pub fn querySelectorAllUncached(root: *Node, input: []const u8, frame: *Frame) ! return error.SyntaxError; } const arena = try frame.getArena(.small, "querySelectorAllUncached"); - errdefer frame.releaseArena(arena); - return collectAll(arena, try Parser.parseList(arena, input), root, frame); + errdefer arena.release(); + return collectAll(arena, try Parser.parseList(arena.allocator(), input), root, frame); } pub fn matchesUncached(arena: Allocator, el: *Node.Element, input: []const u8, frame: *Frame) !bool { diff --git a/src/browser/webapi/storage/CookieStore.zig b/src/browser/webapi/storage/CookieStore.zig index d6ff17bd3..5ff69cee4 100644 --- a/src/browser/webapi/storage/CookieStore.zig +++ b/src/browser/webapi/storage/CookieStore.zig @@ -27,7 +27,6 @@ const Cookie = @import("Cookie.zig"); const EventTarget = @import("../EventTarget.zig"); const CookieChangeEvent = @import("../event/CookieChangeEvent.zig"); -const Allocator = std.mem.Allocator; const Execution = js.Execution; const String = lp.String; @@ -100,7 +99,7 @@ fn onCookieChanged(ctx: *anyopaque, data: *const Notification.CookieChanged) !vo // from the mutation site. We snapshot the notification fields onto a // small page arena that the scheduled callback releases after dispatch. const arena = try exec.getArena(.tiny, "CookieStore.change"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); const cb = try arena.create(ChangeCallback); cb.* = .{ @@ -129,7 +128,7 @@ const ChangeCallback = struct { // The CookieStore could have been detached in the meantime, and so its // _exec pointer could have been reset. exec: *Execution, - arena: Allocator, + arena: *lp.Arena, kind: Notification.CookieChanged.Kind, name: []const u8, value: []const u8, @@ -144,7 +143,7 @@ const ChangeCallback = struct { } fn releaseArena(self: *ChangeCallback) void { - self.exec.releaseArena(self.arena); + self.arena.release(); } fn run(ctx: *anyopaque) !?u32 { diff --git a/src/browser/webapi/storage/idb/IDBCursor.zig b/src/browser/webapi/storage/idb/IDBCursor.zig index b1a62290a..cbf8f25cd 100644 --- a/src/browser/webapi/storage/idb/IDBCursor.zig +++ b/src/browser/webapi/storage/idb/IDBCursor.zig @@ -144,7 +144,7 @@ fn _init(store: *IDBObjectStore, txn: *IDBTransaction, index_id: ?i64, source: S self.* = cursor_value; public = try local.zigValueToJs(self, .{}); } else { - const with_value = try Factory.chainedWithAllocator(txn._arena, .{ + const with_value = try Factory.chainedWithAllocator(txn._arena.allocator(), .{ cursor_value, IDBCursorWithValue{ ._proto = undefined }, }); @@ -189,7 +189,7 @@ pub fn @"continue"(self: *IDBCursor, key_arg: ?js.Value, exec: *Execution) !void if (key_arg) |k| { // Key conversion (DataError) must run before the got-value flag is // cleared, so a failing continue() leaves the cursor re-iterable. - const encoded = try Key.encodeValue(self._txn._arena, k); + const encoded = try Key.encodeValue(self._txn._arena.allocator(), k); // The target must move past the current key in the iteration direction. const order = std.mem.order(u8, encoded, self._key.?); if (if (self._direction.reverse()) order != .lt else order != .gt) { @@ -212,8 +212,8 @@ pub fn continuePrimaryKey(self: *IDBCursor, key_arg: js.Value, primary_key_arg: const reverse = self._direction.reverse(); // Key conversion (DataError) precedes clearing the got-value flag; see continue(). - const key = try Key.encodeValue(self._txn._arena, key_arg); - const primary_key = try Key.encodeValue(self._txn._arena, primary_key_arg); + const key = try Key.encodeValue(self._txn._arena.allocator(), key_arg); + const primary_key = try Key.encodeValue(self._txn._arena.allocator(), primary_key_arg); // The (key, primaryKey) pair must move past the current position. const ok = switch (std.mem.order(u8, key, self._key.?)) { @@ -364,16 +364,16 @@ fn position(self: *IDBCursor, key: []const u8, primary_key: []const u8, value: ? self.invalidateValue(); self._key_buf.clearRetainingCapacity(); - try self._key_buf.appendSlice(arena, key); + try self._key_buf.appendSlice(arena.allocator(), key); self._key = self._key_buf.items; self._pk_buf.clearRetainingCapacity(); - try self._pk_buf.appendSlice(arena, primary_key); + try self._pk_buf.appendSlice(arena.allocator(), primary_key); self._primary_key = self._pk_buf.items; if (value) |v| { self._val_buf.clearRetainingCapacity(); - try self._val_buf.appendSlice(arena, v); + try self._val_buf.appendSlice(arena.allocator(), v); self._value = self._val_buf.items; } else { self._value = null; diff --git a/src/browser/webapi/storage/idb/IDBDatabase.zig b/src/browser/webapi/storage/idb/IDBDatabase.zig index ebbaaa11a..c2f2237ce 100644 --- a/src/browser/webapi/storage/idb/IDBDatabase.zig +++ b/src/browser/webapi/storage/idb/IDBDatabase.zig @@ -90,11 +90,11 @@ pub fn createObjectStore( if (opts.autoIncrement and keyPathBlocksAutoIncrement(kp)) { return error.InvalidAccessError; } - break :blk try Key.dupeKeyPath(txn._arena, kp); + break :blk try Key.dupeKeyPath(txn._arena.allocator(), kp); } else null; const store_id = self._engine.createObjectStore( - txn._arena, + txn._arena.allocator(), self._database_id, name, key_path, @@ -158,7 +158,7 @@ pub fn transaction( .readonly => .readonly, .readwrite => .readwrite, }, opts.durability, exec); - txn._scope = try normalizeStoreNames(txn._arena, store_names); + txn._scope = try normalizeStoreNames(txn._arena.allocator(), store_names); return txn; } @@ -213,9 +213,9 @@ pub fn getVersion(self: *const IDBDatabase) i64 { pub fn getObjectStoreNames(self: *IDBDatabase, exec: *Execution) !*DOMStringList { const arena = try exec.getArena(.small, "IDB.getObjectStoreNames"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); - const names = try self._engine.objectStoreNames(arena, self._database_id); + const names = try self._engine.objectStoreNames(arena.allocator(), self._database_id); const list = try arena.create(DOMStringList); list.* = .{ ._items = names, ._arena = arena }; return list; diff --git a/src/browser/webapi/storage/idb/IDBIndex.zig b/src/browser/webapi/storage/idb/IDBIndex.zig index df993cc15..5c55f5a97 100644 --- a/src/browser/webapi/storage/idb/IDBIndex.zig +++ b/src/browser/webapi/storage/idb/IDBIndex.zig @@ -89,7 +89,7 @@ fn txn(self: *IDBIndex) !*IDBTransaction { pub fn get(self: *IDBIndex, query: js.Value, exec: *Execution) !*IDBRequest { const t = try self.txn(); - const bounds = try IDBKeyRange.resolveKey(t._arena, query, exec); + const bounds = try IDBKeyRange.resolveKey(t._arena.allocator(), query, exec); const request = try t.newRequest(); return request.submit(.{ .index_get = .{ .index = self, .bounds = bounds } }, exec); } @@ -107,7 +107,7 @@ pub fn runGet(self: *IDBIndex, request: *IDBRequest, bounds: Engine.Bounds, exec pub fn getKey(self: *IDBIndex, query: js.Value, exec: *Execution) !*IDBRequest { const t = try self.txn(); - const bounds = try IDBKeyRange.resolveKey(t._arena, query, exec); + const bounds = try IDBKeyRange.resolveKey(t._arena.allocator(), query, exec); const request = try t.newRequest(); return request.submit(.{ .index_get_key = .{ .index = self, .bounds = bounds } }, exec); } @@ -133,14 +133,14 @@ pub fn getAllKeys(self: *IDBIndex, query_or_options: ?js.Value, count_: ?u32, ex pub fn getAllRecords(self: *IDBIndex, options: ?js.Value, exec: *Execution) !*IDBRequest { const t = try self.txn(); - const args = try IDBKeyRange.resolveGetAllOptions(t._arena, options, exec); + const args = try IDBKeyRange.resolveGetAllOptions(t._arena.allocator(), options, exec); const request = try t.newRequest(); return request.submit(.{ .index_get_all = .{ .index = self, .args = args, .mode = .record } }, exec); } fn _getAll(self: *IDBIndex, query_or_options: ?js.Value, count_: ?u32, mode: IDBObjectStore.GetAllMode, exec: *Execution) !*IDBRequest { const t = try self.txn(); - const args = try IDBKeyRange.resolveGetAll(t._arena, query_or_options, count_, exec); + const args = try IDBKeyRange.resolveGetAll(t._arena.allocator(), query_or_options, count_, exec); const request = try t.newRequest(); return request.submit(.{ .index_get_all = .{ .index = self, .args = args, .mode = mode } }, exec); } @@ -184,7 +184,7 @@ fn rowToValue(self: *IDBIndex, mode: IDBObjectStore.GetAllMode, key: []const u8, pub fn count(self: *IDBIndex, query: ?js.Value, exec: *Execution) !*IDBRequest { const t = try self.txn(); - const bounds = try IDBKeyRange.resolveQuery(t._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(t._arena.allocator(), query, exec); const request = try t.newRequest(); return request.submit(.{ .index_count = .{ .index = self, .bounds = bounds } }, exec); } @@ -200,13 +200,13 @@ pub fn runCount(self: *IDBIndex, request: *IDBRequest, bounds: Engine.Bounds, ex pub fn openCursor(self: *IDBIndex, query: ?js.Value, direction: ?IDBCursor.Direction, exec: *Execution) !*IDBRequest { try self.assertLive(); - const bounds = try IDBKeyRange.resolveQuery(self._store._txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(self._store._txn._arena.allocator(), query, exec); return IDBCursor.initIndex(self, bounds, direction orelse .next, false, exec); } pub fn openKeyCursor(self: *IDBIndex, query: ?js.Value, direction: ?IDBCursor.Direction, exec: *Execution) !*IDBRequest { try self.assertLive(); - const bounds = try IDBKeyRange.resolveQuery(self._store._txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(self._store._txn._arena.allocator(), query, exec); return IDBCursor.initIndex(self, bounds, direction orelse .next, true, exec); } diff --git a/src/browser/webapi/storage/idb/IDBObjectStore.zig b/src/browser/webapi/storage/idb/IDBObjectStore.zig index 272ed7ee0..f9554f53f 100644 --- a/src/browser/webapi/storage/idb/IDBObjectStore.zig +++ b/src/browser/webapi/storage/idb/IDBObjectStore.zig @@ -93,7 +93,7 @@ pub fn get(self: *IDBObjectStore, query: js.Value, exec: *Execution) !*IDBReques try self.assertLive(); const txn = self._txn; try txn.assertActive(); - const bounds = try IDBKeyRange.resolveKey(txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveKey(txn._arena.allocator(), query, exec); const request = try txn.newRequest(); return request.submit(.{ .store_get = .{ .store = self, .bounds = bounds } }, exec); } @@ -116,7 +116,7 @@ pub fn delete(self: *IDBObjectStore, query: js.Value, exec: *Execution) !*IDBReq return error.ReadOnlyError; } try txn.assertActive(); - const bounds = try IDBKeyRange.resolveKey(txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveKey(txn._arena.allocator(), query, exec); const request = try txn.newRequest(); return request.submit(.{ .store_delete = .{ .store = self, .bounds = bounds } }, exec); } @@ -160,7 +160,7 @@ pub fn count(self: *IDBObjectStore, query: ?js.Value, exec: *Execution) !*IDBReq try self.assertLive(); const txn = self._txn; try txn.assertActive(); - const bounds = try IDBKeyRange.resolveQuery(txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(txn._arena.allocator(), query, exec); const request = try txn.newRequest(); return request.submit(.{ .store_count = .{ .store = self, .bounds = bounds } }, exec); } @@ -189,7 +189,7 @@ pub fn getAllRecords(self: *IDBObjectStore, options: ?js.Value, exec: *Execution try self.assertLive(); const txn = self._txn; try txn.assertActive(); - const args = try IDBKeyRange.resolveGetAllOptions(txn._arena, options, exec); + const args = try IDBKeyRange.resolveGetAllOptions(txn._arena.allocator(), options, exec); const request = try txn.newRequest(); return request.submit(.{ .store_get_all = .{ .store = self, .args = args, .mode = .record } }, exec); } @@ -198,7 +198,7 @@ fn _getAll(self: *IDBObjectStore, query_or_options: ?js.Value, count_: ?u32, mod try self.assertLive(); const txn = self._txn; try txn.assertActive(); - const args = try IDBKeyRange.resolveGetAll(txn._arena, query_or_options, count_, exec); + const args = try IDBKeyRange.resolveGetAll(txn._arena.allocator(), query_or_options, count_, exec); const request = try txn.newRequest(); return request.submit(.{ .store_get_all = .{ .store = self, .args = args, .mode = mode } }, exec); } @@ -241,7 +241,7 @@ pub fn getKey(self: *IDBObjectStore, query: js.Value, exec: *Execution) !*IDBReq try self.assertLive(); const txn = self._txn; try txn.assertActive(); - const bounds = try IDBKeyRange.resolveKey(txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveKey(txn._arena.allocator(), query, exec); const request = try txn.newRequest(); return request.submit(.{ .store_get_key = .{ .store = self, .bounds = bounds } }, exec); } @@ -259,13 +259,13 @@ pub fn runGetKey(self: *IDBObjectStore, request: *IDBRequest, bounds: Engine.Bou pub fn openCursor(self: *IDBObjectStore, query: ?js.Value, direction: ?IDBCursor.Direction, exec: *Execution) !*IDBRequest { try self.assertLive(); - const bounds = try IDBKeyRange.resolveQuery(self._txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(self._txn._arena.allocator(), query, exec); return IDBCursor.init(self, bounds, direction orelse .next, false, exec); } pub fn openKeyCursor(self: *IDBObjectStore, query: ?js.Value, direction: ?IDBCursor.Direction, exec: *Execution) !*IDBRequest { try self.assertLive(); - const bounds = try IDBKeyRange.resolveQuery(self._txn._arena, query, exec); + const bounds = try IDBKeyRange.resolveQuery(self._txn._arena.allocator(), query, exec); return IDBCursor.init(self, bounds, direction orelse .next, true, exec); } @@ -319,7 +319,7 @@ fn write(self: *IDBObjectStore, value: js.Value, key_arg: ?js.Value, kind: Write } if (try Key.extractKeyPath(exec.js.local.?, value, kp)) |extracted| { break :blk .{ .explicit = .{ - .encoded = try Key.encodeValue(txn._arena, extracted), + .encoded = try Key.encodeValue(txn._arena.allocator(), extracted), .bump = if (self._auto_increment and extracted.isNumber()) try extracted.toF64() else null, } }; } @@ -338,7 +338,7 @@ fn write(self: *IDBObjectStore, value: js.Value, key_arg: ?js.Value, kind: Write // Out-of-line keys. if (key_arg) |k| { break :blk .{ .explicit = .{ - .encoded = try Key.encodeValue(txn._arena, k), + .encoded = try Key.encodeValue(txn._arena.allocator(), k), .bump = if (self._auto_increment and k.isNumber()) try k.toF64() else null, } }; } @@ -498,12 +498,12 @@ pub fn createIndex(self: *IDBObjectStore, name: []const u8, key_path: Key.KeyPat return error.InvalidAccessError; } - const owned_key_path = try Key.dupeKeyPath(txn._arena, key_path); + const owned_key_path = try Key.dupeKeyPath(txn._arena.allocator(), key_path); try self._engine.savepoint(); errdefer self._engine.rollbackSavepoint(); - const index_id = self._engine.createIndexRow(txn._arena, self._store_id, name, owned_key_path, opts.unique, opts.multiEntry) catch |err| switch (err) { + const index_id = self._engine.createIndexRow(txn._arena.allocator(), self._store_id, name, owned_key_path, opts.unique, opts.multiEntry) catch |err| switch (err) { error.Constraint => return error.ConstraintError, // duplicate index name else => return err, }; @@ -533,7 +533,7 @@ pub fn createIndex(self: *IDBObjectStore, name: []const u8, key_path: Key.KeyPat }, owned_name); idb_index._created = true; try self._engine.releaseSavepoint(); - try self._indexes.append(txn._arena, idb_index); + try self._indexes.append(txn._arena.allocator(), idb_index); return idb_index; } @@ -569,18 +569,18 @@ pub fn index(self: *IDBObjectStore, name: []const u8, _: *Execution) !*IDBIndex } const txn = self._txn; - const info = (try self._engine.indexInfo(txn._arena, self._store_id, name)) orelse return error.NotFound; + const info = (try self._engine.indexInfo(txn._arena.allocator(), self._store_id, name)) orelse return error.NotFound; const owned_name = try txn.dupe(name); const idx = try IDBIndex.init(self, info, owned_name); - try self._indexes.append(txn._arena, idx); + try self._indexes.append(txn._arena.allocator(), idx); return idx; } pub fn getIndexNames(self: *IDBObjectStore, exec: *Execution) !*DOMStringList { const arena = try exec.getArena(.small, "IDB.getIndexNames"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); - const names = try self._engine.indexNames(arena, self._store_id); + const names = try self._engine.indexNames(arena.allocator(), self._store_id); const list = try arena.create(DOMStringList); list.* = .{ ._items = names, ._arena = arena }; return list; diff --git a/src/browser/webapi/storage/idb/IDBTransaction.zig b/src/browser/webapi/storage/idb/IDBTransaction.zig index 747f0a0f1..726cae77f 100644 --- a/src/browser/webapi/storage/idb/IDBTransaction.zig +++ b/src/browser/webapi/storage/idb/IDBTransaction.zig @@ -35,7 +35,6 @@ const DOMStringList = @import("../../collections.zig").DOMStringList; const log = lp.log; const Execution = js.Execution; -const Allocator = std.mem.Allocator; const FunctionSetter = idb.FunctionSetter; const IS_DEBUG = @import("builtin").mode == .Debug; @@ -62,7 +61,7 @@ _durability: Durability = .default, // engine's connection gate (gate ownership needs no ref of its own: it's only // ever held while a drain task exists). _rc: lp.RC = .{}, -_arena: Allocator, +_arena: *lp.Arena, // v8 handles owned by the transaction, swept (reset) in deinit. Slots are // arena-allocated so an early release and the sweep hit the same instance — @@ -127,8 +126,8 @@ pub fn init(db: *IDBDatabase, mode: Mode, durability: Durability, exec: *Executi const arena = try exec.getArena(.small, "IDBTransaction"); const self = blk: { - errdefer exec.releaseArena(arena); - const s = try exec._factory.eventTargetWithAllocator(arena, IDBTransaction{ + errdefer arena.release(); + const s = try exec._factory.eventTargetWithAllocator(arena.allocator(), IDBTransaction{ ._proto = undefined, ._exec = exec, ._db = db, @@ -154,9 +153,9 @@ pub fn init(db: *IDBDatabase, mode: Mode, durability: Durability, exec: *Executi // path) must pin it for the duration of the upgrade. pub fn initVersionChange(db: *IDBDatabase, exec: *Execution) !*IDBTransaction { const arena = try exec.getArena(.small, "IDBTransaction"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); - const self = try exec._factory.eventTargetWithAllocator(arena, IDBTransaction{ + const self = try exec._factory.eventTargetWithAllocator(arena.allocator(), IDBTransaction{ ._proto = undefined, ._exec = exec, ._db = db, @@ -175,7 +174,7 @@ pub fn initVersionChange(db: *IDBDatabase, exec: *Execution) !*IDBTransaction { return self; } -pub fn deinit(self: *IDBTransaction, page: *Page) void { +pub fn deinit(self: *IDBTransaction, _: *Page) void { if (comptime IS_DEBUG) { // Pins hold refs, so the last release can't happen while parked (nor // while a drain task is scheduled). @@ -184,7 +183,7 @@ pub fn deinit(self: *IDBTransaction, page: *Page) void { for (self._globals.items) |slot| { slot.reset(); } - page.releaseArena(self._arena); + self._arena.release(); } pub fn acquireRef(self: *IDBTransaction) void { @@ -199,9 +198,9 @@ pub fn releaseRef(self: *IDBTransaction, page: *Page) void { // transaction's memory is released — or earlier, by calling reset() on the // returned slot (the sweep's second reset is then a no-op). pub fn persist(self: *IDBTransaction, value: js.Value) !*js.GlobalSlot { - const slot = try value.persistBare(self._arena); + const slot = try value.persistBare(self._arena.allocator()); errdefer slot.reset(); - try self._globals.append(self._arena, slot); + try self._globals.append(self._arena.allocator(), slot); return slot; } @@ -364,14 +363,14 @@ pub fn ensureBegun(self: *IDBTransaction) !void { } pub fn newRequest(self: *IDBTransaction) !*IDBRequest { - const request = try self._exec._factory.eventTargetWithAllocator(self._arena, IDBRequest{ ._proto = undefined }); + const request = try self._exec._factory.eventTargetWithAllocator(self._arena.allocator(), IDBRequest{ ._proto = undefined }); request._txn = .{ .owned = self }; return request; } pub fn enqueue(self: *IDBTransaction, request: *IDBRequest) !void { request._txn_index = self._queue.items.len; - try self._queue.append(self._arena, request); + try self._queue.append(self._arena.allocator(), request); } pub fn objectStore(self: *IDBTransaction, name: []const u8) !*IDBObjectStore { @@ -382,20 +381,20 @@ pub fn objectStore(self: *IDBTransaction, name: []const u8) !*IDBObjectStore { } const database_id = self._db._database_id; - const info = (try self._engine.objectStoreInfo(self._arena, database_id, name)) orelse { + const info = (try self._engine.objectStoreInfo(self._arena.allocator(), database_id, name)) orelse { return error.NotFound; }; const owned_name = try self.dupe(name); const store = try IDBObjectStore.init(self, info.id, owned_name, info.key_path, info.auto_increment); - try self._stores.append(self._arena, store); + try self._stores.append(self._arena.allocator(), store); return store; } // Register a store created during an upgrade so a later objectStore() returns // the same object. pub fn cacheStore(self: *IDBTransaction, store: *IDBObjectStore) !void { - try self._stores.append(self._arena, store); + try self._stores.append(self._arena.allocator(), store); } // A store was deleted during an upgrade; a later objectStore() must miss. @@ -423,14 +422,14 @@ pub fn getDb(self: *IDBTransaction) *IDBDatabase { pub fn getObjectStoreNames(self: *IDBTransaction, exec: *Execution) !*DOMStringList { const arena = try exec.getArena(.small, "IDB.getObjectStoreNames"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); // A versionchange transaction spans every store; its set changes as the // upgrade creates/deletes stores, so resolve it live rather than caching. // The list is refcounted and can outlive the transaction, so the scope // names are copied onto the list's own arena. const names = if (self._mode == .versionchange) - try self._engine.objectStoreNames(arena, self._db._database_id) + try self._engine.objectStoreNames(arena.allocator(), self._db._database_id) else blk: { const copy = try arena.alloc([]const u8, self._scope.len); for (self._scope, 0..) |name, i| { diff --git a/src/browser/webapi/storage/idb/IDBVersionChangeEvent.zig b/src/browser/webapi/storage/idb/IDBVersionChangeEvent.zig index 948d742a7..1dddc0363 100644 --- a/src/browser/webapi/storage/idb/IDBVersionChangeEvent.zig +++ b/src/browser/webapi/storage/idb/IDBVersionChangeEvent.zig @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../../../js/js.zig"); @@ -24,7 +23,6 @@ const js = @import("../../../js/js.zig"); const Event = @import("../../Event.zig"); const String = lp.String; -const Allocator = std.mem.Allocator; const Execution = js.Execution; const IDBVersionChangeEvent = @This(); @@ -44,18 +42,18 @@ const Options = Event.inheritOptions(IDBVersionChangeEvent, IDBVersionChangeEven pub fn init(typ: []const u8, opts_: ?Options, exec: *const Execution) !*IDBVersionChangeEvent { const arena = try exec.getArena(.tiny, "IDBVersionChangeEvent"); - errdefer exec.releaseArena(arena); - const type_string = try String.init(arena, typ, .{}); + errdefer arena.release(); + const type_string = try String.init(arena.allocator(), typ, .{}); return initWithTrusted(arena, type_string, opts_, false, exec); } pub fn initTrusted(typ: String, old_version: u64, new_version: ?u64, exec: *const Execution) !*IDBVersionChangeEvent { const arena = try exec.getArena(.tiny, "IDBVersionChangeEvent.trusted"); - errdefer exec.releaseArena(arena); + errdefer arena.release(); return initWithTrusted(arena, typ, .{ .oldVersion = old_version, .newVersion = new_version }, true, exec); } -fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool, exec: *const Execution) !*IDBVersionChangeEvent { +fn initWithTrusted(arena: *lp.Arena, typ: String, opts_: ?Options, trusted: bool, exec: *const Execution) !*IDBVersionChangeEvent { const opts = opts_ orelse Options{}; const event = try exec._factory.event(arena, typ, IDBVersionChangeEvent{ diff --git a/src/browser/webapi/svg/Angle.zig b/src/browser/webapi/svg/Angle.zig index 163ba6e39..1d250affb 100644 --- a/src/browser/webapi/svg/Angle.zig +++ b/src/browser/webapi/svg/Angle.zig @@ -29,7 +29,7 @@ const String = lp.String; const Angle = @This(); _rc: lp.RC = .{}, -_arena: std.mem.Allocator, +_arena: *lp.Arena, _value: f64 = 0, _unit: Unit = .unspecified, _element: ?*Element = null, @@ -47,14 +47,14 @@ const Unit = enum(u16) { pub fn detached(frame: *Frame) !*Angle { const arena = try frame._page.getArena(.tiny, "SVGAngle"); - errdefer frame._page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Angle); self.* = .{ ._arena = arena }; return self; } -pub fn deinit(self: *Angle, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Angle, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *Angle) void { diff --git a/src/browser/webapi/svg/Length.zig b/src/browser/webapi/svg/Length.zig index 6bb0dd817..0aec09250 100644 --- a/src/browser/webapi/svg/Length.zig +++ b/src/browser/webapi/svg/Length.zig @@ -32,7 +32,7 @@ const Length = @This(); // lookup cache, so a JS wrapper being collected can never free it. Only a // detached length (own arena, zero initial refs) dies with its last wrapper. _rc: lp.RC = .init(1), -_arena: ?std.mem.Allocator = null, +_arena: ?*lp.Arena = null, _value: f64 = 0, _unit: Unit = .number, _element: ?*Element = null, @@ -66,14 +66,14 @@ const MAX_ANCESTOR_DEPTH = 32; pub fn detached(frame: *Frame) !*Length { const arena = try frame._page.getArena(.tiny, "SVGLength"); - errdefer frame._page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Length); self.* = .{ ._rc = .{}, ._arena = arena }; return self; } -pub fn deinit(self: *Length, page: *Page) void { - page.releaseArena(self._arena.?); +pub fn deinit(self: *Length, _: *Page) void { + self._arena.?.release(); } pub fn acquireRef(self: *Length) void { diff --git a/src/browser/webapi/svg/Number.zig b/src/browser/webapi/svg/Number.zig index 3183f2f99..fef890b1a 100644 --- a/src/browser/webapi/svg/Number.zig +++ b/src/browser/webapi/svg/Number.zig @@ -26,19 +26,19 @@ const Page = @import("../../Page.zig"); const Number = @This(); _rc: lp.RC = .{}, -_arena: std.mem.Allocator, +_arena: *lp.Arena, _value: f32 = 0, pub fn detached(frame: *Frame) !*Number { const arena = try frame._page.getArena(.tiny, "SVGNumber"); - errdefer frame._page.releaseArena(arena); + errdefer arena.release(); const self = try arena.create(Number); self.* = .{ ._arena = arena }; return self; } -pub fn deinit(self: *Number, page: *Page) void { - page.releaseArena(self._arena); +pub fn deinit(self: *Number, _: *Page) void { + self._arena.release(); } pub fn acquireRef(self: *Number) void { diff --git a/src/cdp/AXNode.zig b/src/cdp/AXNode.zig index 270de9439..753c288a8 100644 --- a/src/cdp/AXNode.zig +++ b/src/cdp/AXNode.zig @@ -44,7 +44,7 @@ pub const Writer = struct { frame: *Frame, visibility_cache: *DOMNode.Element.VisibilityCache, label_index: *Label.LabelByForIndex, - temp_arena: std.mem.Allocator, + temp_arena: *lp.Arena, // When null, emit the full AX tree (getFullAXTree). When set, walk the // subtree visiting all nodes (including AX-ignored ones, per the // queryAXTree spec) and emit only nodes whose role + accessible name @@ -703,7 +703,7 @@ pub const Writer = struct { if (!std.mem.eql(u8, needle, resolved.role)) return; } - const name = (try axn.getName(self.frame, self.temp_arena)) orelse ""; + const name = (try axn.getName(self.frame, self.temp_arena.allocator())) orelse ""; if (filter.accessible_name) |needle| { if (!std.mem.eql(u8, needle, name)) return; } @@ -970,12 +970,12 @@ pub fn getName(self: AXNode, frame: *Frame, allocator: std.mem.Allocator) !?[]co fn writeName( axnode: AXNode, - temp_arena: ?std.mem.Allocator, + temp_arena: ?*lp.Arena, w: anytype, frame: *Frame, label_index: ?*Label.LabelByForIndex, ) !?AXSource { - defer if (temp_arena) |a| frame._session.arena_pool.reset(a, scratch_retain_limit); + defer if (temp_arena) |a| a.reset(scratch_retain_limit); const node = axnode.dom; @@ -1227,7 +1227,7 @@ fn labelPromotionTarget( } fn writeLabelName( - temp_arena: ?std.mem.Allocator, + temp_arena: ?*lp.Arena, node: *DOMNode, el: *DOMNode.Element, frame: *Frame, @@ -1256,7 +1256,7 @@ fn writeLabelName( } fn writeLabelInnerText( - temp_arena: ?std.mem.Allocator, + temp_arena: ?*lp.Arena, label_el: *DOMNode.Element, frame: *Frame, w: anytype, @@ -1272,8 +1272,8 @@ fn writeLabelInnerText( /// Allocator for throwaway name-resolution buffers: prefers the writer's /// temp arena so multiple calls reuse its retained page; falls back to /// `frame.call_arena` on the non-Writer `getName` path. -fn scratchAllocator(temp_arena: ?std.mem.Allocator, frame: *Frame) std.mem.Allocator { - return temp_arena orelse frame.call_arena; +fn scratchAllocator(temp_arena: ?*lp.Arena, frame: *Frame) std.mem.Allocator { + return if (temp_arena) |a| a.allocator() else frame.call_arena; } fn isHidden(elt: *DOMNode.Element, frame: *Frame, cache: *DOMNode.Element.VisibilityCache) bool { @@ -1524,7 +1524,7 @@ test "AXNode: writer" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .registry = ®istry, @@ -1616,7 +1616,7 @@ test "AXNode: writer prunes hidden and resolves labels" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .registry = ®istry, @@ -1751,7 +1751,7 @@ test "AXNode: Writer query filters by role" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, @@ -1816,7 +1816,7 @@ test "AXNode: writer maps password input to textbox" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .registry = ®istry, @@ -1869,7 +1869,7 @@ test "AXNode: Writer query filters by accessible name" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, @@ -1910,7 +1910,7 @@ test "AXNode: Writer query combined role+name filter promotes hidden-input label var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); // Fixture has a CSS-hidden checkbox `` plus // ``. Walking finds both: @@ -1963,7 +1963,7 @@ test "AXNode: Writer query no match returns empty array" { var visibility_cache: DOMNode.Element.VisibilityCache = .empty; var label_index: Label.LabelByForIndex = .{}; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index bac8a64a6..623c97023 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -717,13 +717,13 @@ pub const BrowserContext = struct { pub fn createIsolatedWorld(self: *BrowserContext, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld { const browser = &self.cdp.browser; const arena = try browser.arena_pool.acquire(.small, "IsolatedWorld"); - errdefer browser.arena_pool.release(arena); + errdefer arena.release(); const call_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.call_arena"); - errdefer browser.arena_pool.release(call_arena); + errdefer call_arena.release(); const local_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.local_arena"); - errdefer browser.arena_pool.release(local_arena); + errdefer local_arena.release(); const world = try arena.create(IsolatedWorld); world.* = .{ @@ -750,7 +750,7 @@ pub const BrowserContext = struct { }; } - pub fn axnodeWriter(self: *BrowserContext, temp_arena: Allocator, root: *const Node, opts: AXNode.Writer.Opts) !AXNode.Writer { + pub fn axnodeWriter(self: *BrowserContext, temp_arena: *lp.Arena, root: *const Node, opts: AXNode.Writer.Opts) !AXNode.Writer { // Bind the writer to the frame that owns the root node. Name resolution // (`Label.findLabelByFor` against `ownerDocument`) and visibility // checks (`frame._style_manager`) are per-frame; getting this wrong on @@ -1148,9 +1148,9 @@ const ScriptOnNewDocument = struct { /// Generally the client needs to resolve a node into the isolated world to be able to work with it. /// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts. const IsolatedWorld = struct { - arena: Allocator, - call_arena: Allocator, - local_arena: Allocator, + arena: *lp.Arena, + call_arena: *lp.Arena, + local_arena: *lp.Arena, browser: *Browser, name: []const u8, context: ?*js.Context = null, @@ -1162,9 +1162,9 @@ 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); + self.call_arena.release(); + self.local_arena.release(); + self.arena.release(); } pub fn removeContext(self: *IsolatedWorld) void { @@ -1187,9 +1187,9 @@ const IsolatedWorld = struct { if (self.context == null) { const ctx = try self.browser.env.createContext(frame, .{ .identity = &self.identity, - .identity_arena = self.arena, - .call_arena = self.call_arena, - .local_arena = self.local_arena, + .identity_arena = self.arena.allocator(), + .call_arena = self.call_arena.allocator(), + .local_arena = self.local_arena.allocator(), .debug_name = "IsolatedContext", }); self.context = ctx; diff --git a/src/cdp/Connection.zig b/src/cdp/Connection.zig index d86ea0362..aa5424337 100644 --- a/src/cdp/Connection.zig +++ b/src/cdp/Connection.zig @@ -374,7 +374,7 @@ fn handleMessage(self: *Connection, msg: WS.Message) !bool { .text, .binary => return self.pushCdp(msg.data), .ping => { const arena = try self.arena_pool.acquire(.tiny, "cdp ping"); - errdefer self.arena_pool.release(arena); + errdefer arena.release(); self.inbox.push(arena, .{ .ping = try arena.dupe(u8, msg.data) }); return true; }, @@ -396,13 +396,13 @@ fn handleMessage(self: *Connection, msg: WS.Message) !bool { fn pushCdp(self: *Connection, bytes: []const u8) !bool { // TODO: is it worth trying to pad this for the cost overhead of parsing? const arena = try self.arena_pool.acquire(bytes.len, "cdp data"); - errdefer self.arena_pool.release(arena); + errdefer arena.release(); const raw = try arena.dupe(u8, bytes); const input = std.json.parseFromSliceLeaky( CDP.InputMessage, - arena, + arena.allocator(), raw, .{ .ignore_unknown_fields = true }, ) catch { diff --git a/src/cdp/domains/accessibility.zig b/src/cdp/domains/accessibility.zig index 861205c5b..2bdda6aa5 100644 --- a/src/cdp/domains/accessibility.zig +++ b/src/cdp/domains/accessibility.zig @@ -73,7 +73,7 @@ fn getFullAXTree(cmd: *CDP.Command) !void { const node = try bc.node_registry.register(doc); const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); return cmd.sendResult(.{ .nodes = try bc.axnodeWriter(temp_arena, node, .{}) }, .{}); } @@ -93,7 +93,7 @@ fn queryAXTree(cmd: *CDP.Command) !void { const frame = bc.mainFrame() orelse return error.FrameNotLoaded; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); return cmd.sendResult(.{ .nodes = try bc.axnodeWriter(temp_arena, node, .{ .filter = .{ @@ -128,7 +128,7 @@ fn getPartialAXTree(cmd: *CDP.Command) !void { const frame = bc.mainFrame() orelse return error.FrameNotLoaded; const temp_arena = try frame.getArena(.medium, "AXNode"); - defer frame.releaseArena(temp_arena); + defer temp_arena.release(); // No filter: emit the full accessibility subtree rooted at the resolved // node, the same shape getFullAXTree produces for the document root. diff --git a/src/cdp/domains/dom.zig b/src/cdp/domains/dom.zig index 101ca6061..3b0505142 100644 --- a/src/cdp/domains/dom.zig +++ b/src/cdp/domains/dom.zig @@ -162,8 +162,8 @@ fn performSearch(cmd: *CDP.Command) !void { if (isXPathQuery(params.query)) { const arena = try frame.getArena(.medium, "DOM.performSearch"); - defer frame.releaseArena(arena); - const nodes = try xpath.searchAll(arena, root, params.query, frame); + defer arena.release(); + const nodes = try xpath.searchAll(arena.allocator(), root, params.query, frame); return finishSearch(cmd, bc, nodes); } @@ -655,13 +655,13 @@ fn fileFromDiskPath(path: []const u8, page: *Page) !*File { // Mirror File.init: a Blob and File sharing one reference-counted arena, // but read the bytes straight off disk into it (single copy, no JS parts). const arena = try page.getArena(.large, "File"); - errdefer page.releaseArena(arena); + errdefer arena.release(); - const data = try std.Io.Dir.cwd().readFileAlloc(lp.io, path, arena, .limited(MAX_FILE_BYTES)); + const data = try std.Io.Dir.cwd().readFileAlloc(lp.io, path, arena.allocator(), .limited(MAX_FILE_BYTES)); const stat = try std.Io.Dir.cwd().statFile(lp.io, path, .{}); const basename = std.fs.path.basename(path); - const file = try Factory.chainedWithAllocator(arena, .{ + const file = try Factory.chainedWithAllocator(arena.allocator(), .{ Blob{ ._rc = .{}, ._arena = arena, diff --git a/src/cdp/domains/fetch.zig b/src/cdp/domains/fetch.zig index 64a2b4320..5d25c4660 100644 --- a/src/cdp/domains/fetch.zig +++ b/src/cdp/domains/fetch.zig @@ -333,7 +333,7 @@ fn continueWithAuth(cmd: *CDP.Command) !void { // continueTransfer (which owns its failures). errdefer transfer.abortAuthChallenge(); transfer.updateCredentials(try std.fmt.allocPrintSentinel( - transfer.arena, + transfer.arena.allocator(), "{s}:{s}", .{ params.authChallengeResponse.username, diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index ee832ed95..deb77ffcd 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -469,7 +469,7 @@ pub const RequestWriter = struct { try SafeString.writeObjectField(jws, hdr.name); try jws.write(SafeString.wrap(hdr.value)); } - if (try request.getCookieString(transfer.arena)) |cookies| { + if (try request.getCookieString(transfer.arena.allocator())) |cookies| { try jws.objectField("Cookie"); try jws.write(cookies[0 .. cookies.len - 1]); } diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 4a24ea45f..f47b496db 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -524,7 +524,7 @@ pub fn frameCreated(bc: *CDP.BrowserContext, frame: *Frame) !void { const in_commit = bc.inCommit(); if (!in_commit) { - bc.cdp.browser.arena_pool.reset(bc.frame_arena, 1024 * 512); + _ = bc.cdp.frame_arena.reset(.{ .retain_with_limit = 1024 * 512 }); } for (bc.isolated_worlds.items) |isolated_world| { diff --git a/src/cookies.zig b/src/cookies.zig index ef493b52d..d888af183 100644 --- a/src/cookies.zig +++ b/src/cookies.zig @@ -33,9 +33,9 @@ pub fn loadFromFile(session: *Session, path: []const u8) void { fn _loadFromFile(session: *Session, path: []const u8) !void { const arena = try session.getArena(.medium, "Cookies.loadFromFile"); - defer session.releaseArena(arena); + defer arena.release(); - const content = std.Io.Dir.cwd().readFileAlloc(lp.io, path, arena, .limited(1024 * 1024)) catch |err| { + const content = std.Io.Dir.cwd().readFileAlloc(lp.io, path, arena.allocator(), .limited(1024 * 1024)) catch |err| { switch (err) { error.FileNotFound => log.debug(.app, "Cookie.readFile", .{ .path = path, .note = "file not found" }), else => log.err(.app, "Cookie.readFile", .{ .path = path, .err = err }), @@ -43,7 +43,7 @@ fn _loadFromFile(session: *Session, path: []const u8) !void { return; }; - const json_cookies = std.json.parseFromSliceLeaky([]const JsonCookie, arena, content, .{ + const json_cookies = std.json.parseFromSliceLeaky([]const JsonCookie, arena.allocator(), content, .{ .ignore_unknown_fields = true, }) catch |err| { log.err(.app, "Cookie.parseFile", .{ .path = path, .err = err }); diff --git a/src/lightpanda.zig b/src/lightpanda.zig index 06ceb23d7..53a5502e2 100644 --- a/src/lightpanda.zig +++ b/src/lightpanda.zig @@ -21,6 +21,7 @@ const std = @import("std"); pub const log = @import("log.zig"); pub const datetime = @import("datetime.zig"); pub const App = @import("App.zig"); +pub const Arena = @import("Arena.zig"); pub const Network = @import("network/Network.zig"); pub const Server = @import("Server.zig"); pub const Config = @import("Config.zig"); @@ -217,7 +218,7 @@ pub fn fetch(app: *App, browser: *Browser, urls: []const [:0]const u8, opts: Fet // One page per url. `PageHandle.frame()` always re-resolves the live frame, // so the handles stay valid across navigate / wait. The Runner's wait paths // already operate over every live page in the session. - var pages: std.ArrayList(Session.PageHandle) = try .initCapacity(session.arena, urls.len); + var pages: std.ArrayList(Session.PageHandle) = try .initCapacity(session.arena.allocator(), urls.len); for (urls) |url| { const page = try session.createPage(); const frame = page.frame().?; diff --git a/src/network/HttpClient.zig b/src/network/HttpClient.zig index 6d9d856ba..10229cff3 100644 --- a/src/network/HttpClient.zig +++ b/src/network/HttpClient.zig @@ -259,7 +259,7 @@ pub fn deinit(self: *Client) void { self.robots.deinit(); self.blocking_requests.deinit(self.allocator); self.transfers.deinit(self.allocator); - self.inbox.deinit(self.arena_pool); + self.inbox.deinit(); } // Look up a live transfer by its id. Returns null if the transfer has been @@ -440,7 +440,7 @@ fn processGraveyard(self: *Client) void { while (self.graveyard.popFirst()) |node| { const transfer: *Transfer = @fieldParentPtr("_node", node); const arena = transfer.arena; - self.arena_pool.release(arena); + arena.release(); } } @@ -547,7 +547,7 @@ pub fn newRequest(self: *Client, req: Request, owner: ?*Owner) anyerror!*Transfe var owned = req; errdefer { owned.headers.deinit(); - self.arena_pool.release(arena); + arena.release(); } if (owned.headers.headers == null) { @@ -836,7 +836,7 @@ fn pipeline(self: *Client, transfer: *Transfer, from: SubmitFrom) !void { .start => { if (self.network.web_bot_auth) |wba| { const authority = URL.getHost(transfer.req.url); - try wba.signRequest(transfer.arena, &transfer.req.headers, authority); + try wba.signRequest(transfer.arena.allocator(), &transfer.req.headers, authority); } if (self.serve_mode) { @@ -923,9 +923,9 @@ fn cacheLookup(self: *Client, transfer: *Transfer) !bool { const arena = transfer.arena; var iter = req.headers.iterator(); - const req_headers = try iter.collect(arena); + const req_headers = try iter.collect(arena.allocator()); - const cached = cache.get(arena, .{ + const cached = cache.get(arena.allocator(), .{ .url = req.url, .timestamp = lp.datetime.timestamp(.real), .request_headers = req_headers.items, @@ -957,10 +957,10 @@ fn cacheLookup(self: *Client, transfer: *Transfer) !bool { .last_modified = cached.metadata.last_modified, }); if (cached.metadata.etag) |etag| { - try req.headers.add(try std.fmt.allocPrintSentinel(arena, "If-None-Match: {s}", .{etag}, 0)); + try req.headers.add(try std.fmt.allocPrintSentinel(arena.allocator(), "If-None-Match: {s}", .{etag}, 0)); } if (cached.metadata.last_modified) |lm| { - try req.headers.add(try std.fmt.allocPrintSentinel(arena, "If-Modified-Since: {s}", .{lm}, 0)); + try req.headers.add(try std.fmt.allocPrintSentinel(arena.allocator(), "If-Modified-Since: {s}", .{lm}, 0)); } transfer._cache_intent = .{ .revalidate = cached }; return false; @@ -982,7 +982,7 @@ fn cacheRevalidated(self: *Client, transfer: *Transfer) !bool { const stale = transfer._cache_intent.revalidate; transfer._cache_intent = .none; - cache.renew(transfer.arena, .{ + cache.renew(transfer.arena.allocator(), .{ .url = transfer._cache_key, .timestamp = lp.datetime.timestamp(.real), .headers = transfer.res.headers, @@ -1021,7 +1021,7 @@ fn cacheStore(self: *Client, transfer: *Transfer) void { const vary = findHeader(headers, "vary"); const maybe_cm = Cache.tryCache( - arena, + arena.allocator(), lp.datetime.timestamp(.real), transfer._cache_key, rh.status, @@ -1051,7 +1051,7 @@ fn cacheStore(self: *Client, transfer: *Transfer) void { .name = arena.dupe(u8, hdr.name) catch return, .value = arena.dupe(u8, hdr.value) catch return, }; - vary_headers.append(arena, owned) catch return; + vary_headers.append(arena.allocator(), owned) catch return; } } } @@ -1250,7 +1250,7 @@ fn drainInbox(self: *Client, mode: DrainMode) !void { .sync_wait => self.inbox.popIf(allowDuringSyncWait), } orelse return; - defer msg.deinit(self.arena_pool); + defer msg.deinit(); switch (msg.payload) { .cdp => |*c| cdp.onMessage(c) catch |err| { @@ -1873,7 +1873,7 @@ pub const Owner = struct { pub const Transfer = struct { id: u32 = 0, - arena: Allocator, + arena: *lp.Arena, owner: ?*Owner, owner_node: std.DoublyLinkedList.Node = .{}, @@ -2284,7 +2284,7 @@ pub const Transfer = struct { // error_callback fires from the dispatcher, never from the caller's // stack. pub fn failAsync(self: *Transfer, err: anyerror) void { - self._events.append(self.arena, .{ .err = err }) catch { + self._events.append(self.arena.allocator(), .{ .err = err }) catch { // Can't buffer (OOM): failing inline beats losing the error. return self.abort(err); }; @@ -2327,7 +2327,7 @@ pub const Transfer = struct { // http_status reflects the final response only. Internal requests // (robots.txt) are tracked by the robots_* metrics instead. if (self.res.stream.started) { - try self._events.append(self.arena, .done); + try self._events.append(self.arena.allocator(), .done); self.scheduleDispatch(); return; } @@ -2338,7 +2338,7 @@ pub const Transfer = struct { lp.metrics.http_response_size_bytes.observe(body.len); } - try self._events.ensureUnusedCapacity(self.arena, 4); + try self._events.ensureUnusedCapacity(self.arena.allocator(), 4); self._events.appendAssumeCapacity(.start); self._events.appendAssumeCapacity(.header); if (body.len > 0) { @@ -2435,7 +2435,7 @@ pub const Transfer = struct { self.res.header.?.url = (try arena.dupeZ(u8, std.mem.span(self.res.header.?.url))).ptr; var it = HeaderIterator{ .curl = .{ .conn = conn } }; - const headers = try it.collect(arena); + const headers = try it.collect(arena.allocator()); self.res.headers = headers.items; if (self.req.cookie_jar) |jar| { @@ -2479,7 +2479,7 @@ pub const Transfer = struct { try conn.setHeaders(&header_list); // Add cookies from cookie jar. - if (try self.req.getCookieString(self.arena)) |cookies| { + if (try self.req.getCookieString(self.arena.allocator())) |cookies| { try conn.setCookies(@ptrCast(cookies.ptr)); } @@ -2594,7 +2594,7 @@ pub const Transfer = struct { break :blk ""; } - const resolved = try URL.resolve(arena, base, location, .{}); + const resolved = try URL.resolve(arena.allocator(), base, location, .{}); // RFC 7231 §7.1.2: if the Location value has no fragment, the redirect // inherits the fragment from the URI used to generate the request. @@ -2603,7 +2603,7 @@ pub const Transfer = struct { if (URL.getHash(resolved).len == 0) { const original_hash = URL.getHash(transfer.req.url); if (original_hash.len != 0) { - break :blk try std.mem.joinZ(arena, "", &.{ resolved, original_hash }); + break :blk try std.mem.joinZ(arena.allocator(), "", &.{ resolved, original_hash }); } } break :blk resolved; @@ -2705,7 +2705,7 @@ pub const Transfer = struct { res.callback_error = error.ResponseTooLarge; return http.writefunc_error; } - res.buffer.ensureTotalCapacity(transfer.arena, cl) catch {}; + res.buffer.ensureTotalCapacity(transfer.arena.allocator(), cl) catch {}; } } @@ -2739,7 +2739,7 @@ pub const Transfer = struct { return http.writefunc_error; } - res.buffer.appendSlice(transfer.arena, chunk) catch |err| { + res.buffer.appendSlice(transfer.arena.allocator(), chunk) catch |err| { res.callback_error = err; return http.writefunc_error; }; @@ -2756,16 +2756,16 @@ pub const Transfer = struct { if (res.stream.started == false) { // we haven't delivered the start/header events yet try self.materializeResponse(conn); - try self._events.ensureUnusedCapacity(self.arena, 3); + try self._events.ensureUnusedCapacity(self.arena.allocator(), 3); self._events.appendAssumeCapacity(.start); self._events.appendAssumeCapacity(.header); res.stream.started = true; } // append the data to whatever data we already have (but haven't delivered) - try res.buffer.appendSlice(self.arena, chunk); + try res.buffer.appendSlice(self.arena.allocator(), chunk); if (res.stream.data_queued == false) { res.stream.data_queued = true; - try self._events.append(self.arena, .stream_data); + try self._events.append(self.arena.allocator(), .stream_data); } switch (self.state) { @@ -3037,7 +3037,7 @@ const Synthetic = struct { var content_type: []const u8 = ""; if (std.mem.startsWith(u8, url, "data:")) { - const parsed = try data_url.parse(arena, url); + const parsed = try data_url.parse(arena.allocator(), url); content_type = parsed.content_type; body = parsed.body; } else { @@ -3085,32 +3085,38 @@ test "HttpClient: isFetchInterceptionMethod rejects unrelated methods" { } test "HttpClient: allowDuringSyncWait allows ping/close/disconnect" { + const test_arena = try testing.test_app.arena_pool.acquire(.tiny, "HttpClient test"); + defer test_arena.release(); + var ping_msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .ping = "" }, }; try testing.expect(allowDuringSyncWait(&ping_msg)); var close_msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .close, }; try testing.expect(allowDuringSyncWait(&close_msg)); var disconnect_msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .disconnect = null }, }; try testing.expect(allowDuringSyncWait(&disconnect_msg)); var disconnect_err_msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .disconnect = error.PeerClosed }, }; try testing.expect(allowDuringSyncWait(&disconnect_err_msg)); } test "HttpClient: allowDuringSyncWait allows only Fetch interception CDP methods" { + const test_arena = try testing.test_app.arena_pool.acquire(.tiny, "HttpClient test"); + defer test_arena.release(); + var raw_buf: [16]u8 = undefined; inline for ([_][]const u8{ @@ -3120,7 +3126,7 @@ test "HttpClient: allowDuringSyncWait allows only Fetch interception CDP methods "Fetch.continueWithAuth", }) |method| { var msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .cdp = .{ .raw = &raw_buf, .input = .{ .method = method }, @@ -3131,6 +3137,9 @@ test "HttpClient: allowDuringSyncWait allows only Fetch interception CDP methods } test "HttpClient: allowDuringSyncWait denies non-Fetch CDP methods" { + const test_arena = try testing.test_app.arena_pool.acquire(.tiny, "HttpClient test"); + defer test_arena.release(); + var raw_buf: [16]u8 = undefined; inline for ([_][]const u8{ @@ -3142,7 +3151,7 @@ test "HttpClient: allowDuringSyncWait denies non-Fetch CDP methods" { "", }) |method| { var msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .cdp = .{ .raw = &raw_buf, .input = .{ .method = method }, @@ -3153,6 +3162,9 @@ test "HttpClient: allowDuringSyncWait denies non-Fetch CDP methods" { } test "HttpClient: isSyncWaitInterrupt matches teardown methods, close and disconnect" { + const test_arena = try testing.test_app.arena_pool.acquire(.tiny, "HttpClient test"); + defer test_arena.release(); + var raw_buf: [16]u8 = undefined; inline for ([_][]const u8{ @@ -3161,7 +3173,7 @@ test "HttpClient: isSyncWaitInterrupt matches teardown methods, close and discon "Page.close", }) |method| { var msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .cdp = .{ .raw = &raw_buf, .input = .{ .method = method }, @@ -3170,15 +3182,18 @@ test "HttpClient: isSyncWaitInterrupt matches teardown methods, close and discon try testing.expect(isSyncWaitInterrupt(&msg)); } - var close_msg = Inbox.Message{ .arena = testing.allocator, .payload = .close }; + var close_msg = Inbox.Message{ .arena = test_arena, .payload = .close }; try testing.expect(isSyncWaitInterrupt(&close_msg)); - var disconnect_msg = Inbox.Message{ .arena = testing.allocator, .payload = .{ .disconnect = null } }; + var disconnect_msg = Inbox.Message{ .arena = test_arena, .payload = .{ .disconnect = null } }; try testing.expect(isSyncWaitInterrupt(&disconnect_msg)); } test "HttpClient: isSyncWaitInterrupt ignores ping and non-teardown CDP methods" { - var ping_msg = Inbox.Message{ .arena = testing.allocator, .payload = .{ .ping = "" } }; + const test_arena = try testing.test_app.arena_pool.acquire(.tiny, "HttpClient test"); + defer test_arena.release(); + + var ping_msg = Inbox.Message{ .arena = test_arena, .payload = .{ .ping = "" } }; try testing.expect(!isSyncWaitInterrupt(&ping_msg)); var raw_buf: [16]u8 = undefined; @@ -3190,7 +3205,7 @@ test "HttpClient: isSyncWaitInterrupt ignores ping and non-teardown CDP methods" "", }) |method| { var msg = Inbox.Message{ - .arena = testing.allocator, + .arena = test_arena, .payload = .{ .cdp = .{ .raw = &raw_buf, .input = .{ .method = method }, diff --git a/src/network/RobotsGate.zig b/src/network/RobotsGate.zig index 10cc175a8..a4d025c54 100644 --- a/src/network/RobotsGate.zig +++ b/src/network/RobotsGate.zig @@ -53,7 +53,7 @@ pub fn deinit(self: *RobotsGate) void { pub fn check(self: *RobotsGate, transfer: *Transfer) !Result { const url = transfer.req.url; - const robots_url = try URL.getRobotsUrl(transfer.arena, url); + const robots_url = try URL.getRobotsUrl(transfer.arena.allocator(), url); if (self.network.robot_store.get(robots_url)) |robot_entry| { switch (robot_entry) { @@ -104,7 +104,7 @@ fn fetchThenResume(self: *RobotsGate, robots_url: [:0]const u8, transfer: *Trans // fetch's callbacks must survive that. The arena is released by // whichever terminal callback fires (done / error / shutdown). const arena = try client.arena_pool.acquire(.small, "RobotsGate.RobotsContext"); - errdefer client.arena_pool.release(arena); + errdefer arena.release(); const owned_url = try arena.dupeZ(u8, robots_url); const robots_ctx = try arena.create(RobotsContext); @@ -200,7 +200,7 @@ fn flushPendingShutdown(self: *RobotsGate, robots_url: []const u8) void { const RobotsContext = struct { gate: *RobotsGate, - arena: Allocator, + arena: *lp.Arena, arena_pool: *ArenaPool, robots_url: [:0]const u8, buffer: std.ArrayList(u8), @@ -214,7 +214,7 @@ const RobotsContext = struct { } lp.metrics.robots_status.incr(http.statusCategory(self.status)); if (transfer.getContentLength()) |cl| { - try self.buffer.ensureTotalCapacity(self.arena, cl); + try self.buffer.ensureTotalCapacity(self.arena.allocator(), cl); } return .proceed; } @@ -222,7 +222,7 @@ const RobotsContext = struct { fn dataCallback(transfer: *Transfer, data: []const u8) anyerror!void { const self: *RobotsContext = @ptrCast(@alignCast(transfer.req.ctx)); if (self.status == 200) { - try self.buffer.appendSlice(self.arena, data); + try self.buffer.appendSlice(self.arena.allocator(), data); } } @@ -277,17 +277,15 @@ const RobotsContext = struct { log.debug(.http, "robots fetch shutdown", .{}); const gate = self.gate; - const pool = self.arena_pool; const arena = self.arena; gate.flushPendingShutdown(self.robots_url); - pool.release(arena); + arena.release(); } fn resolve(self: *RobotsContext) void { const gate = self.gate; - const pool = self.arena_pool; const arena = self.arena; gate.flushPending(self.robots_url); - pool.release(arena); + arena.release(); } }; diff --git a/src/network/header_parser.zig b/src/network/header_parser.zig index 54e672d03..c1842ee8c 100644 --- a/src/network/header_parser.zig +++ b/src/network/header_parser.zig @@ -17,7 +17,6 @@ // along with this program. If not, see . const std = @import("std"); -const builtin = @import("builtin"); /// Block size of the CPU. const block_size = @sizeOf(usize);