mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Migrate Function and String
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ==
|
||||
|
||||
@@ -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
53
src/browser/js/String.zig
Normal 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;
|
||||
}
|
||||
@@ -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 .{
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(.{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user