mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 09:35:59 -04:00
Add arena buckets to ArenaPool
ArenaPool previously maintained up to 512 16KB buckets. The 16KB retention is small for things like XHR and scripts, but increasing it to something more reasonably, like 128KB, would use up to 8x more memory. This commit adds 4 buckets: 1KB, 4KB, 16KB and 128KB. Callers can request a tiny, small, medium or large bucket. We end up using less memory peak memory and less allocations. Furthermore, callers can request a specific size. This is particularly useful for WebSocket or Blob where the size could vary greatly (so we'd likely default to a large bucket), but that could needlessly use up a large arena. The bucket sizes were derived from analyzing allocations. A significant number of allocations were very small. Things like ScheduleCallback and FinalizerCallback are always less than 1K and can be generated in the thousands. The 16KB retention was wasteful in these cases...better to have a large number of 1K pools, so that we can have a handful of very large buffers.
This commit is contained in:
@@ -69,7 +69,7 @@ pub fn init(allocator: Allocator, config: *const Config) !*App {
|
||||
app.telemetry = try Telemetry.init(app, config.mode);
|
||||
errdefer app.telemetry.deinit(allocator);
|
||||
|
||||
app.arena_pool = ArenaPool.init(allocator, 512, 1024 * 16);
|
||||
app.arena_pool = ArenaPool.init(allocator, .{});
|
||||
errdefer app.arena_pool.deinit();
|
||||
|
||||
return app;
|
||||
|
||||
@@ -27,33 +27,52 @@ const ArenaPool = @This();
|
||||
|
||||
const IS_DEBUG = builtin.mode == .Debug;
|
||||
|
||||
allocator: Allocator,
|
||||
retain_bytes: usize,
|
||||
free_list_len: u16 = 0,
|
||||
free_list: ?*Entry = null,
|
||||
free_list_max: u16,
|
||||
entry_pool: std.heap.MemoryPool(Entry),
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
// Debug mode: track acquire/release counts per debug name to detect leaks and double-frees
|
||||
_leak_track: if (IS_DEBUG) std.StringHashMapUnmanaged(isize) else void = if (IS_DEBUG) .empty else {},
|
||||
pub const BucketSize = enum { tiny, small, medium, large };
|
||||
|
||||
const Bucket = struct {
|
||||
free_list: ?*Entry = 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 DebugInfo = struct {
|
||||
debug: []const u8 = "",
|
||||
pub const Config = struct {
|
||||
tiny: Config.Bucket = .{ .max = 512, .retain = 1024 },
|
||||
small: Config.Bucket = .{ .max = 128, .retain = 4 * 1024 },
|
||||
medium: Config.Bucket = .{ .max = 64, .retain = 16 * 1024 },
|
||||
large: Config.Bucket = .{ .max = 32, .retain = 128 * 1024 },
|
||||
|
||||
const Bucket = struct {
|
||||
max: u16,
|
||||
retain: usize,
|
||||
};
|
||||
};
|
||||
|
||||
pub fn init(allocator: Allocator, free_list_max: u16, retain_bytes: usize) ArenaPool {
|
||||
tiny: Bucket,
|
||||
small: Bucket,
|
||||
medium: Bucket,
|
||||
large: Bucket,
|
||||
allocator: Allocator,
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
entry_pool: std.heap.MemoryPool(Entry),
|
||||
|
||||
_leak_track: if (IS_DEBUG) std.StringHashMapUnmanaged(isize) else void = if (IS_DEBUG) .empty else {},
|
||||
|
||||
pub fn init(allocator: Allocator, config: Config) ArenaPool {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.free_list_max = free_list_max,
|
||||
.retain_bytes = retain_bytes,
|
||||
.entry_pool = .init(allocator),
|
||||
._leak_track = if (IS_DEBUG) .empty else {},
|
||||
.tiny = .{ .free_list_max = config.tiny.max, .retain_bytes = config.tiny.retain },
|
||||
.small = .{ .free_list_max = config.small.max, .retain_bytes = config.small.retain },
|
||||
.medium = .{ .free_list_max = config.medium.max, .retain_bytes = config.medium.retain },
|
||||
.large = .{ .free_list_max = config.large.max, .retain_bytes = config.large.retain },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,24 +92,49 @@ pub fn deinit(self: *ArenaPool) void {
|
||||
self._leak_track.deinit(self.allocator);
|
||||
}
|
||||
|
||||
var entry = self.free_list;
|
||||
while (entry) |e| {
|
||||
entry = e.next;
|
||||
e.arena.deinit();
|
||||
// Free all arenas in all buckets
|
||||
inline for (&[_]*Bucket{ &self.tiny, &self.small, &self.medium, &self.large }) |bucket| {
|
||||
var entry = bucket.free_list;
|
||||
while (entry) |e| {
|
||||
entry = e.next;
|
||||
e.arena.deinit();
|
||||
}
|
||||
}
|
||||
self.entry_pool.deinit();
|
||||
}
|
||||
|
||||
pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator {
|
||||
// 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 {
|
||||
const bucket = blk: {
|
||||
const T = @TypeOf(size_or_bucket);
|
||||
if (T == BucketSize or T == @TypeOf(.enum_literal)) {
|
||||
break :blk switch (@as(BucketSize, size_or_bucket)) {
|
||||
.tiny => &self.tiny,
|
||||
.small => &self.small,
|
||||
.medium => &self.medium,
|
||||
.large => &self.large,
|
||||
};
|
||||
}
|
||||
if (T == usize or T == comptime_int) {
|
||||
if (size_or_bucket <= self.tiny.retain_bytes) break :blk &self.tiny;
|
||||
if (size_or_bucket <= self.small.retain_bytes) break :blk &self.small;
|
||||
if (size_or_bucket <= self.medium.retain_bytes) break :blk &self.medium;
|
||||
break :blk &self.large;
|
||||
}
|
||||
@compileError("acquire expects BucketSize or usize, got " ++ @typeName(T));
|
||||
};
|
||||
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
if (self.free_list) |entry| {
|
||||
self.free_list = entry.next;
|
||||
self.free_list_len -= 1;
|
||||
if (bucket.free_list) |entry| {
|
||||
bucket.free_list = entry.next;
|
||||
bucket.free_list_len -= 1;
|
||||
if (IS_DEBUG) {
|
||||
entry.debug = dbg.debug;
|
||||
const gop = try self._leak_track.getOrPut(self.allocator, dbg.debug);
|
||||
entry.debug = debug;
|
||||
const gop = try self._leak_track.getOrPut(self.allocator, debug);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = 0;
|
||||
}
|
||||
@@ -102,12 +146,13 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator {
|
||||
const entry = try self.entry_pool.create();
|
||||
entry.* = .{
|
||||
.next = null,
|
||||
.bucket = bucket,
|
||||
.debug = if (IS_DEBUG) debug else {},
|
||||
.arena = ArenaAllocator.init(self.allocator),
|
||||
.debug = if (IS_DEBUG) dbg.debug else {},
|
||||
};
|
||||
|
||||
if (IS_DEBUG) {
|
||||
const gop = try self._leak_track.getOrPut(self.allocator, dbg.debug);
|
||||
const gop = try self._leak_track.getOrPut(self.allocator, debug);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = 0;
|
||||
}
|
||||
@@ -116,12 +161,14 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator {
|
||||
return entry.arena.allocator();
|
||||
}
|
||||
|
||||
// Universal release - determines bucket from the Entry automatically
|
||||
pub fn release(self: *ArenaPool, allocator: Allocator) void {
|
||||
const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
const entry: *Entry = @fieldParentPtr("arena", arena);
|
||||
const bucket = entry.bucket;
|
||||
|
||||
// Reset the arena before acquiring the lock to minimize lock hold time
|
||||
_ = arena.reset(.{ .retain_with_limit = self.retain_bytes });
|
||||
_ = arena.reset(.{ .retain_with_limit = bucket.retain_bytes });
|
||||
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
@@ -139,105 +186,113 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void {
|
||||
}
|
||||
}
|
||||
|
||||
const free_list_len = self.free_list_len;
|
||||
if (free_list_len == self.free_list_max) {
|
||||
if (bucket.free_list_len >= bucket.free_list_max) {
|
||||
arena.deinit();
|
||||
self.entry_pool.destroy(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
entry.next = self.free_list;
|
||||
self.free_list_len = free_list_len + 1;
|
||||
self.free_list = entry;
|
||||
entry.next = bucket.free_list;
|
||||
bucket.free_list = entry;
|
||||
bucket.free_list_len += 1;
|
||||
}
|
||||
|
||||
pub fn reset(_: *const ArenaPool, allocator: Allocator, retain: usize) void {
|
||||
const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
_ = arena.reset(.{ .retain_with_limit = retain });
|
||||
}
|
||||
|
||||
pub fn resetRetain(_: *const ArenaPool, allocator: Allocator) void {
|
||||
const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr));
|
||||
_ = arena.reset(.retain_capacity);
|
||||
}
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
test "arena pool - basic acquire and use" {
|
||||
var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16);
|
||||
test "ArenaPool: basic acquire and release" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{});
|
||||
defer pool.deinit();
|
||||
|
||||
const alloc = try pool.acquire(.{ .debug = "test" });
|
||||
const buf = try alloc.alloc(u8, 64);
|
||||
@memset(buf, 0xAB);
|
||||
try testing.expectEqual(@as(u8, 0xAB), buf[0]);
|
||||
const tiny = try pool.acquire(.tiny, "test-tiny");
|
||||
const medium = try pool.acquire(.medium, "test-medium");
|
||||
const large = try pool.acquire(.large, "test-large");
|
||||
|
||||
pool.release(alloc);
|
||||
// All three must be distinct arenas
|
||||
try testing.expect(tiny.ptr != medium.ptr);
|
||||
try testing.expect(medium.ptr != large.ptr);
|
||||
|
||||
_ = 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);
|
||||
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len);
|
||||
try testing.expectEqual(1, pool.medium.free_list_len);
|
||||
try testing.expectEqual(1, pool.large.free_list_len);
|
||||
}
|
||||
|
||||
test "arena pool - reuse entry after release" {
|
||||
var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16);
|
||||
test "ArenaPool: reuse from correct bucket" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{});
|
||||
defer pool.deinit();
|
||||
|
||||
const alloc1 = try pool.acquire(.{ .debug = "test" });
|
||||
try testing.expectEqual(@as(u16, 0), pool.free_list_len);
|
||||
const tiny1 = try pool.acquire(.tiny, "test");
|
||||
pool.release(tiny1);
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len);
|
||||
|
||||
pool.release(alloc1);
|
||||
try testing.expectEqual(@as(u16, 1), pool.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);
|
||||
|
||||
// The same entry should be returned from the free list.
|
||||
const alloc2 = try pool.acquire(.{ .debug = "test" });
|
||||
try testing.expectEqual(@as(u16, 0), pool.free_list_len);
|
||||
try testing.expectEqual(alloc1.ptr, alloc2.ptr);
|
||||
// acquire with .medium should NOT get the tiny arena
|
||||
const medium = try pool.acquire(.medium, "test-medium");
|
||||
try testing.expect(medium.ptr != tiny2.ptr);
|
||||
|
||||
pool.release(alloc2);
|
||||
pool.release(tiny2);
|
||||
pool.release(medium);
|
||||
}
|
||||
|
||||
test "arena pool - multiple concurrent arenas" {
|
||||
var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16);
|
||||
test "ArenaPool: respects per-bucket max limits" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{
|
||||
.tiny = .{ .max = 1, .retain = 1024 },
|
||||
.medium = .{ .max = 2, .retain = 1024 },
|
||||
.large = .{ .max = 1, .retain = 1024 },
|
||||
});
|
||||
defer pool.deinit();
|
||||
|
||||
const a1 = try pool.acquire(.{ .debug = "test1" });
|
||||
const a2 = try pool.acquire(.{ .debug = "test2" });
|
||||
const a3 = try pool.acquire(.{ .debug = "test3" });
|
||||
// Acquire 3 tiny arenas
|
||||
const t1 = try pool.acquire(.tiny, "t1");
|
||||
const t2 = try pool.acquire(.tiny, "t2");
|
||||
const t3 = try pool.acquire(.tiny, "t3");
|
||||
|
||||
// All three must be distinct arenas.
|
||||
try testing.expect(a1.ptr != a2.ptr);
|
||||
try testing.expect(a2.ptr != a3.ptr);
|
||||
try testing.expect(a1.ptr != a3.ptr);
|
||||
// Release all 3, but only 1 should be kept (tiny_max = 1)
|
||||
pool.release(t1);
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len);
|
||||
pool.release(t2);
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t2 discarded
|
||||
pool.release(t3);
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t3 discarded
|
||||
|
||||
_ = try a1.alloc(u8, 16);
|
||||
_ = try a2.alloc(u8, 32);
|
||||
_ = try a3.alloc(u8, 48);
|
||||
// Acquire 3 medium arenas
|
||||
const m1 = try pool.acquire(.medium, "m1");
|
||||
const m2 = try pool.acquire(.medium, "m2");
|
||||
const m3 = try pool.acquire(.medium, "m3");
|
||||
|
||||
pool.release(a1);
|
||||
pool.release(a2);
|
||||
pool.release(a3);
|
||||
|
||||
try testing.expectEqual(@as(u16, 3), pool.free_list_len);
|
||||
// Release all 3, but only 2 should be kept (medium_max = 2)
|
||||
pool.release(m1);
|
||||
pool.release(m2);
|
||||
pool.release(m3);
|
||||
try testing.expectEqual(2, pool.medium.free_list_len);
|
||||
}
|
||||
|
||||
test "arena pool - free list respects max limit" {
|
||||
// Cap the free list at 1 so the second release discards its arena.
|
||||
var pool = ArenaPool.init(testing.allocator, 1, 1024 * 16);
|
||||
test "ArenaPool: reset clears memory without releasing" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{});
|
||||
defer pool.deinit();
|
||||
|
||||
const a1 = try pool.acquire(.{ .debug = "test1" });
|
||||
const a2 = try pool.acquire(.{ .debug = "test2" });
|
||||
|
||||
pool.release(a1);
|
||||
try testing.expectEqual(@as(u16, 1), pool.free_list_len);
|
||||
|
||||
// The free list is full; a2's arena should be destroyed, not queued.
|
||||
pool.release(a2);
|
||||
try testing.expectEqual(@as(u16, 1), pool.free_list_len);
|
||||
}
|
||||
|
||||
test "arena pool - reset clears memory without releasing" {
|
||||
var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16);
|
||||
defer pool.deinit();
|
||||
|
||||
const alloc = try pool.acquire(.{ .debug = "test" });
|
||||
const alloc = try pool.acquire(.medium, "test");
|
||||
|
||||
const buf = try alloc.alloc(u8, 128);
|
||||
@memset(buf, 0xFF);
|
||||
@@ -246,7 +301,7 @@ test "arena pool - reset clears memory without releasing" {
|
||||
pool.reset(alloc, 0);
|
||||
|
||||
// The free list must stay empty; the allocator was not released.
|
||||
try testing.expectEqual(@as(u16, 0), pool.free_list_len);
|
||||
try testing.expectEqual(0, pool.medium.free_list_len);
|
||||
|
||||
// Allocating again through the same arena must still work.
|
||||
const buf2 = try alloc.alloc(u8, 64);
|
||||
@@ -256,18 +311,60 @@ test "arena pool - reset clears memory without releasing" {
|
||||
pool.release(alloc);
|
||||
}
|
||||
|
||||
test "arena pool - deinit with entries in free list" {
|
||||
test "ArenaPool: deinit with entries in free list" {
|
||||
// Verifies that deinit properly cleans up free-listed arenas (no leaks
|
||||
// detected by the test allocator).
|
||||
var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16);
|
||||
var pool = ArenaPool.init(testing.allocator, .{});
|
||||
|
||||
const a1 = try pool.acquire(.{ .debug = "test1" });
|
||||
const a2 = try pool.acquire(.{ .debug = "test2" });
|
||||
const a1 = try pool.acquire(.tiny, "test1");
|
||||
const a2 = try pool.acquire(.medium, "test2");
|
||||
_ = try a1.alloc(u8, 256);
|
||||
_ = try a2.alloc(u8, 512);
|
||||
pool.release(a1);
|
||||
pool.release(a2);
|
||||
try testing.expectEqual(@as(u16, 2), pool.free_list_len);
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len);
|
||||
try testing.expectEqual(1, pool.medium.free_list_len);
|
||||
|
||||
pool.deinit();
|
||||
}
|
||||
|
||||
test "ArenaPool: small bucket" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{
|
||||
.small = .{ .max = 2, .retain = 4 * 1024 },
|
||||
});
|
||||
defer pool.deinit();
|
||||
|
||||
const s1 = try pool.acquire(.small, "s1");
|
||||
const s2 = try pool.acquire(.small, "s2");
|
||||
const s3 = try pool.acquire(.small, "s3");
|
||||
|
||||
pool.release(s1);
|
||||
pool.release(s2);
|
||||
pool.release(s3);
|
||||
|
||||
try testing.expectEqual(2, pool.small.free_list_len);
|
||||
}
|
||||
|
||||
test "ArenaPool: size-based acquire" {
|
||||
var pool = ArenaPool.init(testing.allocator, .{});
|
||||
defer pool.deinit();
|
||||
|
||||
// <= 1KB -> tiny
|
||||
const a = try pool.acquire(500, "fits-tiny");
|
||||
// <= 4KB -> small
|
||||
const b = try pool.acquire(2000, "fits-small");
|
||||
// <= 16KB -> medium
|
||||
const c = try pool.acquire(8000, "fits-medium");
|
||||
// > 16KB -> large
|
||||
const d = try pool.acquire(20000, "fits-large");
|
||||
|
||||
pool.release(a);
|
||||
pool.release(b);
|
||||
pool.release(c);
|
||||
pool.release(d);
|
||||
|
||||
try testing.expectEqual(1, pool.tiny.free_list_len);
|
||||
try testing.expectEqual(1, pool.small.free_list_len);
|
||||
try testing.expectEqual(1, pool.medium.free_list_len);
|
||||
try testing.expectEqual(1, pool.large.free_list_len);
|
||||
}
|
||||
|
||||
@@ -374,7 +374,8 @@ fn serveFromCache(req: Request, cached: *const CachedResponse) !void {
|
||||
fn processRequest(self: *Client, req: Request) !void {
|
||||
if (self.network.cache) |*cache| {
|
||||
if (req.method == .GET) {
|
||||
const arena = try self.network.app.arena_pool.acquire(.{ .debug = "HttpClient.processRequest.cache" });
|
||||
// cache is only used to read the meta data
|
||||
const arena = try self.network.app.arena_pool.acquire(.small, "HttpClient.cache");
|
||||
defer self.network.app.arena_pool.release(arena);
|
||||
|
||||
var iter = req.headers.iterator();
|
||||
|
||||
@@ -248,7 +248,7 @@ pub fn init(self: *Page, frame_id: u32, session: *Session, parent: ?*Page) !void
|
||||
log.debug(.page, "page.init", .{});
|
||||
}
|
||||
|
||||
const call_arena = try session.getArena(.{ .debug = "call_arena" });
|
||||
const call_arena = try session.getArena(.medium, "call_arena");
|
||||
errdefer session.releaseArena(call_arena);
|
||||
|
||||
const factory = &session.factory;
|
||||
@@ -429,8 +429,8 @@ pub fn headersForRequest(self: *Page, headers: *HttpClient.Headers) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getArena(self: *Page, comptime opts: Session.GetArenaOpts) !Allocator {
|
||||
return self._session.getArena(opts);
|
||||
pub fn getArena(self: *Page, size_or_bucket: anytype, debug: []const u8) !Allocator {
|
||||
return self._session.getArena(size_or_bucket, debug);
|
||||
}
|
||||
|
||||
pub fn releaseArena(self: *Page, allocator: Allocator) void {
|
||||
@@ -510,7 +510,7 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi
|
||||
log.warn(.js, "invalid blob", .{ .url = request_url });
|
||||
return error.BlobNotFound;
|
||||
};
|
||||
const parse_arena = try self.getArena(.{ .debug = "Page.parseBlob" });
|
||||
const parse_arena = try self.getArena(.medium, "Page.parseBlob");
|
||||
defer self.releaseArena(parse_arena);
|
||||
var parser = Parser.init(parse_arena, self.document.asNode(), self);
|
||||
parser.parse(blob._slice);
|
||||
@@ -619,7 +619,7 @@ pub fn scheduleNavigation(self: *Page, request_url: []const u8, opts: NavigateOp
|
||||
if (self.canScheduleNavigation(std.meta.activeTag(nt)) == false) {
|
||||
return;
|
||||
}
|
||||
const arena = try self._session.getArena(.{ .debug = "scheduleNavigation" });
|
||||
const arena = try self._session.getArena(.small, "scheduleNavigation");
|
||||
errdefer self._session.releaseArena(arena);
|
||||
return self.scheduleNavigationWithArena(arena, request_url, opts, nt);
|
||||
}
|
||||
@@ -1022,7 +1022,7 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
|
||||
});
|
||||
};
|
||||
|
||||
const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
|
||||
const parse_arena = try self.getArena(.medium, "Page.parse");
|
||||
defer self.releaseArena(parse_arena);
|
||||
|
||||
var parser = Parser.init(parse_arena, self.document.asNode(), self);
|
||||
@@ -3568,7 +3568,7 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form
|
||||
// I don't think this is technically correct, but FormData handles it ok
|
||||
const form_data = try FormData.init(form, submitter_, self);
|
||||
|
||||
const arena = try self._session.getArena(.{ .debug = "submitForm" });
|
||||
const arena = try self._session.getArena(.medium, "submitForm");
|
||||
errdefer self._session.releaseArena(arena);
|
||||
|
||||
const encoding = form_element.getAttributeSafe(comptime .wrap("enctype"));
|
||||
|
||||
@@ -249,7 +249,7 @@ fn _tick(self: *Runner, comptime is_cdp: bool, opts: TickOpts) !CDPTickResult {
|
||||
}
|
||||
|
||||
pub fn waitForSelector(self: *Runner, selector: [:0]const u8, timeout_ms: u32) !*Node.Element {
|
||||
const arena = try self.session.getArena(.{ .debug = "Runner.waitForSelector" });
|
||||
const arena = try self.session.getArena(.small, "Runner.waitForSelector");
|
||||
defer self.session.releaseArena(arena);
|
||||
|
||||
var timer = try std.time.Timer.start();
|
||||
|
||||
@@ -188,7 +188,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e
|
||||
var handover = false;
|
||||
const page = self.page;
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "addFromElement" });
|
||||
const arena = try page.getArena(.large, "SM.addFromElement");
|
||||
errdefer if (!handover) {
|
||||
page.releaseArena(arena);
|
||||
};
|
||||
@@ -369,7 +369,7 @@ pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const
|
||||
errdefer _ = self.imported_modules.remove(url);
|
||||
|
||||
const page = self.page;
|
||||
const arena = try page.getArena(.{ .debug = "preloadImport" });
|
||||
const arena = try page.getArena(.large, "SM.preloadImport");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const script = try arena.create(Script);
|
||||
@@ -469,7 +469,7 @@ pub fn waitForImport(self: *ScriptManager, url: [:0]const u8) !ModuleSource {
|
||||
|
||||
pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.Callback, cb_data: *anyopaque, referrer: []const u8) !void {
|
||||
const page = self.page;
|
||||
const arena = try page.getArena(.{ .debug = "getAsyncImport" });
|
||||
const arena = try page.getArena(.large, "SM.getAsyncImport");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const script = try arena.create(Script);
|
||||
|
||||
@@ -110,10 +110,10 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi
|
||||
const allocator = browser.app.allocator;
|
||||
const arena_pool = browser.arena_pool;
|
||||
|
||||
const arena = try arena_pool.acquire(.{ .debug = "Session" });
|
||||
const arena = try arena_pool.acquire(.small, "Session");
|
||||
errdefer arena_pool.release(arena);
|
||||
|
||||
const page_arena = try arena_pool.acquire(.{ .debug = "Session.page_arena" });
|
||||
const page_arena = try arena_pool.acquire(.large, "Session.page_arena");
|
||||
errdefer arena_pool.release(page_arena);
|
||||
|
||||
self.* = .{
|
||||
@@ -186,12 +186,8 @@ pub fn removePage(self: *Session) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub const GetArenaOpts = struct {
|
||||
debug: []const u8,
|
||||
};
|
||||
|
||||
pub fn getArena(self: *Session, opts: GetArenaOpts) !Allocator {
|
||||
return self.arena_pool.acquire(.{ .debug = opts.debug });
|
||||
pub fn getArena(self: *Session, size_or_bucket: anytype, debug: []const u8) !Allocator {
|
||||
return self.arena_pool.acquire(size_or_bucket, debug);
|
||||
}
|
||||
|
||||
pub fn releaseArena(self: *Session, allocator: Allocator) void {
|
||||
|
||||
@@ -66,7 +66,7 @@ dirty: bool = false,
|
||||
pub fn init(page: *Page) !StyleManager {
|
||||
return .{
|
||||
.page = page,
|
||||
.arena = try page.getArena(.{ .debug = "StyleManager" }),
|
||||
.arena = try page.getArena(.medium, "StyleManager"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ pub const ContextParams = struct {
|
||||
};
|
||||
|
||||
pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context {
|
||||
const context_arena = try self.app.arena_pool.acquire(.{ .debug = params.debug_name });
|
||||
const context_arena = try self.app.arena_pool.acquire(.large, params.debug_name);
|
||||
errdefer self.app.arena_pool.release(context_arena);
|
||||
|
||||
const isolate = self.isolate;
|
||||
|
||||
@@ -1479,7 +1479,7 @@ fn createFinalizerCallback(
|
||||
) !*Session.FinalizerCallback {
|
||||
const session = self.ctx.session;
|
||||
|
||||
const arena = try session.getArena(.{ .debug = "FinalizerCallback" });
|
||||
const arena = try session.getArena(.tiny, "FinalizerCallback");
|
||||
errdefer session.releaseArena(arena);
|
||||
|
||||
const fc = try arena.create(Session.FinalizerCallback);
|
||||
|
||||
@@ -45,7 +45,7 @@ key: []const u8,
|
||||
security_token: v8.Global,
|
||||
|
||||
pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin {
|
||||
const arena = try app.arena_pool.acquire(.{ .debug = "Origin" });
|
||||
const arena = try app.arena_pool.acquire(.tiny, "Origin");
|
||||
errdefer app.arena_pool.release(arena);
|
||||
|
||||
var hs: js.HandleScope = undefined;
|
||||
|
||||
@@ -44,11 +44,11 @@ fn _toSlice(self: String, comptime null_terminate: bool, allocator: Allocator) !
|
||||
const handle = self.handle;
|
||||
const isolate = local.isolate.handle;
|
||||
|
||||
const len = v8.v8__String__Utf8Length(handle, isolate);
|
||||
const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(len), 0) else allocator.alloc(u8, @intCast(len)));
|
||||
const l = v8.v8__String__Utf8Length(handle, isolate);
|
||||
const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(l), 0) else allocator.alloc(u8, @intCast(l)));
|
||||
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(n == len);
|
||||
std.debug.assert(n == l);
|
||||
}
|
||||
|
||||
return buf;
|
||||
@@ -64,32 +64,32 @@ pub fn toSSOWithAlloc(self: String, allocator: Allocator) !SSO {
|
||||
const handle = self.handle;
|
||||
const isolate = self.local.isolate.handle;
|
||||
|
||||
const len: usize = @intCast(v8.v8__String__Utf8Length(handle, isolate));
|
||||
const l: usize = @intCast(v8.v8__String__Utf8Length(handle, isolate));
|
||||
|
||||
if (len <= 12) {
|
||||
if (l <= 12) {
|
||||
var content: [12]u8 = undefined;
|
||||
const n = v8.v8__String__WriteUtf8(handle, isolate, &content[0], content.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(n == len);
|
||||
std.debug.assert(n == l);
|
||||
}
|
||||
// Weird that we do this _after_, but we have to..I've seen weird issues
|
||||
// in ReleaseMode where v8 won't write to content if it starts off zero
|
||||
// initiated
|
||||
@memset(content[len..], 0);
|
||||
return .{ .len = @intCast(len), .payload = .{ .content = content } };
|
||||
@memset(content[l..], 0);
|
||||
return .{ .len = @intCast(l), .payload = .{ .content = content } };
|
||||
}
|
||||
|
||||
const buf = try allocator.alloc(u8, len);
|
||||
const buf = try allocator.alloc(u8, l);
|
||||
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(n == len);
|
||||
std.debug.assert(n == l);
|
||||
}
|
||||
|
||||
var prefix: [4]u8 = @splat(0);
|
||||
@memcpy(&prefix, buf[0..4]);
|
||||
|
||||
return .{
|
||||
.len = @intCast(len),
|
||||
.len = @intCast(l),
|
||||
.payload = .{ .heap = .{
|
||||
.prefix = prefix,
|
||||
.ptr = buf.ptr,
|
||||
@@ -103,9 +103,13 @@ pub fn format(self: String, writer: *std.Io.Writer) !void {
|
||||
const isolate = local.isolate.handle;
|
||||
|
||||
var small: [1024]u8 = undefined;
|
||||
const len = v8.v8__String__Utf8Length(handle, isolate);
|
||||
var buf = if (len < 1024) &small else local.call_arena.alloc(u8, @intCast(len)) catch return error.WriteFailed;
|
||||
const l = v8.v8__String__Utf8Length(handle, isolate);
|
||||
var buf = if (l < 1024) &small else local.call_arena.alloc(u8, @intCast(l)) catch return error.WriteFailed;
|
||||
|
||||
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||
return writer.writeAll(buf[0..n]);
|
||||
}
|
||||
|
||||
pub fn len(self: String) usize {
|
||||
return @intCast(v8.v8__String__Utf8Length(self.handle, self.local.isolate.handle));
|
||||
}
|
||||
|
||||
@@ -77,7 +77,15 @@ pub fn initWithMimeValidation(
|
||||
validate_mime: bool,
|
||||
page: *Page,
|
||||
) !*Blob {
|
||||
const arena = try page.getArena(.{ .debug = "Blob" });
|
||||
const data_len = blk: {
|
||||
const parts = maybe_blob_parts orelse break :blk 0;
|
||||
var size: usize = 0;
|
||||
for (parts) |p| {
|
||||
size += p.len;
|
||||
}
|
||||
break :blk size;
|
||||
};
|
||||
const arena = try page.getArena(256 + data_len, "Blob");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const options: InitOptions = maybe_options orelse .{};
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn parseFromString(
|
||||
@"image/svg+xml",
|
||||
}, mime_type) orelse return error.NotSupported;
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "DOMParser.parseFromString" });
|
||||
const arena = try page.getArena(.medium, "DOMParser.parseFromString");
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
return switch (target_mime) {
|
||||
|
||||
@@ -666,7 +666,7 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
|
||||
page._parse_mode = .document_write;
|
||||
defer page._parse_mode = previous_parse_mode;
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "Document.write" });
|
||||
const arena = try page.getArena(.medium, "Document.write");
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
var parser = Parser.init(arena, fragment_node, page);
|
||||
|
||||
@@ -90,14 +90,14 @@ pub const Options = struct {
|
||||
};
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
||||
const arena = try page.getArena(.{ .debug = "Event" });
|
||||
const arena = try page.getArena(.tiny, "Event");
|
||||
errdefer page.releaseArena(arena);
|
||||
const str = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, str, opts_, false);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*Event {
|
||||
const arena = try page.getArena(.{ .debug = "Event.trusted" });
|
||||
const arena = try page.getArena(.tiny, "Event.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, opts_, true);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ _proto: *Blob,
|
||||
|
||||
// TODO: Implement File API.
|
||||
pub fn init(page: *Page) !*File {
|
||||
const arena = try page.getArena(.{ .debug = "File" });
|
||||
const arena = try page.getArena(.tiny, "File");
|
||||
errdefer page.releaseArena(arena);
|
||||
return page._factory.blob(arena, File{ ._proto = undefined });
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ const Result = union(enum) {
|
||||
};
|
||||
|
||||
pub fn init(page: *Page) !*FileReader {
|
||||
const arena = try page.getArena(.{ .debug = "FileReader" });
|
||||
const arena = try page.getArena(.tiny, "FileReader");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
return page._factory.eventTargetWithAllocator(arena, FileReader{
|
||||
|
||||
@@ -71,7 +71,7 @@ pub const ObserverInit = struct {
|
||||
};
|
||||
|
||||
pub fn init(callback: js.Function.Temp, options: ?ObserverInit, page: *Page) !*IntersectionObserver {
|
||||
const arena = try page.getArena(.{ .debug = "IntersectionObserver" });
|
||||
const arena = try page.getArena(.medium, "IntersectionObserver");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const opts = options orelse ObserverInit{};
|
||||
@@ -266,7 +266,7 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, page: *Page)
|
||||
(was_intersecting_opt != null and was_intersecting_opt.? != is_now_intersecting);
|
||||
|
||||
if (should_report) {
|
||||
const arena = try page.getArena(.{ .debug = "IntersectionObserverEntry" });
|
||||
const arena = try page.getArena(.tiny, "IntersectionObserverEntry");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const entry = try arena.create(IntersectionObserverEntry);
|
||||
|
||||
@@ -76,7 +76,7 @@ pub const ObserveOptions = struct {
|
||||
};
|
||||
|
||||
pub fn init(callback: js.Function.Temp, page: *Page) !*MutationObserver {
|
||||
const arena = try page.getArena(.{ .debug = "MutationObserver" });
|
||||
const arena = try page.getArena(.medium, "MutationObserver");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const self = try arena.create(MutationObserver);
|
||||
@@ -227,7 +227,7 @@ pub fn notifyAttributeChange(
|
||||
}
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "MutationRecord" });
|
||||
const arena = try page.getArena(.tiny, "MutationRecord");
|
||||
const record = try arena.create(MutationRecord);
|
||||
record.* = .{
|
||||
._arena = arena,
|
||||
@@ -271,7 +271,7 @@ pub fn notifyCharacterDataChange(
|
||||
continue;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "MutationRecord" });
|
||||
const arena = try page.getArena(.tiny, "MutationRecord");
|
||||
const record = try arena.create(MutationRecord);
|
||||
record.* = .{
|
||||
._arena = arena,
|
||||
@@ -318,7 +318,7 @@ pub fn notifyChildListChange(
|
||||
continue;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "MutationRecord" });
|
||||
const arena = try page.getArena(.tiny, "MutationRecord");
|
||||
const record = try arena.create(MutationRecord);
|
||||
record.* = .{
|
||||
._arena = arena,
|
||||
|
||||
@@ -38,7 +38,7 @@ const QueryDescriptor = struct {
|
||||
};
|
||||
// We always report 'prompt' (the default safe value — neither granted nor denied).
|
||||
pub fn query(_: *const Permissions, qd: QueryDescriptor, page: *Page) !js.Promise {
|
||||
const arena = try page.getArena(.{ .debug = "PermissionStatus" });
|
||||
const arena = try page.getArena(.tiny, "PermissionStatus");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const status = try arena.create(PermissionStatus);
|
||||
|
||||
@@ -33,7 +33,7 @@ const Range = @This();
|
||||
_proto: *AbstractRange,
|
||||
|
||||
pub fn init(page: *Page) !*Range {
|
||||
const arena = try page.getArena(.{ .debug = "Range" });
|
||||
const arena = try page.getArena(.medium, "Range");
|
||||
errdefer page.releaseArena(arena);
|
||||
return page._factory.abstractRange(arena, Range{ ._proto = undefined }, page);
|
||||
}
|
||||
@@ -312,7 +312,7 @@ pub fn intersectsNode(self: *const Range, node: *Node) bool {
|
||||
}
|
||||
|
||||
pub fn cloneRange(self: *const Range, page: *Page) !*Range {
|
||||
const arena = try page.getArena(.{ .debug = "Range.clone" });
|
||||
const arena = try page.getArena(.medium, "Range.clone");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const clone = try page._factory.abstractRange(arena, Range{ ._proto = undefined }, page);
|
||||
|
||||
@@ -407,7 +407,7 @@ pub fn postMessage(self: *Window, message: js.Value.Temp, target_origin: ?[]cons
|
||||
const target_page = self._page;
|
||||
const source_window = target_page.js.getIncumbent().window;
|
||||
|
||||
const arena = try target_page.getArena(.{ .debug = "Window.postMessage" });
|
||||
const arena = try target_page.getArena(.medium, "Window.postMessage");
|
||||
errdefer target_page.releaseArena(arena);
|
||||
|
||||
// Origin should be the source window's origin (where the message came from)
|
||||
@@ -645,7 +645,7 @@ fn scheduleCallback(self: *Window, cb: js.Function.Temp, delay_ms: u32, opts: Sc
|
||||
return error.TooManyTimeout;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
||||
const arena = try page.getArena(.tiny, "Window.schedule");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const timer_id = self._timer_id +% 1;
|
||||
|
||||
@@ -52,7 +52,7 @@ _playState: PlayState = .idle,
|
||||
//
|
||||
// TODO add support for effect and timeline
|
||||
pub fn init(page: *Page) !*Animation {
|
||||
const arena = try page.getArena(.{ .debug = "Animation" });
|
||||
const arena = try page.getArena(.tiny, "Animation");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const self = try arena.create(Animation);
|
||||
|
||||
@@ -39,7 +39,7 @@ pub const ValueIterator = GenericIterator(Iterator, "1");
|
||||
pub const EntryIterator = GenericIterator(Iterator, null);
|
||||
|
||||
pub fn init(node: *Node, page: *Page) !*ChildNodes {
|
||||
const arena = try page.getArena(.{ .debug = "ChildNodes" });
|
||||
const arena = try page.getArena(.small, "ChildNodes");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const self = try arena.create(ChildNodes);
|
||||
|
||||
@@ -33,7 +33,7 @@ _family: []const u8,
|
||||
pub fn init(family: []const u8, source: []const u8, page: *Page) !*FontFace {
|
||||
_ = source;
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "FontFace" });
|
||||
const arena = try page.getArena(.tiny, "FontFace");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const self = try arena.create(FontFace);
|
||||
|
||||
@@ -34,7 +34,7 @@ _proto: *EventTarget,
|
||||
_arena: Allocator,
|
||||
|
||||
pub fn init(page: *Page) !*FontFaceSet {
|
||||
const arena = try page.getArena(.{ .debug = "FontFaceSet" });
|
||||
const arena = try page.getArena(.tiny, "FontFaceSet");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
return page._factory.eventTargetWithAllocator(arena, FontFaceSet{
|
||||
|
||||
@@ -292,7 +292,7 @@ pub fn insertAdjacentHTML(
|
||||
});
|
||||
const doc_node = doc.asNode();
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "HTML.insertAdjacentHTML" });
|
||||
const arena = try page.getArena(.medium, "HTML.insertAdjacentHTML");
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
const Parser = @import("../../parser/Parser.zig");
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder {
|
||||
_ = std.meta.stringToEnum(Label, label) orelse return error.RangeError;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "TextDecoder" });
|
||||
const arena = try page.getArena(.large, "TextDecoder");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const opts = opts_ orelse InitOpts{};
|
||||
|
||||
@@ -39,14 +39,14 @@ const CloseEventOptions = struct {
|
||||
const Options = Event.inheritOptions(CloseEvent, CloseEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*CloseEvent {
|
||||
const arena = try page.getArena(.{ .debug = "CloseEvent" });
|
||||
const arena = try page.getArena(.tiny, "CloseEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*CloseEvent {
|
||||
const arena = try page.getArena(.{ .debug = "CloseEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "CloseEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const CompositionEventOptions = struct {
|
||||
const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent {
|
||||
const arena = try page.getArena(.{ .debug = "CompositionEvent" });
|
||||
const arena = try page.getArena(.tiny, "CompositionEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const CustomEventOptions = struct {
|
||||
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent {
|
||||
const arena = try page.getArena(.{ .debug = "CustomEvent" });
|
||||
const arena = try page.getArena(.tiny, "CustomEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ pub const ErrorEventOptions = struct {
|
||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||
const arena = try page.getArena(.{ .debug = "ErrorEvent" });
|
||||
const arena = try page.getArena(.small, "ErrorEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, opts_, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||
const arena = try page.getArena(.{ .debug = "ErrorEvent.trusted" });
|
||||
const arena = try page.getArena(.small, "ErrorEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, opts_, true, page);
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ pub const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*FocusEvent {
|
||||
const arena = try page.getArena(.{ .debug = "FocusEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "FocusEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*FocusEvent {
|
||||
const arena = try page.getArena(.{ .debug = "FocusEvent" });
|
||||
const arena = try page.getArena(.tiny, "FocusEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
|
||||
@@ -38,14 +38,14 @@ const Options = Event.inheritOptions(FormDataEvent, struct {
|
||||
});
|
||||
|
||||
pub fn init(typ: []const u8, maybe_options: Options, page: *Page) !*FormDataEvent {
|
||||
const arena = try page.getArena(.{ .debug = "FormDataEvent" });
|
||||
const arena = try page.getArena(.tiny, "FormDataEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, maybe_options, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*FormDataEvent {
|
||||
const arena = try page.getArena(.{ .debug = "FormDataEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "FormDataEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*InputEvent {
|
||||
const arena = try page.getArena(.{ .debug = "InputEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "InputEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*InputEvent {
|
||||
const arena = try page.getArena(.{ .debug = "InputEvent" });
|
||||
const arena = try page.getArena(.tiny, "InputEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
|
||||
@@ -186,13 +186,13 @@ const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
const arena = try page.getArena(.{ .debug = "KeyboardEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "KeyboardEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
const arena = try page.getArena(.{ .debug = "KeyboardEvent" });
|
||||
const arena = try page.getArena(.tiny, "KeyboardEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
|
||||
@@ -50,14 +50,14 @@ pub const Data = union(enum) {
|
||||
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||
const arena = try page.getArena(.{ .debug = "MessageEvent" });
|
||||
const arena = try page.getArena(.small, "MessageEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, opts_, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||
const arena = try page.getArena(.{ .debug = "MessageEvent.trusted" });
|
||||
const arena = try page.getArena(.small, "MessageEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, opts_, true, page);
|
||||
}
|
||||
|
||||
@@ -82,14 +82,14 @@ pub const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
||||
const arena = try page.getArena(.{ .debug = "MouseEvent" });
|
||||
const arena = try page.getArena(.tiny, "MouseEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*MouseEvent {
|
||||
const arena = try page.getArena(.{ .debug = "MouseEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "MouseEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -45,14 +45,14 @@ const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||
const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent" });
|
||||
const arena = try page.getArena(.tiny, "NavigationCurrentEntryChangeEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||
const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "NavigationCurrentEntryChangeEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, opts, true, page);
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ const PageTransitionEventOptions = struct {
|
||||
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PageTransitionEvent" });
|
||||
const arena = try page.getArena(.tiny, "PageTransitionEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PageTransitionEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "PageTransitionEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent {
|
||||
const arena = try page.getArena(.{ .debug = "UIEvent" });
|
||||
const arena = try page.getArena(.tiny, "PointerEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -39,14 +39,14 @@ const PopStateEventOptions = struct {
|
||||
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PopStateEvent" });
|
||||
const arena = try page.getArena(.tiny, "PopStateEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PopStateEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "PopStateEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ const ProgressEventOptions = struct {
|
||||
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||
const arena = try page.getArena(.{ .debug = "ProgressEvent" });
|
||||
const arena = try page.getArena(.tiny, "ProgressEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||
const arena = try page.getArena(.{ .debug = "ProgressEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "ProgressEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ const PromiseRejectionEventOptions = struct {
|
||||
const Options = Event.inheritOptions(PromiseRejectionEvent, PromiseRejectionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*PromiseRejectionEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PromiseRejectionEvent" });
|
||||
const arena = try page.getArena(.tiny, "PromiseRejectionEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -39,14 +39,14 @@ const SubmitEventOptions = struct {
|
||||
const Options = Event.inheritOptions(SubmitEvent, SubmitEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*SubmitEvent {
|
||||
const arena = try page.getArena(.{ .debug = "SubmitEvent" });
|
||||
const arena = try page.getArena(.tiny, "SubmitEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
return initWithTrusted(arena, type_string, opts_, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*SubmitEvent {
|
||||
const arena = try page.getArena(.{ .debug = "SubmitEvent.trusted" });
|
||||
const arena = try page.getArena(.tiny, "SubmitEvent.trusted");
|
||||
errdefer page.releaseArena(arena);
|
||||
return initWithTrusted(arena, typ, _opts, true, page);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*TextEvent {
|
||||
const arena = try page.getArena(.{ .debug = "TextEvent" });
|
||||
const arena = try page.getArena(.tiny, "TextEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ pub const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
||||
const arena = try page.getArena(.{ .debug = "UIEvent" });
|
||||
const arena = try page.getArena(.tiny, "UIEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ pub const Options = Event.inheritOptions(
|
||||
);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*WheelEvent {
|
||||
const arena = try page.getArena(.{ .debug = "WheelEvent" });
|
||||
const arena = try page.getArena(.medium, "WheelEvent");
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ const InitOpts = struct {
|
||||
};
|
||||
|
||||
pub fn init(body_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*Response {
|
||||
const arena = try page.getArena(.{ .debug = "Response" });
|
||||
const arena = try page.getArena(.large, "Response");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const opts = opts_ orelse InitOpts{};
|
||||
@@ -174,7 +174,7 @@ pub fn bytes(self: *const Response, page: *Page) !js.Promise {
|
||||
}
|
||||
|
||||
pub fn clone(self: *const Response, page: *Page) !*Response {
|
||||
const arena = try page.getArena(.{ .debug = "Response.clone" });
|
||||
const arena = try page.getArena((self._body orelse "").len + self._url.len + 256, "Response.clone");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const body = if (self._body) |b| try arena.dupe(u8, b) else null;
|
||||
|
||||
@@ -105,7 +105,7 @@ pub fn init(url: []const u8, protocols_: ?[]const u8, page: *Page) !*WebSocket {
|
||||
}
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "WebSocket" });
|
||||
const arena = try page.getArena(.medium, "WebSocket");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const resolved_url = try URL.resolve(arena, page.base(), url, .{ .always_dupe = true, .encode = true });
|
||||
@@ -272,12 +272,10 @@ pub fn send(self: *WebSocket, data: SendData) !void {
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
|
||||
// Get a dedicated arena for this message
|
||||
const arena = try self._page._session.getArena(.{ .debug = "WebSocket message" });
|
||||
errdefer self._page._session.releaseArena(arena);
|
||||
|
||||
switch (data) {
|
||||
.blob => |blob| {
|
||||
const arena = try self._page._session.getArena(blob._slice.len, "WebSocket.message");
|
||||
errdefer self._page._session.releaseArena(arena);
|
||||
try self.queueMessage(.{ .binary = .{
|
||||
.arena = arena,
|
||||
.data = try arena.dupe(u8, blob._slice),
|
||||
@@ -285,15 +283,21 @@ pub fn send(self: *WebSocket, data: SendData) !void {
|
||||
},
|
||||
.js_val => |js_val| {
|
||||
if (js_val.isString()) |str| {
|
||||
const arena = try self._page._session.getArena(str.len(), "WebSocket.message");
|
||||
errdefer self._page._session.releaseArena(arena);
|
||||
try self.queueMessage(.{ .text = .{
|
||||
.arena = arena,
|
||||
.data = try str.toSliceWithAlloc(arena),
|
||||
} });
|
||||
} else {
|
||||
const binary = try js_val.toZig(BinaryData);
|
||||
const buffer = binary.asBuffer();
|
||||
|
||||
const arena = try self._page._session.getArena(buffer.len, "WebSocket.message");
|
||||
errdefer self._page._session.releaseArena(arena);
|
||||
try self.queueMessage(.{ .binary = .{
|
||||
.arena = arena,
|
||||
.data = try arena.dupe(u8, binary.asBuffer()),
|
||||
.data = try arena.dupe(u8, buffer),
|
||||
} });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ const ResponseType = enum {
|
||||
};
|
||||
|
||||
pub fn init(page: *Page) !*XMLHttpRequest {
|
||||
const arena = try page.getArena(.{ .debug = "XMLHttpRequest" });
|
||||
const arena = try page.getArena(.large, "XMLHttpRequest");
|
||||
errdefer page.releaseArena(arena);
|
||||
const self = try page._factory.xhrEventTarget(arena, XMLHttpRequest{
|
||||
._page = page,
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn querySelectorAll(root: *Node, input: []const u8, page: *Page) !*List {
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "querySelectorAll" });
|
||||
const arena = try page.getArena(.small, "querySelectorAll");
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
var nodes: std.AutoArrayHashMapUnmanaged(*Node, void) = .empty;
|
||||
|
||||
@@ -487,10 +487,10 @@ 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(.{ .debug = "IsolatedWorld" });
|
||||
const arena = try browser.arena_pool.acquire(.small, "IsolatedWorld");
|
||||
errdefer browser.arena_pool.release(arena);
|
||||
|
||||
const call_arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld.call_arena" });
|
||||
const call_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.call_arena");
|
||||
errdefer browser.arena_pool.release(call_arena);
|
||||
|
||||
const world = try arena.create(IsolatedWorld);
|
||||
|
||||
Reference in New Issue
Block a user