Migrate Function and String

This commit is contained in:
Karl Seguin
2025-12-30 16:27:42 +08:00
parent 3b1cd06615
commit 8aaef674fe
24 changed files with 212 additions and 110 deletions

View File

@@ -449,7 +449,7 @@ const Function = union(enum) {
fn eqlFunction(self: Function, func: js.Function) bool {
return switch (self) {
.value => |v| return v.id == func.id,
.value => |v| return v.id() == func.id(),
else => false,
};
}

View File

@@ -1309,7 +1309,7 @@ pub fn appendNew(self: *Page, parent: *Node, child: Node.NodeOrText) !void {
// called from the parser when the node and all its children have been added
pub fn nodeComplete(self: *Page, node: *Node) !void {
Node.Build.call(node, "complete", .{ node, self }) catch |err| {
log.err(.bug, "build.complete", .{ .tag = node.getNodeName(self), .err = err });
log.err(.bug, "build.complete", .{ .tag = node.getNodeName(&self.buf), .err = err });
return err;
};
return self.nodeIsReady(true, node);

View File

@@ -65,10 +65,6 @@ call_arena: Allocator,
// the call which is calling the callback.
call_depth: usize = 0,
// Callbacks are PesistendObjects. When the context ends, we need
// to free every callback we created.
callbacks: std.ArrayListUnmanaged(v8.Persistent(v8.Function)) = .empty,
// Serves two purposes. Like `callbacks` above, this is used to free
// every PeristentObjet we've created during the lifetime of the context.
// More importantly, it serves as an identity map - for a given Zig
@@ -86,8 +82,8 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty,
// we now simply persist every time persist() is called.
js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty,
// tracks Global(v8.c.Value).
global_values: std.ArrayList(js.Global(js.Value)) = .empty,
global_functions: std.ArrayList(js.Global(js.Function)) = .empty,
// Various web APIs depend on having a persistent promise resolver. They
// require for this PromiseResolver to be valid for a lifetime longer than
@@ -169,6 +165,10 @@ pub fn deinit(self: *Context) void {
global.deinit();
}
for (self.global_functions.items) |*global| {
global.deinit();
}
for (self.persisted_promise_resolvers.items) |*p| {
p.deinit();
}
@@ -188,9 +188,6 @@ pub fn deinit(self: *Context) void {
}
}
for (self.callbacks.items) |*cb| {
cb.deinit();
}
if (self.handle_scope) |*scope| {
scope.deinit();
self.v8_context.exit();
@@ -199,10 +196,6 @@ pub fn deinit(self: *Context) void {
presistent_context.deinit();
}
fn trackCallback(self: *Context, pf: PersistentFunction) !void {
return self.callbacks.append(self.arena, pf);
}
// == Executors ==
pub fn eval(self: *Context, src: []const u8, name: ?[]const u8) !void {
_ = try self.exec(src, name);
@@ -392,13 +385,9 @@ pub fn createFunction(self: *Context, js_value: v8.Value) !js.Function {
// caller should have made sure this was a function
std.debug.assert(js_value.isFunction());
const func = v8.Persistent(v8.Function).init(self.isolate, js_value.castTo(v8.Function));
try self.trackCallback(func);
return .{
.func = func,
.context = self,
.id = js_value.castTo(v8.Object).getIdentityHash(),
.ctx = self,
.handle = @ptrCast(js_value.handle),
};
}
@@ -488,7 +477,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
if (T == js.Function) {
// we're returning a callback
return value.func.toValue();
return .{ .handle = @ptrCast(value.handle) };
}
if (T == js.Object) {
@@ -2031,7 +2020,7 @@ pub fn queueSlotchangeDelivery(self: *Context) !void {
}
pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
self.isolate.enqueueMicrotaskFunc(cb.func.castToFunction());
self.isolate.enqueueMicrotaskFunc(.{ .handle = cb.handle });
}
// == Misc ==

View File

@@ -20,24 +20,25 @@ const std = @import("std");
const js = @import("js.zig");
const v8 = js.v8;
const PersistentFunction = v8.Persistent(v8.Function);
const Allocator = std.mem.Allocator;
const Function = @This();
id: usize,
context: *js.Context,
ctx: *js.Context,
this: ?v8.Object = null,
func: PersistentFunction,
handle: *const v8.c.Function,
pub const Result = struct {
stack: ?[]const u8,
exception: []const u8,
};
pub fn id(self: *const Function) u32 {
return @as(u32, @bitCast(v8.c.v8__Object__GetIdentityHash(@ptrCast(self.handle))));
}
pub fn getName(self: *const Function, allocator: Allocator) ![]const u8 {
const name = self.func.castToFunction().getName();
const name = v8.c.v8__Function__GetName(self.handle).?;
return self.context.valueToString(name, .{ .allocator = allocator });
}
@@ -50,28 +51,27 @@ pub fn withThis(self: *const Function, value: anytype) !Function {
const this_obj = if (@TypeOf(value) == js.Object)
value.js_obj
else
(try self.context.zigValueToJs(value, .{})).castTo(v8.Object);
(try self.ctx.zigValueToJs(value, .{})).castTo(v8.Object);
return .{
.id = self.id,
.ctx = self.ctx,
.this = this_obj,
.func = self.func,
.context = self.context,
.handle = self.handle,
};
}
pub fn newInstance(self: *const Function, result: *Result) !js.Object {
const context = self.context;
const ctx = self.ctx;
var try_catch: js.TryCatch = undefined;
try_catch.init(context);
try_catch.init(ctx);
defer try_catch.deinit();
// This creates a new instance using this Function as a constructor.
// This returns a generic Object
const js_obj = self.func.castToFunction().initInstance(context.v8_context, &.{}) orelse {
// const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{}));
const handle = v8.c.v8__Function__NewInstance(self.handle, ctx.v8_context.handle, 0, null) orelse {
if (try_catch.hasCaught()) {
const allocator = context.call_arena;
const allocator = ctx.call_arena;
result.stack = try_catch.stack(allocator) catch null;
result.exception = (try_catch.exception(allocator) catch "???") orelse "???";
} else {
@@ -82,8 +82,8 @@ pub fn newInstance(self: *const Function, result: *Result) !js.Object {
};
return .{
.context = context,
.js_obj = js_obj,
.context = ctx,
.js_obj = .{ .handle = handle },
};
}
@@ -97,12 +97,13 @@ pub fn tryCall(self: *const Function, comptime T: type, args: anytype, result: *
pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, result: *Result) !T {
var try_catch: js.TryCatch = undefined;
try_catch.init(self.context);
try_catch.init(self.ctx);
defer try_catch.deinit();
return self.callWithThis(T, this, args) catch |err| {
if (try_catch.hasCaught()) {
const allocator = self.context.call_arena;
const allocator = self.ctx.call_arena;
result.stack = try_catch.stack(allocator) catch null;
result.exception = (try_catch.exception(allocator) catch @errorName(err)) orelse @errorName(err);
} else {
@@ -114,8 +115,9 @@ pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, a
}
pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype) !T {
const context = self.context;
const ctx = self.ctx;
<<<<<<< HEAD
// When we're calling a function from within JavaScript itself, this isn't
// necessary. We're within a Caller instantiation, which will already have
// incremented the call_depth and it won't decrement it until the Caller is
@@ -147,33 +149,35 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args
const fields = s.fields;
var js_args: [fields.len]v8.Value = undefined;
inline for (fields, 0..) |f, i| {
js_args[i] = try context.zigValueToJs(@field(aargs, f.name), .{});
js_args[i] = try ctx.zigValueToJs(@field(aargs, f.name), .{});
}
const cargs: [fields.len]v8.Value = js_args;
break :blk &cargs;
},
.pointer => blk: {
var values = try context.call_arena.alloc(v8.Value, args.len);
var values = try ctx.call_arena.alloc(v8.Value, args.len);
for (args, 0..) |a, i| {
values[i] = try context.zigValueToJs(a, .{});
values[i] = try ctx.zigValueToJs(a, .{});
}
break :blk values;
},
else => @compileError("JS Function called with invalid paremter type"),
};
const result = self.func.castToFunction().call(context.v8_context, js_this, js_args);
if (result == null) {
const c_args = @as(?[*]const ?*v8.c.Value, @ptrCast(js_args.ptr));
const handle = v8.c.v8__Function__Call(self.handle, ctx.v8_context.handle, js_this.handle, @as(c_int, @intCast(js_args.len)), c_args) orelse {
// std.debug.print("CB ERR: {s}\n", .{self.src() catch "???"});
return error.JSExecCallback;
}
};
if (@typeInfo(T) == .void) return {};
return context.jsValueToZig(T, result.?);
if (@typeInfo(T) == .void) {
return {};
}
return ctx.jsValueToZig(T, .{ .handle = handle });
}
fn getThis(self: *const Function) v8.Object {
return self.this orelse self.context.v8_context.getGlobal();
return self.this orelse self.ctx.v8_context.getGlobal();
}
pub fn src(self: *const Function) ![]const u8 {
@@ -182,8 +186,32 @@ pub fn src(self: *const Function) ![]const u8 {
}
pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
const func_obj = self.func.castToFunction().toObject();
const key = v8.String.initUtf8(self.context.isolate, name);
const value = func_obj.getValue(self.context.v8_context, key) catch return null;
return self.context.createValue(value);
const ctx = self.ctx;
const key = v8.String.initUtf8(ctx.isolate, name);
const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse {
return error.JsException;
};
return .{
.ctx = ctx,
.handle = handle,
};
}
pub fn persist(self: *const Function) !Function {
var ctx = self.ctx;
const global = js.Global(Function).init(ctx.isolate.handle, self.handle);
try ctx.global_functions.append(ctx.arena, global);
return .{
.ctx = ctx,
.this = self.this,
.handle = global.local(),
};
}
pub fn persistWithThis(self: *const Function, value: anytype) !Function {
var persisted = try self.persist();
return persisted.withThis(value);
}

53
src/browser/js/String.zig Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("js.zig");
const Allocator = std.mem.Allocator;
const v8 = js.v8;
const String = @This();
ctx: *js.Context,
handle: *const v8.c.String,
pub const ToZigOpts = struct {
allocator: ?Allocator = null,
};
pub fn toZig(self: String, opts: ToZigOpts) ![]u8 {
return self._toZig(false, opts);
}
pub fn toZigZ(self: String, opts: ToZigOpts) ![:0]u8 {
return self._toZig(true, opts);
}
fn _toZig(self: String, comptime null_terminate: bool, opts: ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) {
const isolate = self.ctx.isolate.handle;
const allocator = opts.allocator orelse self.ctx.call_arena;
const len: u32 = @intCast(v8.c.v8__String__Utf8Length(self.handle, isolate));
const buf = if (null_terminate) try allocator.allocSentinel(u8, len, 0) else try allocator.alloc(u8, len);
const options = v8.c.NO_NULL_TERMINATION | v8.c.REPLACE_INVALID_UTF8;
const n = v8.c.v8__String__WriteUtf8(self.handle, isolate, buf.ptr, buf.len, options);
std.debug.assert(n == len);
return buf;
}

View File

@@ -50,8 +50,34 @@ pub fn isUndefined(self: Value) bool {
return self.js_val.isUndefined();
}
pub fn toString(self: Value, allocator: Allocator) ![]const u8 {
return self.ctx.valueToString(.{ .handle = self.handle }, .{ .allocator = allocator });
pub fn isSymbol(self: Value) bool {
return v8.c.v8__Value__IsSymbol(self.handle);
}
pub fn toString(self: Value, opts: js.String.ToZigOpts) ![]u8 {
return self._toString(false, opts);
}
pub fn toStringZ(self: Value, opts: js.String.ToZigOpts) ![:0]u8 {
return self._toString(true, opts);
}
fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) {
const ctx = self.ctx;
if (self.isSymbol()) {
const sym_handle = v8.c.v8__Symbol__Description(@ptrCast(self.handle), ctx.isolate.handle).?;
return _toString(.{ .handle = @ptrCast(sym_handle), .ctx = ctx }, null_terminate, opts);
}
const str_handle = v8.c.v8__Value__ToString(self.handle, ctx.v8_context.handle) orelse {
return error.JsException;
};
const str = js.String{ .ctx = ctx, .handle = str_handle };
if (comptime null_terminate) {
return js.String.toZigZ(str, opts);
}
return js.String.toZig(str, opts);
}
pub fn toBool(self: Value) bool {
@@ -67,7 +93,7 @@ pub fn fromJson(ctx: *js.Context, json: []const u8) !Value {
pub fn persist(self: Value) !Value {
var ctx = self.ctx;
const global = js.Global(Value).init(ctx.isolate.handle, self);
const global = js.Global(Value).init(ctx.isolate.handle, self.handle);
try ctx.global_values.append(ctx.arena, global);
return .{

View File

@@ -29,9 +29,9 @@ pub fn Global(comptime T: type) type {
const Self = @This();
pub fn init(isolate: *v8.c.Isolate, data: T) Self {
pub fn init(isolate: *v8.c.Isolate, handle: H) Self {
var global: v8.c.Global = undefined;
v8.c.v8__Global__New(isolate, data.handle, &global);
v8.c.v8__Global__New(isolate, handle, &global);
return .{
.global = global,
};

View File

@@ -33,6 +33,7 @@ pub const Platform = @import("Platform.zig");
pub const This = @import("This.zig");
pub const Value = @import("Value.zig");
pub const Array = @import("Array.zig");
pub const String = @import("String.zig");
pub const Object = @import("Object.zig");
pub const TryCatch = @import("TryCatch.zig");
pub const Function = @import("Function.zig");
@@ -157,15 +158,6 @@ pub const PersistentPromiseResolver = struct {
pub const Promise = v8.Promise;
// When doing jsValueToZig, string ([]const u8) are managed by the
// call_arena. That means that if the API wants to persist the string
// (which is relatively common), it needs to dupe it again.
// If the parameter is an Env.String rather than a []const u8, then
// the page's arena will be used (rather than the call arena).
pub const String = struct {
string: []const u8,
};
pub const Exception = struct {
inner: v8.Value,
context: *const Context,

View File

@@ -51,7 +51,7 @@ pub fn getOnAbort(self: *const AbortSignal) ?js.Function {
pub fn setOnAbort(self: *AbortSignal, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_abort = try cb.withThis(self);
self._on_abort = try cb.persistWithThis(self);
} else {
self._on_abort = null;
}

View File

@@ -63,7 +63,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
const definition = try page._factory.create(CustomElementDefinition{
.name = owned_name,
.constructor = constructor,
.constructor = try constructor.persist(),
.extends = extends_tag,
});
@@ -73,7 +73,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
var js_arr = observed_attrs.toArray();
for (0..js_arr.len()) |i| {
const attr_val = js_arr.get(@intCast(i)) catch continue;
const attr_name = attr_val.toString(page.arena) catch continue;
const attr_name = attr_val.toString(.{ .allocator = page.arena }) catch continue;
const owned_attr = page.dupeString(attr_name) catch continue;
definition.observed_attributes.put(page.arena, owned_attr, {}) catch continue;
}

View File

@@ -72,8 +72,8 @@ pub fn addEventListener(self: *EventTarget, typ: []const u8, callback_: ?EventLi
const callback = callback_ orelse return;
const em_callback = switch (callback) {
.function => |func| EventManager.Callback{ .function = func },
.object => |obj| EventManager.Callback{ .object = try obj.persist() },
.function => |func| EventManager.Callback{ .function = try func.persist() },
};
const options = blk: {

View File

@@ -70,7 +70,12 @@ pub fn init(callback: js.Function, options: ?ObserverInit, page: *Page) !*Inters
.array => |arr| try page.arena.dupe(f64, arr),
};
return page._factory.create(IntersectionObserver{ ._callback = callback, ._root = opts.root, ._root_margin = root_margin, ._threshold = threshold });
return page._factory.create(IntersectionObserver{
._callback = try callback.persist(),
._root = opts.root,
._root_margin = root_margin,
._threshold = threshold,
});
}
pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void {

View File

@@ -68,7 +68,7 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?N
while (try it.next()) |name| {
const js_value = try js_obj.get(name);
const value = try js_value.toString(arena);
const value = try js_value.toString(.{ .allocator = arena });
const normalized = if (comptime normalizer) |n| n(name, page) else name;
list._entries.appendAssumeCapacity(.{

View File

@@ -94,7 +94,7 @@ pub fn getOnMessage(self: *const MessagePort) ?js.Function {
pub fn setOnMessage(self: *MessagePort, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_message = cb;
self._on_message = try cb.persist();
} else {
self._on_message = null;
}
@@ -106,7 +106,7 @@ pub fn getOnMessageError(self: *const MessagePort) ?js.Function {
pub fn setOnMessageError(self: *MessagePort, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_message_error = cb;
self._on_message_error = try cb.persist();
} else {
self._on_message_error = null;
}

View File

@@ -55,7 +55,7 @@ pub const ObserveOptions = struct {
pub fn init(callback: js.Function, page: *Page) !*MutationObserver {
return page._factory.create(MutationObserver{
._callback = callback,
._callback = try callback.persist(),
});
}

View File

@@ -36,10 +36,13 @@ pub const FilterOpts = union(enum) {
pub fn init(opts_: ?FilterOpts) !NodeFilter {
const opts = opts_ orelse return .{ ._func = null, ._original_filter = null };
const func = switch (opts) {
.function => |func| func,
.object => |obj| obj.acceptNode,
.function => |func| try func.persist(),
.object => |obj| try obj.acceptNode.persist(),
};
return .{
._func = func,
._original_filter = opts_,
};
return .{ ._func = func, ._original_filter = opts_ };
}
// Constants

View File

@@ -150,7 +150,7 @@ pub fn getOnLoad(self: *const Window) ?js.Function {
}
pub fn setOnLoad(self: *Window, setter: ?FunctionSetter) !void {
self._on_load = getFunctionFromSetter(setter);
self._on_load = try getFunctionFromSetter(setter);
}
pub fn getOnPageShow(self: *const Window) ?js.Function {
@@ -158,7 +158,7 @@ pub fn getOnPageShow(self: *const Window) ?js.Function {
}
pub fn setOnPageShow(self: *Window, setter: ?FunctionSetter) !void {
self._on_pageshow = getFunctionFromSetter(setter);
self._on_pageshow = try getFunctionFromSetter(setter);
}
pub fn getOnPopState(self: *const Window) ?js.Function {
@@ -166,7 +166,7 @@ pub fn getOnPopState(self: *const Window) ?js.Function {
}
pub fn setOnPopState(self: *Window, setter: ?FunctionSetter) !void {
self._on_popstate = getFunctionFromSetter(setter);
self._on_popstate = try getFunctionFromSetter(setter);
}
pub fn getOnError(self: *const Window) ?js.Function {
@@ -174,7 +174,7 @@ pub fn getOnError(self: *const Window) ?js.Function {
}
pub fn setOnError(self: *Window, setter: ?FunctionSetter) !void {
self._on_error = getFunctionFromSetter(setter);
self._on_error = try getFunctionFromSetter(setter);
}
pub fn getOnUnhandledRejection(self: *const Window) ?js.Function {
@@ -182,7 +182,7 @@ pub fn getOnUnhandledRejection(self: *const Window) ?js.Function {
}
pub fn setOnUnhandledRejection(self: *Window, setter: ?FunctionSetter) !void {
self._on_unhandled_rejection = getFunctionFromSetter(setter);
self._on_unhandled_rejection = try getFunctionFromSetter(setter);
}
pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, page: *Page) !js.Promise {
@@ -497,7 +497,7 @@ fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: Schedul
errdefer _ = self._timers.remove(timer_id);
const callback = try page._factory.create(ScheduleCallback{
.cb = cb,
.cb = try cb.persist(),
.page = page,
.mode = opts.mode,
.name = opts.name,
@@ -622,10 +622,10 @@ const FunctionSetter = union(enum) {
// window.onload = {}; doesn't fail, but it doesn't do anything.
// seems like setting to null is ok (though, at least on Firefix, it preserves
// the original value, which we could do, but why?)
fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function {
fn getFunctionFromSetter(setter_: ?FunctionSetter) !?js.Function {
const setter = setter_ orelse return null;
return switch (setter) {
.func => |func| func,
.func => |func| try func.persist(),
.anything => null,
};
}

View File

@@ -80,7 +80,7 @@ pub fn getOnLoad(self: *const Script) ?js.Function {
pub fn setOnLoad(self: *Script, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_load = cb;
self._on_load = try cb.persist();
} else {
self._on_load = null;
}
@@ -92,7 +92,7 @@ pub fn getOnError(self: *const Script) ?js.Function {
pub fn setOnError(self: *Script, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_error = cb;
self._on_error = try cb.persist();
} else {
self._on_error = null;
}
@@ -136,17 +136,23 @@ pub const Build = struct {
self._src = element.getAttributeSafe("src") orelse "";
if (element.getAttributeSafe("onload")) |on_load| {
self._on_load = page.js.stringToFunction(on_load) catch |err| blk: {
self._on_load = blk: {
const func = page.js.stringToFunction(on_load) catch |err| {
log.err(.js, "script.onload", .{ .err = err, .str = on_load });
break :blk null;
};
break :blk try func.persist();
};
}
if (element.getAttributeSafe("onerror")) |on_error| {
self._on_error = page.js.stringToFunction(on_error) catch |err| blk: {
self._on_error = blk: {
const func = page.js.stringToFunction(on_error) catch |err| {
log.err(.js, "script.onerror", .{ .err = err, .str = on_error });
break :blk null;
};
break :blk try func.persist();
};
}
}
};

View File

@@ -79,7 +79,7 @@ pub fn getOnEnter(self: *const TextTrackCue) ?js.Function {
pub fn setOnEnter(self: *TextTrackCue, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_enter = try cb.withThis(self);
self._on_enter = try cb.persistWithThis(self);
} else {
self._on_enter = null;
}
@@ -91,7 +91,7 @@ pub fn getOnExit(self: *const TextTrackCue) ?js.Function {
pub fn setOnExit(self: *TextTrackCue, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_exit = try cb.withThis(self);
self._on_exit = try cb.persistWithThis(self);
} else {
self._on_exit = null;
}

View File

@@ -57,7 +57,7 @@ pub fn getOnCurrentEntryChange(self: *NavigationEventTarget) ?js.Function {
pub fn setOnCurrentEntryChange(self: *NavigationEventTarget, listener: ?js.Function) !void {
if (listener) |listen| {
self._on_currententrychange = try listen.withThis(self);
self._on_currententrychange = try listen.persistWithThis(self);
} else {
self._on_currententrychange = null;
}

View File

@@ -50,7 +50,7 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*URLSearchParams {
break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page);
}
if (js_val.isString()) {
break :blk try paramsFromString(arena, try js_val.toString(arena), &page.buf);
break :blk try paramsFromString(arena, try js_val.toString(.{ .allocator = arena }), &page.buf);
}
return error.InvalidArgument;
},

View File

@@ -104,7 +104,7 @@ pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function {
pub fn setOnReadyStateChange(self: *XMLHttpRequest, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_ready_state_change = try cb.withThis(self);
self._on_ready_state_change = try cb.persistWithThis(self);
} else {
self._on_ready_state_change = null;
}

View File

@@ -77,7 +77,7 @@ pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnAbort(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_abort = try cb.withThis(self);
self._on_abort = try cb.persistWithThis(self);
} else {
self._on_abort = null;
}
@@ -89,7 +89,7 @@ pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnError(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_error = try cb.withThis(self);
self._on_error = try cb.persistWithThis(self);
} else {
self._on_error = null;
}
@@ -101,7 +101,7 @@ pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnLoad(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_load = try cb.withThis(self);
self._on_load = try cb.persistWithThis(self);
} else {
self._on_load = null;
}
@@ -113,7 +113,7 @@ pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnLoadEnd(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_load_end = try cb.withThis(self);
self._on_load_end = try cb.persistWithThis(self);
} else {
self._on_load_end = null;
}
@@ -125,7 +125,7 @@ pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnLoadStart(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_load_start = try cb.withThis(self);
self._on_load_start = try cb.persistWithThis(self);
} else {
self._on_load_start = null;
}
@@ -137,7 +137,7 @@ pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnProgress(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_progress = try cb.withThis(self);
self._on_progress = try cb.persistWithThis(self);
} else {
self._on_progress = null;
}
@@ -149,7 +149,7 @@ pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function {
pub fn setOnTimeout(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
if (cb_) |cb| {
self._on_timeout = try cb.withThis(self);
self._on_timeout = try cb.persistWithThis(self);
} else {
self._on_timeout = null;
}

View File

@@ -85,7 +85,7 @@ pub fn init(src_: ?UnderlyingSource, strategy_: ?QueueingStrategy, page: *Page)
}
if (src.pull) |pull| {
self._pull_fn = pull;
self._pull_fn = try pull.persist();
try self.callPullIfNeeded();
}
}