mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-08-01 02:06:17 -04:00
mem: add local_arena
The `call_arena` is currently our shortest-lived arena. Its promise is that it
will be valid for at least 1 v8 -> zig -> v8 function call, which makes it ideal
for getting values from v8 into zig, temporary work, and getting values from
zig to v8.
VBut `call_arena`'s lifetime is actually much longer. It's only reset when the
call_depth reaches 0. And the reason for that are Zig functions that invoke
v8 callbacks. If you just do it when a function ends, then if you allocate in
ZigA and then call CallbackA which calls ZigB, then when ZigB ends, your ZigA
allocation is cleared. This is something we could solve by reaching into
Zig's ArenaAllocator to capture a position to rollback from.
So, `call_arena` can end up living relatively long and accumulating quite a bit
of memory. But most calls _don't_ invoke callbacks. Hence, `local_arena` IS
reset at the end of every function.
Using `local_arena` _is_ dangerous, mostly for the case where some of our APIs
receive a *Frame (or *Execution) and thus might use a `local_arena` because they
know they aren't invoking a JS callback. BUT, those APIs might not know that
they're also invoked by some other Zig code that _could_ be invoking JS.
Dangerous? Sure. But, github has code that looks like:
```js
function onDelegatedClick(e) {
for (const el of document.querySelectorAll('[data-action]')) {
if (el.matches('.menu > .item:not(.disabled) a[href]')) {
handle(el);
}
el.closest('.panel');
}
}
```
Anything allocated in the `call_arena` will only be freed when the caller of
`onDelegatedClick` ends. The `querySelectorAll` returns hundreds of elements
and thus builds hundreds of parsed CSS and other scrap (x2 for `matches` and
closest`). `call_arena` peaks at 15MB. With the local_arena? 1MB. If 10x more
elements were returned, the peak would be 10x higher. With local_arena, it
stays 1MB.
This commit is contained in:
@@ -251,10 +251,15 @@ js: *JS.Context,
|
||||
// An arena for the lifetime of the frame.
|
||||
arena: Allocator,
|
||||
|
||||
// An arena with a lifetime guaranteed to be for 1 invoking of a Zig function
|
||||
// from JS. Best arena to use, when possible.
|
||||
// An arena with a lifetime for at least the scope of one Zig invocation from
|
||||
// JS. Prefer local_arena where possible. Use call_arena when allocations may
|
||||
// need to call back into JS (event dispatch, forEach callback, ....)
|
||||
call_arena: Allocator,
|
||||
|
||||
// An arena with a lifetime guaranteed to be for exactly 1 invoking of a Zig
|
||||
// function from JS. Best arena to use, when possible.
|
||||
local_arena: Allocator,
|
||||
|
||||
parent: ?*Frame,
|
||||
window: *Window,
|
||||
document: *Document,
|
||||
@@ -306,6 +311,9 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void {
|
||||
const call_arena = try session.getArena(.medium, "call_arena");
|
||||
errdefer session.releaseArena(call_arena);
|
||||
|
||||
const local_arena = try session.getArena(.medium, "local_arena");
|
||||
errdefer session.releaseArena(local_arena);
|
||||
|
||||
const factory = &page.factory;
|
||||
const document = (try factory.document(Node.Document.HTMLDocument{
|
||||
._proto = undefined,
|
||||
@@ -320,6 +328,7 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void {
|
||||
.document = document,
|
||||
.window = undefined,
|
||||
.call_arena = call_arena,
|
||||
.local_arena = local_arena,
|
||||
._frame_id = frame_id,
|
||||
._page = page,
|
||||
._session = session,
|
||||
@@ -382,6 +391,7 @@ pub fn init(self: *Frame, frame_id: u32, page: *Page, opts: InitOpts) !void {
|
||||
.identity = &page.identity,
|
||||
.identity_arena = arena,
|
||||
.call_arena = self.call_arena,
|
||||
.local_arena = self.local_arena,
|
||||
});
|
||||
errdefer browser.env.destroyContext(self.js);
|
||||
|
||||
@@ -480,6 +490,7 @@ pub fn deinit(self: *Frame) void {
|
||||
self._style_manager.deinit();
|
||||
|
||||
page.releaseArena(self.call_arena);
|
||||
page.releaseArena(self.local_arena);
|
||||
}
|
||||
|
||||
pub fn trackWorker(self: *Frame, worker: *Worker) !void {
|
||||
|
||||
@@ -368,7 +368,7 @@ pub fn waitForScript(self: *Runner, frame_id: u32, src: [:0]const u8, timeout_ms
|
||||
defer try_catch.deinit();
|
||||
|
||||
const s = ls.local.compile(src, "wait_script") catch |err| {
|
||||
const caught = try_catch.caughtOrError(frame.call_arena, err);
|
||||
const caught = try_catch.caughtOrError(frame.local_arena, err);
|
||||
log.err(.app, "wait script error", .{ .err = caught });
|
||||
return error.ScriptError;
|
||||
};
|
||||
@@ -396,7 +396,7 @@ pub fn waitForScript(self: *Runner, frame_id: u32, src: [:0]const u8, timeout_ms
|
||||
|
||||
const script = compiled.get(ls.local.isolate).bindToCurrentContext(&ls.local);
|
||||
const value = script.run() catch |err| {
|
||||
const caught = try_catch.caughtOrError(frame.call_arena, err);
|
||||
const caught = try_catch.caughtOrError(frame.local_arena, err);
|
||||
log.err(.app, "wait script error", .{ .err = caught });
|
||||
return error.ScriptError;
|
||||
};
|
||||
|
||||
@@ -935,7 +935,7 @@ pub const Script = struct {
|
||||
return;
|
||||
}
|
||||
|
||||
const caught = try_catch.caughtOrError(frame.call_arena, error.Unknown);
|
||||
const caught = try_catch.caughtOrError(frame.local_arena, error.Unknown);
|
||||
log.warn(.js, "eval script", .{
|
||||
.url = url,
|
||||
.caught = caught,
|
||||
|
||||
@@ -356,7 +356,7 @@ pub fn createElementNS(frame: *Frame, namespace: Element.Namespace, name: []cons
|
||||
defer try_catch.deinit();
|
||||
|
||||
ls.local.eval(inject_script, "inject_script") catch |err| {
|
||||
const caught = try_catch.caughtOrError(frame.call_arena, err);
|
||||
const caught = try_catch.caughtOrError(frame.local_arena, err);
|
||||
log.err(.app, "inject script error", .{ .err = caught });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ const v8 = js.v8;
|
||||
const log = lp.log;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const CALL_ARENA_RETAIN = 1024 * 16;
|
||||
const LOCAL_ARENA_RETAIN = 1024 * 16;
|
||||
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||
|
||||
const Caller = @This();
|
||||
@@ -101,6 +102,16 @@ pub fn deinit(self: *Caller) void {
|
||||
_ = arena.reset(.{ .retain_with_limit = CALL_ARENA_RETAIN });
|
||||
}
|
||||
|
||||
// Unlike call_arena, local_arena is reset on _every_ return, since its
|
||||
// users promise not to hold data across a nested call. In debug, free
|
||||
// back to the backing allocator so a stale pointer trips the
|
||||
// DebugAllocator's use-after-free detection; in release, retain a buffer
|
||||
// to avoid realloc churn.
|
||||
{
|
||||
const local_arena: *ArenaAllocator = @ptrCast(@alignCast(ctx.local_arena.ptr));
|
||||
_ = local_arena.reset(if (comptime IS_DEBUG) .free_all else .{ .retain_with_limit = LOCAL_ARENA_RETAIN });
|
||||
}
|
||||
|
||||
ctx.call_depth = call_depth;
|
||||
ctx.local = self.prev_local;
|
||||
ctx.global.setJs(self.prev_context);
|
||||
|
||||
@@ -100,6 +100,10 @@ arena: Allocator,
|
||||
// owned by the IsolatedWorld.
|
||||
call_arena: Allocator,
|
||||
|
||||
// Like call_arena, but reset on _every_ Caller.deinit rather than only at
|
||||
// call_depth 0.
|
||||
local_arena: Allocator,
|
||||
|
||||
// Because calls can be nested (i.e.a function calling a callback),
|
||||
// we can only reset the call_arena when call_depth == 0. If we were
|
||||
// to reset it within a callback, it would invalidate the data of
|
||||
@@ -700,7 +704,7 @@ fn importMetaResolveCallback(callback_handle: ?*const v8.FunctionCallbackInfo) c
|
||||
return;
|
||||
};
|
||||
|
||||
const resolved = ctx.script_manager.resolveSpecifier(ctx.call_arena, data.base, specifier) catch {
|
||||
const resolved = ctx.script_manager.resolveSpecifier(ctx.local_arena, data.base, specifier) catch {
|
||||
_ = isolate.throwException(isolate.createTypeError("failed to resolve module specifier"));
|
||||
return;
|
||||
};
|
||||
@@ -904,7 +908,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
|
||||
defer try_catch.deinit();
|
||||
|
||||
break :blk self.module(true, local, ms.src(), state.specifier, true) catch |err| {
|
||||
const caught = try_catch.caughtOrError(self.call_arena, err);
|
||||
const caught = try_catch.caughtOrError(self.local_arena, err);
|
||||
log.err(.js, "module compilation failed", .{
|
||||
.caught = caught,
|
||||
.specifier = state.specifier,
|
||||
|
||||
@@ -229,6 +229,7 @@ pub const ContextParams = struct {
|
||||
identity: *js.Identity,
|
||||
identity_arena: Allocator,
|
||||
call_arena: Allocator,
|
||||
local_arena: Allocator,
|
||||
debug_name: []const u8 = "Context",
|
||||
};
|
||||
|
||||
@@ -315,6 +316,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context
|
||||
.handle = context_global,
|
||||
.templates = self.templates,
|
||||
.call_arena = params.call_arena,
|
||||
.local_arena = params.local_arena,
|
||||
.microtask_queue = microtask_queue,
|
||||
.script_manager = if (comptime is_frame) &global._script_manager.base else &global._script_manager,
|
||||
.scheduler = .init(context_arena),
|
||||
@@ -332,6 +334,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context
|
||||
.page = context.page,
|
||||
.session = page.session,
|
||||
.call_arena = params.call_arena,
|
||||
.local_arena = params.local_arena,
|
||||
._factory = global._factory,
|
||||
._scheduler = &context.scheduler,
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ js: *Context,
|
||||
buf: []u8,
|
||||
arena: Allocator,
|
||||
call_arena: Allocator,
|
||||
local_arena: Allocator,
|
||||
|
||||
page: *Page,
|
||||
session: *Session,
|
||||
|
||||
@@ -381,7 +381,7 @@ const Context = struct {
|
||||
|
||||
if (!info.has_visible and label == null and href_raw == null) return;
|
||||
|
||||
const href = if (href_raw) |h| URL.resolve(frame.call_arena, frame.base(), h, .{ .encoding = frame.charset }) catch h else null;
|
||||
const href = if (href_raw) |h| URL.resolve(frame.local_arena, frame.base(), h, .{ .encoding = frame.charset }) catch h else null;
|
||||
|
||||
if (info.has_block) {
|
||||
try self.renderChildren(el.asNode());
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn escape(value: []const u8, frame: *Frame) ![]const u8 {
|
||||
return value;
|
||||
}
|
||||
|
||||
const result = try frame.call_arena.alloc(u8, out_len);
|
||||
const result = try frame.local_arena.alloc(u8, out_len);
|
||||
var pos: usize = 0;
|
||||
|
||||
if (needsEscape(true, first)) {
|
||||
|
||||
@@ -190,7 +190,7 @@ pub fn getUsages(self: *const CryptoKey, exec: *const Execution) ![]const []cons
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return exec.call_arena.dupe([]const u8, buf[0..n]);
|
||||
return exec.local_arena.dupe([]const u8, buf[0..n]);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
|
||||
@@ -627,7 +627,7 @@ pub fn inverse(self: *const DOMMatrixReadOnly, page: *Page) !*DOMMatrix {
|
||||
}
|
||||
|
||||
pub fn toFloat32Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) !js.TypedArray(f32) {
|
||||
const out = try exec.call_arena.alloc(f32, 16);
|
||||
const out = try exec.local_arena.alloc(f32, 16);
|
||||
for (0..16) |i| {
|
||||
out[i] = @floatCast(self._m[i]);
|
||||
}
|
||||
@@ -635,7 +635,7 @@ pub fn toFloat32Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution)
|
||||
}
|
||||
|
||||
pub fn toFloat64Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) !js.TypedArray(f64) {
|
||||
const out = try exec.call_arena.dupe(f64, &self._m);
|
||||
const out = try exec.local_arena.dupe(f64, &self._m);
|
||||
return .{ .values = out };
|
||||
}
|
||||
|
||||
@@ -648,7 +648,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
}
|
||||
return std.fmt.allocPrint(exec.call_arena, "matrix({d}, {d}, {d}, {d}, {d}, {d})", .{
|
||||
return std.fmt.allocPrint(exec.local_arena, "matrix({d}, {d}, {d}, {d}, {d}, {d})", .{
|
||||
m[0], m[1], m[4], m[5], m[12], m[13],
|
||||
});
|
||||
}
|
||||
@@ -657,7 +657,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
}
|
||||
return std.fmt.allocPrint(exec.call_arena, "matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{
|
||||
return std.fmt.allocPrint(exec.local_arena, "matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{
|
||||
m[0], m[1], m[2], m[3],
|
||||
m[4], m[5], m[6], m[7],
|
||||
m[8], m[9], m[10], m[11],
|
||||
|
||||
@@ -102,7 +102,7 @@ fn normalizeFormat(arena: Allocator, format: []const u8) ![]const u8 {
|
||||
}
|
||||
|
||||
pub fn getData(self: *const DataTransfer, format: []const u8, frame: *Frame) ![]const u8 {
|
||||
const norm = try normalizeFormat(frame.call_arena, format);
|
||||
const norm = try normalizeFormat(frame.local_arena, format);
|
||||
for (self._items.items) |it| {
|
||||
if (it._kind == .string and std.mem.eql(u8, it._type, norm)) {
|
||||
return it._payload.string;
|
||||
@@ -127,7 +127,7 @@ pub fn setData(self: *DataTransfer, format: []const u8, data: []const u8) !void
|
||||
|
||||
pub fn clearData(self: *DataTransfer, format_: ?[]const u8, frame: *Frame) !void {
|
||||
if (format_) |format| {
|
||||
const norm = try normalizeFormat(frame.call_arena, format);
|
||||
const norm = try normalizeFormat(frame.local_arena, format);
|
||||
var i: usize = 0;
|
||||
while (i < self._items.items.len) {
|
||||
const it = self._items.items[i];
|
||||
@@ -222,14 +222,14 @@ pub fn getTypes(self: *DataTransfer, frame: *Frame) ![][]const u8 {
|
||||
var has_files = false;
|
||||
for (self._items.items) |it| {
|
||||
switch (it._kind) {
|
||||
.string => try out.append(frame.call_arena, it._type),
|
||||
.string => try out.append(frame.local_arena, it._type),
|
||||
.file => has_files = true,
|
||||
}
|
||||
}
|
||||
if (has_files) {
|
||||
try out.append(frame.call_arena, "Files");
|
||||
try out.append(frame.local_arena, "Files");
|
||||
}
|
||||
return out.toOwnedSlice(frame.call_arena);
|
||||
return out.toOwnedSlice(frame.local_arena);
|
||||
}
|
||||
|
||||
pub fn getDropEffect(self: *const DataTransfer) []const u8 {
|
||||
|
||||
@@ -171,7 +171,7 @@ pub fn setDomain(self: *Document, value: []const u8) !void {
|
||||
const doc_frame = self._frame orelse return error.SecurityError;
|
||||
const origin = doc_frame.origin orelse return error.SecurityError;
|
||||
|
||||
const arena = doc_frame.call_arena;
|
||||
const arena = doc_frame.local_arena;
|
||||
const requested = if (idna.needsAscii(value)) try idna.toAscii(arena, value) else value;
|
||||
|
||||
// Validate against the current effective domain. Once relaxed,
|
||||
@@ -193,7 +193,7 @@ pub fn setDomain(self: *Document, value: []const u8) !void {
|
||||
|
||||
pub fn getCookie(_: *Document, frame: *Frame) ![]const u8 {
|
||||
var buf: std.ArrayList(u8) = .empty;
|
||||
try frame._session.cookie_jar.forRequest(frame.url, buf.writer(frame.call_arena), .{
|
||||
try frame._session.cookie_jar.forRequest(frame.url, buf.writer(frame.local_arena), .{
|
||||
.is_http = false,
|
||||
.is_navigation = true,
|
||||
});
|
||||
@@ -738,7 +738,7 @@ pub fn elementFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) !?*Eleme
|
||||
|
||||
const root = self.asNode();
|
||||
var stack: std.ArrayList(*Node) = .empty;
|
||||
try stack.append(frame.call_arena, root);
|
||||
try stack.append(frame.local_arena, root);
|
||||
|
||||
var visibility_cache: Element.VisibilityCache = .{};
|
||||
var preorder_index: f64 = 0;
|
||||
@@ -770,7 +770,7 @@ pub fn elementFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) !?*Eleme
|
||||
// Add children to stack in reverse order so we process them in document order
|
||||
var child = node.lastChild();
|
||||
while (child) |c| {
|
||||
try stack.append(frame.call_arena, c);
|
||||
try stack.append(frame.local_arena, c);
|
||||
child = c.previousSibling();
|
||||
}
|
||||
}
|
||||
@@ -783,7 +783,7 @@ pub fn elementsFromPoint(self: *Document, x: f64, y: f64, frame: *Frame) ![]cons
|
||||
var current: ?*Element = (try self.elementFromPoint(x, y, frame)) orelse return &.{};
|
||||
var result: std.ArrayList(*Element) = .empty;
|
||||
while (current) |el| {
|
||||
try result.append(frame.call_arena, el);
|
||||
try result.append(frame.local_arena, el);
|
||||
current = el.parentElement();
|
||||
}
|
||||
return result.items;
|
||||
|
||||
@@ -222,7 +222,7 @@ pub const JsApi = struct {
|
||||
|
||||
pub const innerHTML = bridge.accessor(_getInnerHTML, _setInnerHTML, .{ .ce_reactions = true });
|
||||
fn _getInnerHTML(self: *DocumentFragment, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.getInnerHTML(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
@@ -1280,7 +1280,7 @@ pub fn getClientRects(self: *Element, frame: *Frame) ![]DOMRect {
|
||||
if (!self.checkVisibilityCached(null, frame)) {
|
||||
return &.{};
|
||||
}
|
||||
const rects = try frame.call_arena.alloc(DOMRect, 1);
|
||||
const rects = try frame.local_arena.alloc(DOMRect, 1);
|
||||
rects[0] = self.getBoundingClientRectForVisible(frame);
|
||||
return rects;
|
||||
}
|
||||
@@ -1924,14 +1924,16 @@ pub const JsApi = struct {
|
||||
|
||||
pub const innerText = bridge.accessor(_innerText, Element.setInnerText, .{ .ce_reactions = true });
|
||||
fn _innerText(self: *Element, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.getInnerText(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
pub const outerHTML = bridge.accessor(_getOuterHTML, _setOuterHTML, .{ .ce_reactions = true });
|
||||
fn _getOuterHTML(self: *Element, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
// local_arena: serialization is read-only and the returned string is
|
||||
// converted to v8 before this call returns. No JS runs in between.
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.getOuterHTML(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
@@ -1942,7 +1944,9 @@ pub const JsApi = struct {
|
||||
|
||||
pub const innerHTML = bridge.accessor(_getInnerHTML, _setInnerHTML, .{ .ce_reactions = true });
|
||||
fn _getInnerHTML(self: *Element, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
// local_arena: read-only serialization, result converted to v8 before
|
||||
// returning; no JS runs in between.
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.getInnerHTML(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ pub fn getTitle(self: *HTMLDocument, frame: *Frame) ![]const u8 {
|
||||
return "";
|
||||
};
|
||||
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try title_element.asNode().getTextContent(&buf.writer);
|
||||
const text = buf.written();
|
||||
|
||||
@@ -114,7 +114,7 @@ pub fn getTitle(self: *HTMLDocument, frame: *Frame) ![]const u8 {
|
||||
var started = false;
|
||||
var in_whitespace = false;
|
||||
var result: std.ArrayList(u8) = .empty;
|
||||
try result.ensureTotalCapacity(frame.call_arena, text.len);
|
||||
try result.ensureTotalCapacity(frame.local_arena, text.len);
|
||||
|
||||
for (text) |c| {
|
||||
const is_ascii_ws = c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == '\x0C';
|
||||
|
||||
@@ -191,7 +191,7 @@ pub fn disconnect(self: *IntersectionObserver, frame: *Frame) void {
|
||||
}
|
||||
|
||||
pub fn takeRecords(self: *IntersectionObserver, frame: *Frame) ![]*IntersectionObserverEntry {
|
||||
const entries = try frame.call_arena.dupe(*IntersectionObserverEntry, self._pending_entries.items);
|
||||
const entries = try frame.local_arena.dupe(*IntersectionObserverEntry, self._pending_entries.items);
|
||||
self._pending_entries.clearRetainingCapacity();
|
||||
return entries;
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ pub fn disconnect(self: *MutationObserver, frame: *Frame) void {
|
||||
}
|
||||
|
||||
pub fn takeRecords(self: *MutationObserver, frame: *Frame) ![]*MutationRecord {
|
||||
const records = try frame.call_arena.dupe(*MutationRecord, self._pending_records.items);
|
||||
const records = try frame.local_arena.dupe(*MutationRecord, self._pending_records.items);
|
||||
self._pending_records.clearRetainingCapacity();
|
||||
return records;
|
||||
}
|
||||
|
||||
@@ -908,7 +908,7 @@ pub fn setData(self: *Node, data: []const u8, frame: *Frame) !void {
|
||||
|
||||
pub fn normalize(self: *Node, frame: *Frame) !void {
|
||||
var buffer: std.ArrayList(u8) = .empty;
|
||||
return self._normalize(frame.call_arena, &buffer, frame);
|
||||
return self._normalize(frame.local_arena, &buffer, frame);
|
||||
}
|
||||
|
||||
const CloneError = error{
|
||||
@@ -1311,7 +1311,9 @@ pub const JsApi = struct {
|
||||
// cdata and attributes can return value directly, avoiding the copy
|
||||
switch (self._type) {
|
||||
.element, .document_fragment => {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
// local_arena: read-only text collection, result converted to
|
||||
// v8 before returning; no JS runs in between.
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.getTextContent(&buf.writer);
|
||||
return buf.written();
|
||||
},
|
||||
|
||||
@@ -215,11 +215,11 @@ pub fn getEntries(self: *const Performance) []*Entry {
|
||||
}
|
||||
|
||||
pub fn getEntriesByType(self: *const Performance, entry_type: []const u8, exec: *const Execution) ![]const *Entry {
|
||||
return filterEntriesByType(exec.call_arena, self._entries.items, entry_type);
|
||||
return filterEntriesByType(exec.local_arena, self._entries.items, entry_type);
|
||||
}
|
||||
|
||||
pub fn getEntriesByName(self: *const Performance, name: []const u8, entry_type: ?[]const u8, exec: *const Execution) ![]const *Entry {
|
||||
return filterEntriesByName(exec.call_arena, self._entries.items, name, entry_type);
|
||||
return filterEntriesByName(exec.local_arena, self._entries.items, name, entry_type);
|
||||
}
|
||||
|
||||
// Also used by PerformanceObserver
|
||||
|
||||
@@ -216,11 +216,11 @@ pub const EntryList = struct {
|
||||
}
|
||||
|
||||
pub fn getEntriesByType(self: *const EntryList, entry_type: []const u8, exec: *Execution) ![]const *Performance.Entry {
|
||||
return Performance.filterEntriesByType(exec.call_arena, self._entries, entry_type);
|
||||
return Performance.filterEntriesByType(exec.local_arena, self._entries, entry_type);
|
||||
}
|
||||
|
||||
pub fn getEntriesByName(self: *const EntryList, name: []const u8, entry_type: ?[]const u8, exec: *Execution) ![]const *Performance.Entry {
|
||||
return Performance.filterEntriesByName(exec.call_arena, self._entries, name, entry_type);
|
||||
return Performance.filterEntriesByName(exec.local_arena, self._entries, name, entry_type);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
|
||||
@@ -569,7 +569,7 @@ pub fn createContextualFragment(self: *const Range, html: []const u8, frame: *Fr
|
||||
|
||||
pub fn toString(self: *const Range, frame: *Frame) ![]const u8 {
|
||||
// Simplified implementation: just extract text content
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.writeTextContent(&buf.writer);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ pub fn importKey(
|
||||
const k = jwk.k orelse {
|
||||
return local.rejectPromise(.{ .dom_exception = .{ .err = error.DataError } });
|
||||
};
|
||||
break :blk common.base64Decode(exec.call_arena, k) catch |err| switch (err) {
|
||||
break :blk common.base64Decode(exec.local_arena, k) catch |err| switch (err) {
|
||||
error.DataError => return local.rejectPromise(.{ .dom_exception = .{ .err = error.DataError } }),
|
||||
else => |e| return e,
|
||||
};
|
||||
@@ -309,13 +309,13 @@ fn exportJwk(key: *CryptoKey, exec: *const Execution) !js.Promise {
|
||||
|
||||
// The `alg` registry value depends on the algorithm and key length.
|
||||
const alg: []const u8 = switch (key._type) {
|
||||
.aes => try std.fmt.allocPrint(exec.call_arena, "A{d}{s}", .{
|
||||
.aes => try std.fmt.allocPrint(exec.local_arena, "A{d}{s}", .{
|
||||
key._key.len * 8,
|
||||
key._algorithm.name[4..], // strip "AES-"
|
||||
}),
|
||||
.hmac => blk: {
|
||||
const hash: []const u8 = key._algorithm.hash orelse "SHA-";
|
||||
break :blk try std.fmt.allocPrint(exec.call_arena, "HS{s}", .{hash[4..]}); // strip "SHA-"
|
||||
break :blk try std.fmt.allocPrint(exec.local_arena, "HS{s}", .{hash[4..]}); // strip "SHA-"
|
||||
},
|
||||
else => {
|
||||
log.warn(.not_implemented, "SubtleCrypto.exportKey", .{ .format = "jwk", .type = key._type });
|
||||
@@ -324,7 +324,7 @@ fn exportJwk(key: *CryptoKey, exec: *const Execution) !js.Promise {
|
||||
};
|
||||
|
||||
return local.resolvePromise(JwkSecret{
|
||||
.k = try common.base64Encode(exec.call_arena, key._key),
|
||||
.k = try common.base64Encode(exec.local_arena, key._key),
|
||||
.alg = alg,
|
||||
.ext = key._extractable,
|
||||
.key_ops = try key.getUsages(exec),
|
||||
|
||||
@@ -174,7 +174,7 @@ pub fn getSearch(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
return "";
|
||||
}
|
||||
|
||||
var buf = std.Io.Writer.Allocating.init(exec.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(exec.local_arena);
|
||||
try buf.writer.writeByte('?');
|
||||
try search_params.toString(&buf.writer);
|
||||
return buf.written();
|
||||
@@ -262,7 +262,7 @@ pub fn getOrigin(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
const origin = U.url_get_origin(self._url);
|
||||
defer origin.deinit();
|
||||
|
||||
return exec.call_arena.dupe(u8, origin.slice());
|
||||
return exec.local_arena.dupe(u8, origin.slice());
|
||||
}
|
||||
|
||||
pub fn setHref(self: *URL, value: []const u8, exec: *const Execution) !void {
|
||||
@@ -288,7 +288,7 @@ pub fn toString(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
if (search_params.getSize() == 0) {
|
||||
U.url_set_query_to_null(self._url);
|
||||
} else {
|
||||
var buf = std.Io.Writer.Allocating.init(exec.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(exec.local_arena);
|
||||
defer buf.deinit();
|
||||
try search_params.toString(&buf.writer);
|
||||
const query = buf.written();
|
||||
|
||||
@@ -696,11 +696,11 @@ pub fn postMessage(self: *Window, message: js.Value, target_origin: ?[]const u8,
|
||||
|
||||
const base64 = @import("encoding/base64.zig");
|
||||
pub fn btoa(_: *const Window, input: base64.BinInput, frame: *Frame) ![]const u8 {
|
||||
return base64.encode(frame.call_arena, input);
|
||||
return base64.encode(frame.local_arena, input);
|
||||
}
|
||||
|
||||
pub fn atob(_: *const Window, input: base64.BinInput, frame: *Frame) !js.String.OneByte {
|
||||
const decoded = try base64.decode(frame.call_arena, input);
|
||||
const decoded = try base64.decode(frame.local_arena, input);
|
||||
return .{ .bytes = decoded };
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ _http_owner: HttpClient.Owner = .{},
|
||||
|
||||
arena: Allocator,
|
||||
call_arena: Allocator,
|
||||
local_arena: Allocator,
|
||||
url: [:0]const u8,
|
||||
// Same-origin constraint: a worker's origin is inherited from its parent frame.
|
||||
origin: ?[]const u8 = null,
|
||||
@@ -129,6 +130,9 @@ pub fn init(
|
||||
const call_arena = try session.getArena(.small, "WorkerGlobalScope.call_arena");
|
||||
errdefer session.releaseArena(call_arena);
|
||||
|
||||
const local_arena = try session.getArena(.small, "WorkerGlobalScope.local_arena");
|
||||
errdefer session.releaseArena(local_arena);
|
||||
|
||||
const factory = frame._factory;
|
||||
const self = try factory.eventTargetWithAllocator(arena, WorkerGlobalScope{
|
||||
.url = url,
|
||||
@@ -136,6 +140,7 @@ pub fn init(
|
||||
.origin = frame.origin,
|
||||
.js = undefined,
|
||||
.call_arena = call_arena,
|
||||
.local_arena = local_arena,
|
||||
._frame = frame,
|
||||
._page = frame._page,
|
||||
._session = session,
|
||||
@@ -162,6 +167,7 @@ pub fn init(
|
||||
|
||||
self.js = try session.browser.env.createWorkerContext(self, .{
|
||||
.call_arena = call_arena,
|
||||
.local_arena = local_arena,
|
||||
.identity_arena = arena,
|
||||
.identity = &self._identity,
|
||||
});
|
||||
@@ -191,6 +197,7 @@ pub fn deinit(self: *WorkerGlobalScope) void {
|
||||
}
|
||||
browser.env.destroyContext(self.js);
|
||||
session.releaseArena(self.call_arena);
|
||||
session.releaseArena(self.local_arena);
|
||||
}
|
||||
|
||||
pub fn base(self: *const WorkerGlobalScope) [:0]const u8 {
|
||||
|
||||
@@ -34,7 +34,7 @@ pub fn init() XMLSerializer {
|
||||
|
||||
pub fn serializeToString(self: *const XMLSerializer, node: *Node, frame: *Frame) ![]const u8 {
|
||||
_ = self;
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
if (node.is(Node.Document)) |doc| {
|
||||
try dump.root(doc, .{ .shadow = .skip }, &buf.writer, frame);
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ const OffscreenCanvasRenderingContext2D = @This();
|
||||
_fill_style: color.RGBA = color.RGBA.Named.black,
|
||||
|
||||
pub fn getFillStyle(self: *const OffscreenCanvasRenderingContext2D, exec: *Execution) ![]const u8 {
|
||||
var w = std.Io.Writer.Allocating.init(exec.call_arena);
|
||||
var w = std.Io.Writer.Allocating.init(exec.local_arena);
|
||||
try self._fill_style.format(&w.writer);
|
||||
return w.written();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ const Lookup = std.StringArrayHashMapUnmanaged(void);
|
||||
const WHITESPACE = " \t\n\r\x0C";
|
||||
|
||||
pub fn length(self: *const DOMTokenList, frame: *Frame) !u32 {
|
||||
const tokens = try self.getTokens(frame.call_arena);
|
||||
const tokens = try self.getTokens(frame.local_arena);
|
||||
return @intCast(tokens.count());
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ pub fn length(self: *const DOMTokenList, frame: *Frame) !u32 {
|
||||
pub fn item(self: *const DOMTokenList, index: usize, frame: *Frame) !?[]const u8 {
|
||||
var i: usize = 0;
|
||||
|
||||
const allocator = frame.call_arena;
|
||||
const allocator = frame.local_arena;
|
||||
var seen: std.StringArrayHashMapUnmanaged(void) = .empty;
|
||||
|
||||
var it = std.mem.tokenizeAny(u8, self.getValue(), WHITESPACE);
|
||||
@@ -84,7 +84,7 @@ pub fn add(self: *DOMTokenList, tokens: []const []const u8, frame: *Frame) !void
|
||||
try validateToken(token);
|
||||
}
|
||||
|
||||
const allocator = frame.call_arena;
|
||||
const allocator = frame.local_arena;
|
||||
var lookup = try self.getTokens(allocator);
|
||||
try lookup.ensureUnusedCapacity(allocator, tokens.len);
|
||||
|
||||
@@ -100,7 +100,7 @@ pub fn remove(self: *DOMTokenList, tokens: []const []const u8, frame: *Frame) !v
|
||||
try validateToken(token);
|
||||
}
|
||||
|
||||
var lookup = try self.getTokens(frame.call_arena);
|
||||
var lookup = try self.getTokens(frame.local_arena);
|
||||
for (tokens) |token| {
|
||||
_ = lookup.orderedRemove(token);
|
||||
}
|
||||
@@ -151,8 +151,8 @@ pub fn replace(self: *DOMTokenList, old_token: []const u8, new_token: []const u8
|
||||
return error.InvalidCharacterError;
|
||||
}
|
||||
|
||||
const allocator = frame.call_arena;
|
||||
var lookup = try self.getTokens(frame.call_arena);
|
||||
const allocator = frame.local_arena;
|
||||
var lookup = try self.getTokens(allocator);
|
||||
|
||||
// Check if old_token exists
|
||||
if (!lookup.contains(old_token)) {
|
||||
|
||||
@@ -260,7 +260,7 @@ fn ctr(
|
||||
std.mem.writeInt(u128, &wrapped, readBlock(counter) & ~mask, .big);
|
||||
const second = try cbcOrCtr(cipher, key, &wrapped, data[split..], encrypting, false, exec);
|
||||
|
||||
const out = try exec.call_arena.alloc(u8, data.len);
|
||||
const out = try exec.local_arena.alloc(u8, data.len);
|
||||
@memcpy(out[0..split], first);
|
||||
@memcpy(out[split..], second);
|
||||
return out;
|
||||
@@ -308,7 +308,7 @@ fn cbcOrCtr(
|
||||
}
|
||||
|
||||
// Block ciphers may emit up to one extra block on top of the input.
|
||||
const out = try exec.call_arena.alloc(u8, data.len + 16);
|
||||
const out = try exec.local_arena.alloc(u8, data.len + 16);
|
||||
var out_len: c_int = 0;
|
||||
if (cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(data.ptr), @intCast(data.len), encrypting) != 1) {
|
||||
return error.OperationError;
|
||||
@@ -365,7 +365,7 @@ fn gcm(
|
||||
}
|
||||
|
||||
if (encrypting) {
|
||||
const out = try exec.call_arena.alloc(u8, data.len + tag_len);
|
||||
const out = try exec.local_arena.alloc(u8, data.len + tag_len);
|
||||
var out_len: c_int = 0;
|
||||
if (data.len > 0 and cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(data.ptr), @intCast(data.len), true) != 1) {
|
||||
return error.OperationError;
|
||||
@@ -391,7 +391,7 @@ fn gcm(
|
||||
return error.OperationError;
|
||||
}
|
||||
|
||||
const out = try exec.call_arena.alloc(u8, ct.len + 16);
|
||||
const out = try exec.local_arena.alloc(u8, ct.len + 16);
|
||||
var out_len: c_int = 0;
|
||||
if (ct.len > 0 and cipherUpdate(ctx, out.ptr, &out_len, @ptrCast(ct.ptr), @intCast(ct.len), false) != 1) {
|
||||
return error.OperationError;
|
||||
|
||||
@@ -236,7 +236,7 @@ pub fn deriveBits(
|
||||
if (crypto.EVP_PKEY_derive(ctx, null, &secret_len) != 1 or secret_len == 0) {
|
||||
return error.OperationError;
|
||||
}
|
||||
const secret = try exec.call_arena.alloc(u8, secret_len);
|
||||
const secret = try exec.local_arena.alloc(u8, secret_len);
|
||||
if (crypto.EVP_PKEY_derive(ctx, secret.ptr, &secret_len) != 1) {
|
||||
return error.OperationError;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ pub fn sign(
|
||||
return resolver.promise();
|
||||
}
|
||||
|
||||
const buffer = try exec.call_arena.alloc(u8, crypto.EVP_MD_size(crypto_key.getDigest()));
|
||||
const buffer = try exec.local_arena.alloc(u8, crypto.EVP_MD_size(crypto_key.getDigest()));
|
||||
var out_len: u32 = 0;
|
||||
// Try to sign.
|
||||
_ = crypto.HMAC(
|
||||
@@ -163,7 +163,6 @@ pub fn sign(
|
||||
buffer.ptr,
|
||||
&out_len,
|
||||
) orelse {
|
||||
exec.call_arena.free(buffer);
|
||||
// Failure.
|
||||
resolver.rejectError("HMAC.sign", .{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
return resolver.promise();
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn pbkdf2(
|
||||
) Error![]const u8 {
|
||||
try checkBaseKey(base_key, "PBKDF2", usage_ok);
|
||||
const digest = crypto.findDigest(params.hash.name()) catch return error.NotSupported;
|
||||
const out = try exec.call_arena.alloc(u8, try outputLen(length));
|
||||
const out = try exec.local_arena.alloc(u8, try outputLen(length));
|
||||
// A zero-length derivation is valid and yields an empty buffer; the C
|
||||
// routines reject a zero output length, so short-circuit here.
|
||||
if (out.len == 0) {
|
||||
@@ -97,7 +97,7 @@ pub fn hkdf(
|
||||
) Error![]const u8 {
|
||||
try checkBaseKey(base_key, "HKDF", usage_ok);
|
||||
const digest = crypto.findDigest(params.hash.name()) catch return error.NotSupported;
|
||||
const out = try exec.call_arena.alloc(u8, try outputLen(length));
|
||||
const out = try exec.local_arena.alloc(u8, try outputLen(length));
|
||||
// A zero-length derivation is valid and yields an empty buffer; the C
|
||||
// routines reject a zero output length, so short-circuit here.
|
||||
if (out.len == 0) {
|
||||
|
||||
@@ -138,8 +138,8 @@ pub fn deriveBits(
|
||||
return error.Internal;
|
||||
}
|
||||
|
||||
const derived_key = try exec.call_arena.alloc(u8, 32);
|
||||
errdefer exec.call_arena.free(derived_key);
|
||||
const derived_key = try exec.local_arena.alloc(u8, 32);
|
||||
errdefer exec.local_arena.free(derived_key);
|
||||
|
||||
var out_key_len: usize = derived_key.len;
|
||||
const result = crypto.EVP_PKEY_derive(ctx, derived_key.ptr, &out_key_len);
|
||||
|
||||
@@ -151,7 +151,7 @@ fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value:
|
||||
const normalized = normalizePropertyName(property_name, &frame.buf);
|
||||
|
||||
// Normalize the value for canonical serialization
|
||||
const normalized_value = try normalizePropertyValue(frame.call_arena, normalized, value);
|
||||
const normalized_value = try normalizePropertyValue(frame.local_arena, normalized, value);
|
||||
|
||||
// Find existing property
|
||||
if (self.findProperty(.wrap(normalized))) |existing| {
|
||||
@@ -206,7 +206,7 @@ pub fn setFloat(self: *CSSStyleDeclaration, value_: ?[]const u8, frame: *Frame)
|
||||
}
|
||||
|
||||
pub fn getCssText(self: *const CSSStyleDeclaration, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try self.format(&buf.writer);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn getStyle(self: *CSSStyleRule, frame: *Frame) !*CSSStyleProperties {
|
||||
pub fn getCssText(self: *CSSStyleRule, frame: *Frame) ![]const u8 {
|
||||
const style_props = try self.getStyle(frame);
|
||||
const style = style_props.asCSSStyleDeclaration();
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
var buf = std.Io.Writer.Allocating.init(frame.local_arena);
|
||||
try buf.writer.print("{s} {{ ", .{self._selector_text});
|
||||
try style.format(&buf.writer);
|
||||
try buf.writer.writeAll(" }");
|
||||
|
||||
@@ -66,7 +66,7 @@ pub fn getCssRules(self: *CSSStyleSheet, frame: *Frame) !*CSSRuleList {
|
||||
|
||||
if (self.getOwnerNode()) |owner| {
|
||||
if (owner.is(Element.Html.Style)) |style| {
|
||||
const text = try style.asNode().getTextContentAlloc(frame.call_arena);
|
||||
const text = try style.asNode().getTextContentAlloc(frame.local_arena);
|
||||
try self.replaceSync(text, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,17 +32,17 @@ const DOMStringMap = @This();
|
||||
_element: *Element,
|
||||
|
||||
fn getProperty(self: *DOMStringMap, name: String, frame: *Frame) !?String {
|
||||
const attr_name = try camelToKebab(frame.call_arena, name);
|
||||
const attr_name = try camelToKebab(frame.local_arena, name);
|
||||
return try self._element.getAttribute(attr_name, frame);
|
||||
}
|
||||
|
||||
fn setProperty(self: *DOMStringMap, name: String, value: String, frame: *Frame) !void {
|
||||
const attr_name = try camelToKebab(frame.call_arena, name);
|
||||
const attr_name = try camelToKebab(frame.local_arena, name);
|
||||
return self._element.setAttributeSafe(attr_name, value, frame);
|
||||
}
|
||||
|
||||
fn deleteProperty(self: *DOMStringMap, name: String, frame: *Frame) !void {
|
||||
const attr_name = try camelToKebab(frame.call_arena, name);
|
||||
const attr_name = try camelToKebab(frame.local_arena, name);
|
||||
try self._element.removeAttribute(attr_name, frame);
|
||||
}
|
||||
|
||||
|
||||
@@ -1598,7 +1598,7 @@ fn mergeTextNodes(left_node: *Node, right_node: *Node, frame: *Frame) !bool {
|
||||
|
||||
// both nodes are Text nodes
|
||||
|
||||
const merged = try std.mem.concat(frame.call_arena, u8, &.{ left.getData().str(), right.getData().str() });
|
||||
const merged = try std.mem.concat(frame.local_arena, u8, &.{ left.getData().str(), right.getData().str() });
|
||||
// set the left node to the merged value
|
||||
try left.setData(merged, frame);
|
||||
|
||||
@@ -1610,7 +1610,7 @@ fn mergeTextNodes(left_node: *Node, right_node: *Node, frame: *Frame) !bool {
|
||||
}
|
||||
|
||||
fn renderedTextFragment(value: []const u8, frame: *Frame) ![]Node.NodeOrText {
|
||||
const arena = frame.call_arena;
|
||||
const arena = frame.local_arena;
|
||||
var nodes: std.ArrayList(Node.NodeOrText) = .empty;
|
||||
|
||||
var rest = value;
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn setTarget(self: *Anchor, value: []const u8, frame: *Frame) !void {
|
||||
|
||||
pub fn getOrigin(self: *Anchor, frame: *Frame) ![]const u8 {
|
||||
const href = try getResolvedHref(self, frame) orelse return "";
|
||||
return (try URL.getOrigin(frame.call_arena, href)) orelse "null";
|
||||
return (try URL.getOrigin(frame.local_arena, href)) orelse "null";
|
||||
}
|
||||
|
||||
pub fn getHost(self: *Anchor, frame: *Frame) ![]const u8 {
|
||||
|
||||
@@ -51,7 +51,7 @@ pub fn setHref(self: *Area, value: []const u8, frame: *Frame) !void {
|
||||
|
||||
pub fn getOrigin(self: *Area, frame: *Frame) ![]const u8 {
|
||||
const href = try getResolvedHref(self, frame) orelse return "";
|
||||
return (try URL.getOrigin(frame.call_arena, href)) orelse "null";
|
||||
return (try URL.getOrigin(frame.local_arena, href)) orelse "null";
|
||||
}
|
||||
|
||||
pub fn getHost(self: *Area, frame: *Frame) ![]const u8 {
|
||||
|
||||
@@ -32,7 +32,7 @@ pub fn getHref(self: *Base, frame: *Frame) ![]const u8 {
|
||||
return "";
|
||||
}
|
||||
const owner = element.asConstNode().ownerFrame(frame);
|
||||
return URL.resolve(frame.call_arena, owner.url, href, .{});
|
||||
return URL.resolve(frame.local_arena, owner.url, href, .{});
|
||||
}
|
||||
|
||||
pub fn setHref(self: *Base, value: []const u8, frame: *Frame) !void {
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn getWidth(self: *const Canvas) u32 {
|
||||
}
|
||||
|
||||
pub fn setWidth(self: *Canvas, value: u32, frame: *Frame) !void {
|
||||
const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value});
|
||||
const str = try std.fmt.allocPrint(frame.local_arena, "{d}", .{value});
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(str), frame);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ pub fn getHeight(self: *const Canvas) u32 {
|
||||
}
|
||||
|
||||
pub fn setHeight(self: *Canvas, value: u32, frame: *Frame) !void {
|
||||
const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value});
|
||||
const str = try std.fmt.allocPrint(frame.local_arena, "{d}", .{value});
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(str), frame);
|
||||
}
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ pub fn getValueForJS(self: *const Input, frame: *Frame) ![]const u8 {
|
||||
if (fl._files.len == 0) {
|
||||
return "";
|
||||
}
|
||||
return try std.fmt.allocPrint(frame.call_arena, "C:\\fakepath\\{s}", .{fl._files[0]._name});
|
||||
return try std.fmt.allocPrint(frame.local_arena, "C:\\fakepath\\{s}", .{fl._files[0]._name});
|
||||
}
|
||||
|
||||
pub fn getValidationMessage(self: *Input, frame: *Frame) []const u8 {
|
||||
|
||||
@@ -691,10 +691,14 @@ pub const BrowserContext = struct {
|
||||
const call_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.call_arena");
|
||||
errdefer browser.arena_pool.release(call_arena);
|
||||
|
||||
const local_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.local_arena");
|
||||
errdefer browser.arena_pool.release(local_arena);
|
||||
|
||||
const world = try arena.create(IsolatedWorld);
|
||||
world.* = .{
|
||||
.arena = arena,
|
||||
.call_arena = call_arena,
|
||||
.local_arena = local_arena,
|
||||
.context = null,
|
||||
.browser = browser,
|
||||
.name = try arena.dupe(u8, world_name),
|
||||
@@ -1118,6 +1122,7 @@ const ScriptOnNewDocument = struct {
|
||||
const IsolatedWorld = struct {
|
||||
arena: Allocator,
|
||||
call_arena: Allocator,
|
||||
local_arena: Allocator,
|
||||
browser: *Browser,
|
||||
name: []const u8,
|
||||
context: ?*js.Context = null,
|
||||
@@ -1130,6 +1135,7 @@ const IsolatedWorld = struct {
|
||||
pub fn deinit(self: *IsolatedWorld) void {
|
||||
self.removeContext();
|
||||
self.browser.arena_pool.release(self.call_arena);
|
||||
self.browser.arena_pool.release(self.local_arena);
|
||||
self.browser.arena_pool.release(self.arena);
|
||||
}
|
||||
|
||||
@@ -1155,6 +1161,7 @@ const IsolatedWorld = struct {
|
||||
.identity = &self.identity,
|
||||
.identity_arena = self.arena,
|
||||
.call_arena = self.call_arena,
|
||||
.local_arena = self.local_arena,
|
||||
.debug_name = "IsolatedContext",
|
||||
});
|
||||
self.context = ctx;
|
||||
|
||||
Reference in New Issue
Block a user