Make Global Function explicit.

This is the first in a series of changes to make globals explicit. The ultimate
goal of having explicit Globals is to move away from the global HandleScope and
to explicit HandleScopes.

Currently, we treat globals and locals interchangeably. In fact, for Global ->
Local, we just ptrCast. This works because we have 1 global HandleScope, which
effectively disables V8's GC and thus nothing ever gets moved.

If we're going to introduce explicit HandleScopes, then we need to first have
correct Globals. Specifically, when we want to act on the global, we need to
get the local value, and that will eventually mean making sure there's a
HandleScope.

While adding explicit globals, we're keeping the global HandleScope so that we
can minimize the change. So, given that we still have the global HandleScope
the change is largely two things:
1 - js.Function.persit() returns a js.Function.Global. Types that persist global
   functions must be updated to js.Function.Global.
2 - To turn js.Function.Global -> js.Function, we need to call .local() on it.

The bridge has been updated to support js.Function.Global for both input and
output parameters. Thus, window.setOnLoad can now directly take a
js.Function.Global, and window.getOnLoad can directly return that
js.Function.Global.
This commit is contained in:
Karl Seguin
2026-01-14 15:12:22 +08:00
parent a30c65966b
commit 5def997bed
25 changed files with 198 additions and 191 deletions

View File

@@ -5,11 +5,11 @@
.fingerprint = 0xda130f3af836cea0, // Changing this has security and trust implications. .fingerprint = 0xda130f3af836cea0, // Changing this has security and trust implications.
.minimum_zig_version = "0.15.2", .minimum_zig_version = "0.15.2",
.dependencies = .{ .dependencies = .{
.v8 = .{ //.v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/5b0555e6b6154f957f9d7002ecb8005cc5a41b7a.tar.gz", // .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/5b0555e6b6154f957f9d7002ecb8005cc5a41b7a.tar.gz",
.hash = "v8-0.0.0-xddH6xUqBABofwwIBsof3cD3c2FstBvm7_VzoughX1Km", // .hash = "v8-0.0.0-xddH6xUqBABofwwIBsof3cD3c2FstBvm7_VzoughX1Km",
}, //},
//.v8 = .{ .path = "../zig-v8-fork" }, .v8 = .{ .path = "../zig-v8-fork" },
.@"boringssl-zig" = .{ .@"boringssl-zig" = .{
.url = "git+https://github.com/Syndica/boringssl-zig.git#c53df00d06b02b755ad88bbf4d1202ed9687b096", .url = "git+https://github.com/Syndica/boringssl-zig.git#c53df00d06b02b755ad88bbf4d1202ed9687b096",
.hash = "boringssl-0.1.0-VtJeWehMAAA4RNnwRnzEvKcS9rjsR1QVRw1uJrwXxmVK", .hash = "boringssl-0.1.0-VtJeWehMAAA4RNnwRnzEvKcS9rjsR1QVRw1uJrwXxmVK",

View File

@@ -102,7 +102,7 @@ pub fn register(self: *EventManager, target: *EventTarget, typ: []const u8, call
} }
const func = switch (callback) { const func = switch (callback) {
.function => |f| Function{ .value = f }, .function => |f| Function{ .value = try f.persist() },
.object => |o| Function{ .object = o }, .object => |o| Function{ .object = o },
}; };
@@ -368,7 +368,7 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe
} }
switch (listener.function) { switch (listener.function) {
.value => |value| try value.callWithThis(void, current_target, .{event}), .value => |value| try value.local().callWithThis(void, current_target, .{event}),
.string => |string| { .string => |string| {
const str = try page.call_arena.dupeZ(u8, string.str()); const str = try page.call_arena.dupeZ(u8, string.str());
try self.page.js.eval(str, null); try self.page.js.eval(str, null);
@@ -443,13 +443,13 @@ const Listener = struct {
}; };
const Function = union(enum) { const Function = union(enum) {
value: js.Function, value: js.Function.Global,
string: String, string: String,
object: js.Object, object: js.Object,
fn eqlFunction(self: Function, func: js.Function) bool { fn eqlFunction(self: Function, func: js.Function) bool {
return switch (self) { return switch (self) {
.value => |v| return v.id() == func.id(), .value => |v| v.isEqual(func),
else => false, else => false,
}; };
} }

View File

@@ -562,18 +562,20 @@ fn _documentIsComplete(self: *Page) !void {
// this event is weird, it's dispatched directly on the window, but // this event is weird, it's dispatched directly on the window, but
// with the document as the target // with the document as the target
event._target = self.document.asEventTarget(); event._target = self.document.asEventTarget();
const on_load = if (self.window._on_load) |*g| g.local() else null;
try self._event_manager.dispatchWithFunction( try self._event_manager.dispatchWithFunction(
self.window.asEventTarget(), self.window.asEventTarget(),
event, event,
self.window._on_load, on_load,
.{ .inject_target = false, .context = "page load" }, .{ .inject_target = false, .context = "page load" },
); );
const pageshow_event = try PageTransitionEvent.initTrusted("pageshow", .{}, self); const pageshow_event = try PageTransitionEvent.initTrusted("pageshow", .{}, self);
const on_pageshow = if (self.window._on_pageshow) |*g| g.local() else null;
try self._event_manager.dispatchWithFunction( try self._event_manager.dispatchWithFunction(
self.window.asEventTarget(), self.window.asEventTarget(),
pageshow_event.asEvent(), pageshow_event.asEvent(),
self.window._on_pageshow, on_pageshow,
.{ .context = "page show" }, .{ .context = "page show" },
); );
} }
@@ -1997,7 +1999,7 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
defer self._upgrading_element = prev_upgrading; defer self._upgrading_element = prev_upgrading;
var caught: JS.TryCatch.Caught = undefined; var caught: JS.TryCatch.Caught = undefined;
_ = def.constructor.newInstance(&caught) catch |err| { _ = def.constructor.local().newInstance(&caught) catch |err| {
log.warn(.js, "custom element constructor", .{ .name = name, .err = err, .caught = caught }); log.warn(.js, "custom element constructor", .{ .name = name, .err = err, .caught = caught });
return node; return node;
}; };

View File

@@ -844,8 +844,9 @@ pub const Script = struct {
self.executeCallback("error", script_element._on_error, page); self.executeCallback("error", script_element._on_error, page);
} }
fn executeCallback(self: *const Script, comptime typ: []const u8, cb_: ?js.Function, page: *Page) void { fn executeCallback(self: *const Script, comptime typ: []const u8, cb_: ?js.Function.Global, page: *Page) void {
const cb = cb_ orelse return; const cb_global = cb_ orelse return;
const cb = cb_global.local();
const Event = @import("webapi/Event.zig"); const Event = @import("webapi/Event.zig");
const event = Event.initTrusted(typ, .{}, page) catch |err| { const event = Event.initTrusted(typ, .{}, page) catch |err| {

View File

@@ -81,7 +81,7 @@ global_values: std.ArrayList(js.Global(js.Value)) = .empty,
global_objects: std.ArrayList(js.Global(js.Object)) = .empty, global_objects: std.ArrayList(js.Global(js.Object)) = .empty,
global_modules: std.ArrayList(js.Global(js.Module)) = .empty, global_modules: std.ArrayList(js.Global(js.Module)) = .empty,
global_promises: std.ArrayList(js.Global(js.Promise)) = .empty, global_promises: std.ArrayList(js.Global(js.Promise)) = .empty,
global_functions: std.ArrayList(js.Global(js.Function)) = .empty, global_functions: std.ArrayList(v8.Global) = .empty,
global_promise_resolvers: std.ArrayList(js.Global(js.PromiseResolver)) = .empty, global_promise_resolvers: std.ArrayList(js.Global(js.PromiseResolver)) = .empty,
// Our module cache: normalized module specifier => module. // Our module cache: normalized module specifier => module.
@@ -154,7 +154,7 @@ pub fn deinit(self: *Context) void {
} }
for (self.global_functions.items) |*global| { for (self.global_functions.items) |*global| {
global.deinit(); v8.v8__Global__Reset(global);
} }
for (self.global_promises.items) |*global| { for (self.global_promises.items) |*global| {
@@ -452,6 +452,11 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
return .{ .ctx = self, .handle = @ptrCast(value.handle) }; return .{ .ctx = self, .handle = @ptrCast(value.handle) };
} }
if (T == js.Function.Global) {
// Auto-convert Global to local for bridge
return .{ .ctx = self, .handle = @ptrCast(value.local().handle) };
}
if (T == js.Object) { if (T == js.Object) {
// we're returning a v8.Object // we're returning a v8.Object
return .{ .ctx = self, .handle = @ptrCast(value.handle) }; return .{ .ctx = self, .handle = @ptrCast(value.handle) };
@@ -778,6 +783,13 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: js.Value) !?T {
} }
return try self.newFunction(js_value); return try self.newFunction(js_value);
}, },
js.Function.Global => {
if (!js_value.isFunction()) {
return null;
}
const func = try self.newFunction(js_value);
return try func.persist();
},
// zig fmt: off // zig fmt: off
js.TypedArray(u8), js.TypedArray(u16), js.TypedArray(u32), js.TypedArray(u64), js.TypedArray(u8), js.TypedArray(u16), js.TypedArray(u32), js.TypedArray(u64),
js.TypedArray(i8), js.TypedArray(i16), js.TypedArray(i32), js.TypedArray(i64), js.TypedArray(i8), js.TypedArray(i16), js.TypedArray(i32), js.TypedArray(i64),

View File

@@ -33,10 +33,6 @@ pub const Result = struct {
exception: []const u8, exception: []const u8,
}; };
pub fn id(self: *const Function) u32 {
return @as(u32, @bitCast(v8.v8__Object__GetIdentityHash(@ptrCast(self.handle))));
}
pub fn withThis(self: *const Function, value: anytype) !Function { pub fn withThis(self: *const Function, value: anytype) !Function {
const this_obj = if (@TypeOf(value) == js.Object) const this_obj = if (@TypeOf(value) == js.Object)
value.handle value.handle
@@ -172,20 +168,41 @@ pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
}; };
} }
pub fn persist(self: *const Function) !Function { pub fn persist(self: *const Function) !Global {
var ctx = self.ctx; var ctx = self.ctx;
const global = js.Global(Function).init(ctx.isolate.handle, self.handle); var global: v8.Global = undefined;
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
try ctx.global_functions.append(ctx.arena, global); try ctx.global_functions.append(ctx.arena, global);
return .{ return .{
.handle = global,
.ctx = ctx, .ctx = ctx,
.this = self.this,
.handle = global.local(),
}; };
} }
pub fn persistWithThis(self: *const Function, value: anytype) !Function { pub fn persistWithThis(self: *const Function, value: anytype) !Global {
var persisted = try self.persist(); const with_this = try self.withThis(value);
return persisted.withThis(value); return with_this.persist();
} }
pub const Global = struct {
handle: v8.Global,
ctx: *js.Context,
pub fn deinit(self: *Global) void {
v8.v8__Global__Reset(&self.handle);
}
pub fn local(self: *const Global) Function {
return .{
.ctx = self.ctx,
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, self.ctx.isolate.handle)),
};
}
pub fn isEqual(self: *const Global, other: Function) bool {
return v8.v8__Global__IsEqual(&self.handle, other.handle);
}
};

View File

@@ -29,7 +29,7 @@ const AbortSignal = @This();
_proto: *EventTarget, _proto: *EventTarget,
_aborted: bool = false, _aborted: bool = false,
_reason: Reason = .undefined, _reason: Reason = .undefined,
_on_abort: ?js.Function = null, _on_abort: ?js.Function.Global = null,
pub fn init(page: *Page) !*AbortSignal { pub fn init(page: *Page) !*AbortSignal {
return page._factory.eventTarget(AbortSignal{ return page._factory.eventTarget(AbortSignal{
@@ -45,16 +45,12 @@ pub fn getReason(self: *const AbortSignal) Reason {
return self._reason; return self._reason;
} }
pub fn getOnAbort(self: *const AbortSignal) ?js.Function { pub fn getOnAbort(self: *const AbortSignal) ?js.Function.Global {
return self._on_abort; return self._on_abort;
} }
pub fn setOnAbort(self: *AbortSignal, cb_: ?js.Function) !void { pub fn setOnAbort(self: *AbortSignal, cb: ?js.Function.Global) !void {
if (cb_) |cb| { self._on_abort = cb;
self._on_abort = try cb.persistWithThis(self);
} else {
self._on_abort = null;
}
} }
pub fn asEventTarget(self: *AbortSignal) *EventTarget { pub fn asEventTarget(self: *AbortSignal) *EventTarget {
@@ -81,10 +77,11 @@ pub fn abort(self: *AbortSignal, reason_: ?Reason, page: *Page) !void {
// Dispatch abort event // Dispatch abort event
const event = try Event.initTrusted("abort", .{}, page); const event = try Event.initTrusted("abort", .{}, page);
const func = if (self._on_abort) |*g| g.local() else null;
try page._event_manager.dispatchWithFunction( try page._event_manager.dispatchWithFunction(
self.asEventTarget(), self.asEventTarget(),
event, event,
self._on_abort, func,
.{ .context = "abort signal" }, .{ .context = "abort signal" },
); );
} }

View File

@@ -24,7 +24,7 @@ const Element = @import("Element.zig");
const CustomElementDefinition = @This(); const CustomElementDefinition = @This();
name: []const u8, name: []const u8,
constructor: js.Function, constructor: js.Function.Global,
observed_attributes: std.StringHashMapUnmanaged(void) = .{}, observed_attributes: std.StringHashMapUnmanaged(void) = .{},
// For customized built-in elements, this is the element tag they extend (e.g., .button) // For customized built-in elements, this is the element tag they extend (e.g., .button)
// For autonomous custom elements, this is null // For autonomous custom elements, this is null

View File

@@ -110,7 +110,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
} }
} }
pub fn get(self: *CustomElementRegistry, name: []const u8) ?js.Function { pub fn get(self: *CustomElementRegistry, name: []const u8) ?js.Function.Global {
const definition = self._definitions.get(name) orelse return null; const definition = self._definitions.get(name) orelse return null;
return definition.constructor; return definition.constructor;
} }
@@ -175,7 +175,7 @@ pub fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinitio
defer page._upgrading_element = prev_upgrading; defer page._upgrading_element = prev_upgrading;
var caught: js.TryCatch.Caught = undefined; var caught: js.TryCatch.Caught = undefined;
_ = definition.constructor.newInstance(&caught) catch |err| { _ = definition.constructor.local().newInstance(&caught) catch |err| {
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err, .caught = caught }); log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err, .caught = caught });
return error.CustomElementUpgradeFailed; return error.CustomElementUpgradeFailed;
}; };

View File

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

View File

@@ -81,10 +81,11 @@ fn goInner(delta: i32, page: *Page) !void {
if (try page.isSameOrigin(url)) { if (try page.isSameOrigin(url)) {
const event = try PopStateEvent.initTrusted("popstate", .{ .state = entry._state.value }, page); const event = try PopStateEvent.initTrusted("popstate", .{ .state = entry._state.value }, page);
const func = if (page.window._on_popstate) |*g| g.local() else null;
try page._event_manager.dispatchWithFunction( try page._event_manager.dispatchWithFunction(
page.window.asEventTarget(), page.window.asEventTarget(),
event.asEvent(), event.asEvent(),
page.window._on_popstate, func,
.{ .context = "Pop State" }, .{ .context = "Pop State" },
); );
} }

View File

@@ -32,7 +32,7 @@ pub fn registerTypes() []const type {
const IntersectionObserver = @This(); const IntersectionObserver = @This();
_callback: js.Function, _callback: js.Function.Global,
_observing: std.ArrayList(*Element) = .{}, _observing: std.ArrayList(*Element) = .{},
_root: ?*Element = null, _root: ?*Element = null,
_root_margin: []const u8 = "0px", _root_margin: []const u8 = "0px",
@@ -59,7 +59,7 @@ pub const ObserverInit = struct {
}; };
}; };
pub fn init(callback: js.Function, options: ?ObserverInit, page: *Page) !*IntersectionObserver { pub fn init(callback: js.Function.Global, options: ?ObserverInit, page: *Page) !*IntersectionObserver {
const opts = options orelse ObserverInit{}; const opts = options orelse ObserverInit{};
const root_margin = if (opts.rootMargin) |rm| try page.arena.dupe(u8, rm) else "0px"; const root_margin = if (opts.rootMargin) |rm| try page.arena.dupe(u8, rm) else "0px";
@@ -73,7 +73,7 @@ pub fn init(callback: js.Function, options: ?ObserverInit, page: *Page) !*Inters
}; };
return page._factory.create(IntersectionObserver{ return page._factory.create(IntersectionObserver{
._callback = try callback.persist(), ._callback = callback,
._root = opts.root, ._root = opts.root,
._root_margin = root_margin, ._root_margin = root_margin,
._threshold = threshold, ._threshold = threshold,
@@ -246,7 +246,7 @@ pub fn deliverEntries(self: *IntersectionObserver, page: *Page) !void {
const entries = try self.takeRecords(page); const entries = try self.takeRecords(page);
var caught: js.TryCatch.Caught = undefined; var caught: js.TryCatch.Caught = undefined;
self._callback.tryCall(void, .{ entries, self }, &caught) catch |err| { self._callback.local().tryCall(void, .{ entries, self }, &caught) catch |err| {
log.err(.page, "IntsctObserver.deliverEntries", .{ .err = err, .caught = caught }); log.err(.page, "IntsctObserver.deliverEntries", .{ .err = err, .caught = caught });
return err; return err;
}; };

View File

@@ -29,8 +29,8 @@ const MessagePort = @This();
_proto: *EventTarget, _proto: *EventTarget,
_enabled: bool = false, _enabled: bool = false,
_closed: bool = false, _closed: bool = false,
_on_message: ?js.Function = null, _on_message: ?js.Function.Global = null,
_on_message_error: ?js.Function = null, _on_message_error: ?js.Function.Global = null,
_entangled_port: ?*MessagePort = null, _entangled_port: ?*MessagePort = null,
pub fn init(page: *Page) !*MessagePort { pub fn init(page: *Page) !*MessagePort {
@@ -88,28 +88,20 @@ pub fn close(self: *MessagePort) void {
self._entangled_port = null; self._entangled_port = null;
} }
pub fn getOnMessage(self: *const MessagePort) ?js.Function { pub fn getOnMessage(self: *const MessagePort) ?js.Function.Global {
return self._on_message; return self._on_message;
} }
pub fn setOnMessage(self: *MessagePort, cb_: ?js.Function) !void { pub fn setOnMessage(self: *MessagePort, cb: ?js.Function.Global) !void {
if (cb_) |cb| { self._on_message = cb;
self._on_message = try cb.persist();
} else {
self._on_message = null;
}
} }
pub fn getOnMessageError(self: *const MessagePort) ?js.Function { pub fn getOnMessageError(self: *const MessagePort) ?js.Function.Global {
return self._on_message_error; return self._on_message_error;
} }
pub fn setOnMessageError(self: *MessagePort, cb_: ?js.Function) !void { pub fn setOnMessageError(self: *MessagePort, cb: ?js.Function.Global) !void {
if (cb_) |cb| { self._on_message_error = cb;
self._on_message_error = try cb.persist();
} else {
self._on_message_error = null;
}
} }
const PostMessageCallback = struct { const PostMessageCallback = struct {
@@ -138,10 +130,11 @@ const PostMessageCallback = struct {
return null; return null;
}; };
const func = if (self.port._on_message) |*g| g.local() else null;
self.page._event_manager.dispatchWithFunction( self.page._event_manager.dispatchWithFunction(
self.port.asEventTarget(), self.port.asEventTarget(),
event.asEvent(), event.asEvent(),
self.port._on_message, func,
.{ .context = "MessagePort message" }, .{ .context = "MessagePort message" },
) catch |err| { ) catch |err| {
log.err(.dom, "MessagePort.postMessage", .{ .err = err }); log.err(.dom, "MessagePort.postMessage", .{ .err = err });

View File

@@ -32,7 +32,7 @@ pub fn registerTypes() []const type {
const MutationObserver = @This(); const MutationObserver = @This();
_callback: js.Function, _callback: js.Function.Global,
_observing: std.ArrayList(Observing) = .{}, _observing: std.ArrayList(Observing) = .{},
_pending_records: std.ArrayList(*MutationRecord) = .{}, _pending_records: std.ArrayList(*MutationRecord) = .{},
/// Intrusively linked to next element (see Page.zig). /// Intrusively linked to next element (see Page.zig).
@@ -53,9 +53,9 @@ pub const ObserveOptions = struct {
attributeFilter: ?[]const []const u8 = null, attributeFilter: ?[]const []const u8 = null,
}; };
pub fn init(callback: js.Function, page: *Page) !*MutationObserver { pub fn init(callback: js.Function.Global, page: *Page) !*MutationObserver {
return page._factory.create(MutationObserver{ return page._factory.create(MutationObserver{
._callback = try callback.persist(), ._callback = callback,
}); });
} }
@@ -249,7 +249,7 @@ pub fn deliverRecords(self: *MutationObserver, page: *Page) !void {
// This ensures mutations triggered during the callback go into a fresh list // This ensures mutations triggered during the callback go into a fresh list
const records = try self.takeRecords(page); const records = try self.takeRecords(page);
var caught: js.TryCatch.Caught = undefined; var caught: js.TryCatch.Caught = undefined;
self._callback.tryCall(void, .{ records, self }, &caught) catch |err| { self._callback.local().tryCall(void, .{ records, self }, &caught) catch |err| {
log.err(.page, "MutObserver.deliverRecords", .{ .err = err, .caught = caught }); log.err(.page, "MutObserver.deliverRecords", .{ .err = err, .caught = caught });
return err; return err;
}; };

View File

@@ -22,22 +22,22 @@ const Node = @import("Node.zig");
const NodeFilter = @This(); const NodeFilter = @This();
_func: ?js.Function, _func: ?js.Function.Global,
_original_filter: ?FilterOpts, _original_filter: ?FilterOpts,
pub const FilterOpts = union(enum) { pub const FilterOpts = union(enum) {
function: js.Function, function: js.Function.Global,
object: struct { object: struct {
pub const js_as_object = true; pub const js_as_object = true;
acceptNode: js.Function, acceptNode: js.Function.Global,
}, },
}; };
pub fn init(opts_: ?FilterOpts) !NodeFilter { pub fn init(opts_: ?FilterOpts) !NodeFilter {
const opts = opts_ orelse return .{ ._func = null, ._original_filter = null }; const opts = opts_ orelse return .{ ._func = null, ._original_filter = null };
const func = switch (opts) { const func = switch (opts) {
.function => |func| try func.persist(), .function => |func| func,
.object => |obj| try obj.acceptNode.persist(), .object => |obj| obj.acceptNode,
}; };
return .{ return .{
._func = func, ._func = func,
@@ -67,7 +67,7 @@ pub const SHOW_NOTATION: u32 = 0x800;
pub fn acceptNode(self: *const NodeFilter, node: *Node) !i32 { pub fn acceptNode(self: *const NodeFilter, node: *Node) !i32 {
const func = self._func orelse return FILTER_ACCEPT; const func = self._func orelse return FILTER_ACCEPT;
return func.call(i32, .{node}); return func.local().call(i32, .{node});
} }
pub fn shouldShow(node: *const Node, what_to_show: u32) bool { pub fn shouldShow(node: *const Node, what_to_show: u32) bool {

View File

@@ -32,7 +32,7 @@ pub fn registerTypes() []const type {
const PerformanceObserver = @This(); const PerformanceObserver = @This();
/// Emitted when there are events with same interests. /// Emitted when there are events with same interests.
_callback: js.Function, _callback: js.Function.Global,
/// The threshold to deliver `PerformanceEventTiming` entries. /// The threshold to deliver `PerformanceEventTiming` entries.
_duration_threshold: f64, _duration_threshold: f64,
/// Entry types we're looking for are encoded as bit flags. /// Entry types we're looking for are encoded as bit flags.
@@ -44,9 +44,9 @@ _entries: std.ArrayList(*Performance.Entry),
const DefaultDurationThreshold: f64 = 104; const DefaultDurationThreshold: f64 = 104;
/// Creates a new PerformanceObserver object with the given observer callback. /// Creates a new PerformanceObserver object with the given observer callback.
pub fn init(callback: js.Function, page: *Page) !*PerformanceObserver { pub fn init(callback: js.Function.Global, page: *Page) !*PerformanceObserver {
return page._factory.create(PerformanceObserver{ return page._factory.create(PerformanceObserver{
._callback = try callback.persist(), ._callback = callback,
._duration_threshold = DefaultDurationThreshold, ._duration_threshold = DefaultDurationThreshold,
._interests = 0, ._interests = 0,
._entries = .{}, ._entries = .{},
@@ -154,7 +154,7 @@ pub inline fn hasRecords(self: *const PerformanceObserver) bool {
pub fn dispatch(self: *PerformanceObserver, page: *Page) !void { pub fn dispatch(self: *PerformanceObserver, page: *Page) !void {
const records = try self.takeRecords(page); const records = try self.takeRecords(page);
var caught: js.TryCatch.Caught = undefined; var caught: js.TryCatch.Caught = undefined;
self._callback.tryCall(void, .{ EntryList{ ._entries = records }, self }, &caught) catch |err| { self._callback.local().tryCall(void, .{ EntryList{ ._entries = records }, self }, &caught) catch |err| {
log.err(.page, "PerfObserver.dispatch", .{ .err = err, .caught = caught }); log.err(.page, "PerfObserver.dispatch", .{ .err = err, .caught = caught });
return err; return err;
}; };

View File

@@ -54,11 +54,11 @@ _navigator: Navigator = .init,
_screen: *Screen, _screen: *Screen,
_performance: Performance, _performance: Performance,
_storage_bucket: *storage.Bucket, _storage_bucket: *storage.Bucket,
_on_load: ?js.Function = null, _on_load: ?js.Function.Global = null,
_on_pageshow: ?js.Function = null, _on_pageshow: ?js.Function.Global = null,
_on_popstate: ?js.Function = null, _on_popstate: ?js.Function.Global = null,
_on_error: ?js.Function = null, // TODO: invoke on error? _on_error: ?js.Function.Global = null, // TODO: invoke on error?
_on_unhandled_rejection: ?js.Function = null, // TODO: invoke on error _on_unhandled_rejection: ?js.Function.Global = null, // TODO: invoke on error
_location: *Location, _location: *Location,
_timer_id: u30 = 0, _timer_id: u30 = 0,
_timers: std.AutoHashMapUnmanaged(u32, *ScheduleCallback) = .{}, _timers: std.AutoHashMapUnmanaged(u32, *ScheduleCallback) = .{},
@@ -145,51 +145,51 @@ pub fn getCustomElements(self: *Window) *CustomElementRegistry {
return &self._custom_elements; return &self._custom_elements;
} }
pub fn getOnLoad(self: *const Window) ?js.Function { pub fn getOnLoad(self: *const Window) ?js.Function.Global {
return self._on_load; return self._on_load;
} }
pub fn setOnLoad(self: *Window, setter: ?FunctionSetter) !void { pub fn setOnLoad(self: *Window, setter: ?FunctionSetter) void {
self._on_load = try getFunctionFromSetter(setter); self._on_load = getFunctionFromSetter(setter);
} }
pub fn getOnPageShow(self: *const Window) ?js.Function { pub fn getOnPageShow(self: *const Window) ?js.Function.Global {
return self._on_pageshow; return self._on_pageshow;
} }
pub fn setOnPageShow(self: *Window, setter: ?FunctionSetter) !void { pub fn setOnPageShow(self: *Window, setter: ?FunctionSetter) void {
self._on_pageshow = try getFunctionFromSetter(setter); self._on_pageshow = getFunctionFromSetter(setter);
} }
pub fn getOnPopState(self: *const Window) ?js.Function { pub fn getOnPopState(self: *const Window) ?js.Function.Global {
return self._on_popstate; return self._on_popstate;
} }
pub fn setOnPopState(self: *Window, setter: ?FunctionSetter) !void { pub fn setOnPopState(self: *Window, setter: ?FunctionSetter) void {
self._on_popstate = try getFunctionFromSetter(setter); self._on_popstate = getFunctionFromSetter(setter);
} }
pub fn getOnError(self: *const Window) ?js.Function { pub fn getOnError(self: *const Window) ?js.Function.Global {
return self._on_error; return self._on_error;
} }
pub fn setOnError(self: *Window, setter: ?FunctionSetter) !void { pub fn setOnError(self: *Window, setter: ?FunctionSetter) void {
self._on_error = try getFunctionFromSetter(setter); self._on_error = getFunctionFromSetter(setter);
} }
pub fn getOnUnhandledRejection(self: *const Window) ?js.Function { pub fn getOnUnhandledRejection(self: *const Window) ?js.Function.Global {
return self._on_unhandled_rejection; return self._on_unhandled_rejection;
} }
pub fn setOnUnhandledRejection(self: *Window, setter: ?FunctionSetter) !void { pub fn setOnUnhandledRejection(self: *Window, setter: ?FunctionSetter) void {
self._on_unhandled_rejection = try getFunctionFromSetter(setter); self._on_unhandled_rejection = getFunctionFromSetter(setter);
} }
pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, page: *Page) !js.Promise { pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, page: *Page) !js.Promise {
return Fetch.init(input, options, page); return Fetch.init(input, options, page);
} }
pub fn setTimeout(self: *Window, cb: js.Function, delay_ms: ?u32, params: []js.Value, page: *Page) !u32 { pub fn setTimeout(self: *Window, cb: js.Function.Global, delay_ms: ?u32, params: []js.Value, page: *Page) !u32 {
return self.scheduleCallback(cb, delay_ms orelse 0, .{ return self.scheduleCallback(cb, delay_ms orelse 0, .{
.repeat = false, .repeat = false,
.params = params, .params = params,
@@ -198,7 +198,7 @@ pub fn setTimeout(self: *Window, cb: js.Function, delay_ms: ?u32, params: []js.V
}, page); }, page);
} }
pub fn setInterval(self: *Window, cb: js.Function, delay_ms: ?u32, params: []js.Value, page: *Page) !u32 { pub fn setInterval(self: *Window, cb: js.Function.Global, delay_ms: ?u32, params: []js.Value, page: *Page) !u32 {
return self.scheduleCallback(cb, delay_ms orelse 0, .{ return self.scheduleCallback(cb, delay_ms orelse 0, .{
.repeat = true, .repeat = true,
.params = params, .params = params,
@@ -207,7 +207,7 @@ pub fn setInterval(self: *Window, cb: js.Function, delay_ms: ?u32, params: []js.
}, page); }, page);
} }
pub fn setImmediate(self: *Window, cb: js.Function, params: []js.Value, page: *Page) !u32 { pub fn setImmediate(self: *Window, cb: js.Function.Global, params: []js.Value, page: *Page) !u32 {
return self.scheduleCallback(cb, 0, .{ return self.scheduleCallback(cb, 0, .{
.repeat = false, .repeat = false,
.params = params, .params = params,
@@ -216,7 +216,7 @@ pub fn setImmediate(self: *Window, cb: js.Function, params: []js.Value, page: *P
}, page); }, page);
} }
pub fn requestAnimationFrame(self: *Window, cb: js.Function, page: *Page) !u32 { pub fn requestAnimationFrame(self: *Window, cb: js.Function.Global, page: *Page) !u32 {
return self.scheduleCallback(cb, 5, .{ return self.scheduleCallback(cb, 5, .{
.repeat = false, .repeat = false,
.params = &.{}, .params = &.{},
@@ -253,7 +253,7 @@ pub fn cancelAnimationFrame(self: *Window, id: u32) void {
const RequestIdleCallbackOpts = struct { const RequestIdleCallbackOpts = struct {
timeout: ?u32 = null, timeout: ?u32 = null,
}; };
pub fn requestIdleCallback(self: *Window, cb: js.Function, opts_: ?RequestIdleCallbackOpts, page: *Page) !u32 { pub fn requestIdleCallback(self: *Window, cb: js.Function.Global, opts_: ?RequestIdleCallbackOpts, page: *Page) !u32 {
const opts = opts_ orelse RequestIdleCallbackOpts{}; const opts = opts_ orelse RequestIdleCallbackOpts{};
return self.scheduleCallback(cb, opts.timeout orelse 50, .{ return self.scheduleCallback(cb, opts.timeout orelse 50, .{
.mode = .idle, .mode = .idle,
@@ -471,7 +471,7 @@ const ScheduleOpts = struct {
animation_frame: bool = false, animation_frame: bool = false,
mode: ScheduleCallback.Mode = .normal, mode: ScheduleCallback.Mode = .normal,
}; };
fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: ScheduleOpts, page: *Page) !u32 { fn scheduleCallback(self: *Window, cb: js.Function.Global, delay_ms: u32, opts: ScheduleOpts, page: *Page) !u32 {
if (self._timers.count() > 512) { if (self._timers.count() > 512) {
// these are active // these are active
return error.TooManyTimeout; return error.TooManyTimeout;
@@ -497,7 +497,7 @@ fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: Schedul
errdefer _ = self._timers.remove(timer_id); errdefer _ = self._timers.remove(timer_id);
const callback = try page._factory.create(ScheduleCallback{ const callback = try page._factory.create(ScheduleCallback{
.cb = try cb.persist(), .cb = cb,
.page = page, .page = page,
.mode = opts.mode, .mode = opts.mode,
.name = opts.name, .name = opts.name,
@@ -526,7 +526,7 @@ const ScheduleCallback = struct {
// delay, in ms, to repeat. When null, will be removed after the first time // delay, in ms, to repeat. When null, will be removed after the first time
repeat_ms: ?u32, repeat_ms: ?u32,
cb: js.Function, cb: js.Function.Global,
page: *Page, page: *Page,
@@ -558,17 +558,17 @@ const ScheduleCallback = struct {
switch (self.mode) { switch (self.mode) {
.idle => { .idle => {
const IdleDeadline = @import("IdleDeadline.zig"); const IdleDeadline = @import("IdleDeadline.zig");
self.cb.call(void, .{IdleDeadline{}}) catch |err| { self.cb.local().call(void, .{IdleDeadline{}}) catch |err| {
log.warn(.js, "window.idleCallback", .{ .name = self.name, .err = err }); log.warn(.js, "window.idleCallback", .{ .name = self.name, .err = err });
}; };
}, },
.animation_frame => { .animation_frame => {
self.cb.call(void, .{page.window._performance.now()}) catch |err| { self.cb.local().call(void, .{page.window._performance.now()}) catch |err| {
log.warn(.js, "window.RAF", .{ .name = self.name, .err = err }); log.warn(.js, "window.RAF", .{ .name = self.name, .err = err });
}; };
}, },
.normal => { .normal => {
self.cb.call(void, self.params) catch |err| { self.cb.local().call(void, self.params) catch |err| {
log.warn(.js, "window.timer", .{ .name = self.name, .err = err }); log.warn(.js, "window.timer", .{ .name = self.name, .err = err });
}; };
}, },
@@ -615,17 +615,17 @@ const PostMessageCallback = struct {
}; };
const FunctionSetter = union(enum) { const FunctionSetter = union(enum) {
func: js.Function, func: js.Function.Global,
anything: js.Value, anything: js.Value,
}; };
// window.onload = {}; doesn't fail, but it doesn't do anything. // 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 // seems like setting to null is ok (though, at least on Firefix, it preserves
// the original value, which we could do, but why?) // the original value, which we could do, but why?)
fn getFunctionFromSetter(setter_: ?FunctionSetter) !?js.Function { fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global {
const setter = setter_ orelse return null; const setter = setter_ orelse return null;
return switch (setter) { return switch (setter) {
.func => |func| try func.persist(), .func => |func| func, // Already a Global from bridge auto-conversion
.anything => null, .anything => null,
}; };
} }

View File

@@ -50,9 +50,10 @@ pub const Build = struct {
pub fn complete(node: *Node, page: *Page) !void { pub fn complete(node: *Node, page: *Page) !void {
const el = node.as(Element); const el = node.as(Element);
const on_load = el.getAttributeSafe("onload") orelse return; const on_load = el.getAttributeSafe("onload") orelse return;
page.window._on_load = page.js.stringToFunction(on_load) catch |err| blk: { if (page.js.stringToFunction(on_load)) |func| {
page.window._on_load = try func.persist();
} else |err| {
log.err(.js, "body.onload", .{ .err = err, .str = on_load }); log.err(.js, "body.onload", .{ .err = err, .str = on_load });
break :blk null; }
};
} }
}; };

View File

@@ -196,7 +196,7 @@ pub fn checkAndAttachBuiltIn(element: *Element, page: *Page) !void {
defer page._upgrading_element = prev_upgrading; defer page._upgrading_element = prev_upgrading;
var caught: js.TryCatch.Caught = undefined; var caught: js.TryCatch.Caught = undefined;
_ = definition.constructor.newInstance(&caught) catch |err| { _ = definition.constructor.local().newInstance(&caught) catch |err| {
log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err, .caught = caught }); log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err, .caught = caught });
return; return;
}; };

View File

@@ -29,8 +29,8 @@ const Script = @This();
_proto: *HtmlElement, _proto: *HtmlElement,
_src: []const u8 = "", _src: []const u8 = "",
_on_load: ?js.Function = null, _on_load: ?js.Function.Global = null,
_on_error: ?js.Function = null, _on_error: ?js.Function.Global = null,
_executed: bool = false, _executed: bool = false,
pub fn asElement(self: *Script) *Element { pub fn asElement(self: *Script) *Element {
@@ -74,28 +74,20 @@ pub fn setNonce(self: *Script, value: []const u8, page: *Page) !void {
return self.asElement().setAttributeSafe("nonce", value, page); return self.asElement().setAttributeSafe("nonce", value, page);
} }
pub fn getOnLoad(self: *const Script) ?js.Function { pub fn getOnLoad(self: *const Script) ?js.Function.Global {
return self._on_load; return self._on_load;
} }
pub fn setOnLoad(self: *Script, cb_: ?js.Function) !void { pub fn setOnLoad(self: *Script, cb: ?js.Function.Global) void {
if (cb_) |cb| { self._on_load = cb;
self._on_load = try cb.persist();
} else {
self._on_load = null;
}
} }
pub fn getOnError(self: *const Script) ?js.Function { pub fn getOnError(self: *const Script) ?js.Function.Global {
return self._on_error; return self._on_error;
} }
pub fn setOnError(self: *Script, cb_: ?js.Function) !void { pub fn setOnError(self: *Script, cb: ?js.Function.Global) void {
if (cb_) |cb| { self._on_error = cb;
self._on_error = try cb.persist();
} else {
self._on_error = null;
}
} }
pub fn getNoModule(self: *const Script) bool { pub fn getNoModule(self: *const Script) bool {
@@ -136,23 +128,19 @@ pub const Build = struct {
self._src = element.getAttributeSafe("src") orelse ""; self._src = element.getAttributeSafe("src") orelse "";
if (element.getAttributeSafe("onload")) |on_load| { if (element.getAttributeSafe("onload")) |on_load| {
self._on_load = blk: { if (page.js.stringToFunction(on_load)) |func| {
const func = page.js.stringToFunction(on_load) catch |err| { self._on_load = try func.persist();
} else |err| {
log.err(.js, "script.onload", .{ .err = err, .str = on_load }); log.err(.js, "script.onload", .{ .err = err, .str = on_load });
break :blk null; }
};
break :blk try func.persist();
};
} }
if (element.getAttributeSafe("onerror")) |on_error| { if (element.getAttributeSafe("onerror")) |on_error| {
self._on_error = blk: { if (page.js.stringToFunction(on_error)) |func| {
const func = page.js.stringToFunction(on_error) catch |err| { self._on_error = try func.persist();
} else |err| {
log.err(.js, "script.onerror", .{ .err = err, .str = on_error }); log.err(.js, "script.onerror", .{ .err = err, .str = on_error });
break :blk null; }
};
break :blk try func.persist();
};
} }
} }
}; };

View File

@@ -30,8 +30,8 @@ _id: []const u8 = "",
_start_time: f64 = 0, _start_time: f64 = 0,
_end_time: f64 = 0, _end_time: f64 = 0,
_pause_on_exit: bool = false, _pause_on_exit: bool = false,
_on_enter: ?js.Function = null, _on_enter: ?js.Function.Global = null,
_on_exit: ?js.Function = null, _on_exit: ?js.Function.Global = null,
pub const Type = union(enum) { pub const Type = union(enum) {
vtt: *@import("VTTCue.zig"), vtt: *@import("VTTCue.zig"),
@@ -73,28 +73,20 @@ pub fn setPauseOnExit(self: *TextTrackCue, value: bool) void {
self._pause_on_exit = value; self._pause_on_exit = value;
} }
pub fn getOnEnter(self: *const TextTrackCue) ?js.Function { pub fn getOnEnter(self: *const TextTrackCue) ?js.Function.Global {
return self._on_enter; return self._on_enter;
} }
pub fn setOnEnter(self: *TextTrackCue, cb_: ?js.Function) !void { pub fn setOnEnter(self: *TextTrackCue, cb: ?js.Function.Global) !void {
if (cb_) |cb| { self._on_enter = cb;
self._on_enter = try cb.persistWithThis(self);
} else {
self._on_enter = null;
}
} }
pub fn getOnExit(self: *const TextTrackCue) ?js.Function { pub fn getOnExit(self: *const TextTrackCue) ?js.Function.Global {
return self._on_exit; return self._on_exit;
} }
pub fn setOnExit(self: *TextTrackCue, cb_: ?js.Function) !void { pub fn setOnExit(self: *TextTrackCue, cb: ?js.Function.Global) !void {
if (cb_) |cb| { self._on_exit = cb;
self._on_exit = try cb.persistWithThis(self);
} else {
self._on_exit = null;
}
} }
pub const JsApi = struct { pub const JsApi = struct {

View File

@@ -26,7 +26,7 @@ const NavigationCurrentEntryChangeEvent = @import("../event/NavigationCurrentEnt
pub const NavigationEventTarget = @This(); pub const NavigationEventTarget = @This();
_proto: *EventTarget, _proto: *EventTarget,
_on_currententrychange: ?js.Function = null, _on_currententrychange: ?js.Function.Global = null,
pub fn asEventTarget(self: *NavigationEventTarget) *EventTarget { pub fn asEventTarget(self: *NavigationEventTarget) *EventTarget {
return self._proto; return self._proto;
@@ -43,15 +43,16 @@ pub fn dispatch(self: *NavigationEventTarget, event_type: DispatchType, page: *P
}; };
}; };
const func = if (@field(self, field)) |*g| g.local() else null;
return page._event_manager.dispatchWithFunction( return page._event_manager.dispatchWithFunction(
self.asEventTarget(), self.asEventTarget(),
event, event,
@field(self, field), func,
.{ .context = "Navigation" }, .{ .context = "Navigation" },
); );
} }
pub fn getOnCurrentEntryChange(self: *NavigationEventTarget) ?js.Function { pub fn getOnCurrentEntryChange(self: *NavigationEventTarget) ?js.Function.Global {
return self._on_currententrychange; return self._on_currententrychange;
} }

View File

@@ -55,7 +55,7 @@ _response_headers: std.ArrayList([]const u8) = .empty,
_response_type: ResponseType = .text, _response_type: ResponseType = .text,
_ready_state: ReadyState = .unsent, _ready_state: ReadyState = .unsent,
_on_ready_state_change: ?js.Function = null, _on_ready_state_change: ?js.Function.Global = null,
const ReadyState = enum(u8) { const ReadyState = enum(u8) {
unsent = 0, unsent = 0,
@@ -98,7 +98,7 @@ fn asEventTarget(self: *XMLHttpRequest) *EventTarget {
return self._proto._proto; return self._proto._proto;
} }
pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function { pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function.Global {
return self._on_ready_state_change; return self._on_ready_state_change;
} }
@@ -416,10 +416,11 @@ fn stateChanged(self: *XMLHttpRequest, state: ReadyState, page: *Page) !void {
self._ready_state = state; self._ready_state = state;
const event = try Event.initTrusted("readystatechange", .{}, page); const event = try Event.initTrusted("readystatechange", .{}, page);
const func = if (self._on_ready_state_change) |*g| g.local() else null;
try page._event_manager.dispatchWithFunction( try page._event_manager.dispatchWithFunction(
self.asEventTarget(), self.asEventTarget(),
event, event,
self._on_ready_state_change, func,
.{ .context = "XHR state change" }, .{ .context = "XHR state change" },
); );
} }

View File

@@ -26,13 +26,13 @@ const XMLHttpRequestEventTarget = @This();
_type: Type, _type: Type,
_proto: *EventTarget, _proto: *EventTarget,
_on_abort: ?js.Function = null, _on_abort: ?js.Function.Global = null,
_on_error: ?js.Function = null, _on_error: ?js.Function.Global = null,
_on_load: ?js.Function = null, _on_load: ?js.Function.Global = null,
_on_load_end: ?js.Function = null, _on_load_end: ?js.Function.Global = null,
_on_load_start: ?js.Function = null, _on_load_start: ?js.Function.Global = null,
_on_progress: ?js.Function = null, _on_progress: ?js.Function.Global = null,
_on_timeout: ?js.Function = null, _on_timeout: ?js.Function.Global = null,
pub const Type = union(enum) { pub const Type = union(enum) {
request: *@import("XMLHttpRequest.zig"), request: *@import("XMLHttpRequest.zig"),
@@ -63,15 +63,16 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
page, page,
); );
const func = if (@field(self, field)) |*g| g.local() else null;
return page._event_manager.dispatchWithFunction( return page._event_manager.dispatchWithFunction(
self.asEventTarget(), self.asEventTarget(),
event.asEvent(), event.asEvent(),
@field(self, field), func,
.{ .context = "XHR " ++ typ }, .{ .context = "XHR " ++ typ },
); );
} }
pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_abort; return self._on_abort;
} }
@@ -83,7 +84,7 @@ pub fn setOnAbort(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
} }
} }
pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_error; return self._on_error;
} }
@@ -95,7 +96,7 @@ pub fn setOnError(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
} }
} }
pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_load; return self._on_load;
} }
@@ -107,7 +108,7 @@ pub fn setOnLoad(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
} }
} }
pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_load_end; return self._on_load_end;
} }
@@ -119,7 +120,7 @@ pub fn setOnLoadEnd(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
} }
} }
pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_load_start; return self._on_load_start;
} }
@@ -131,7 +132,7 @@ pub fn setOnLoadStart(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void
} }
} }
pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_progress; return self._on_progress;
} }
@@ -143,7 +144,7 @@ pub fn setOnProgress(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void
} }
} }
pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
return self._on_timeout; return self._on_timeout;
} }

View File

@@ -43,15 +43,15 @@ _state: State,
_reader: ?*ReadableStreamDefaultReader, _reader: ?*ReadableStreamDefaultReader,
_controller: *ReadableStreamDefaultController, _controller: *ReadableStreamDefaultController,
_stored_error: ?[]const u8, _stored_error: ?[]const u8,
_pull_fn: ?js.Function = null, _pull_fn: ?js.Function.Global = null,
_pulling: bool = false, _pulling: bool = false,
_pull_again: bool = false, _pull_again: bool = false,
_cancel: ?Cancel = null, _cancel: ?Cancel = null,
const UnderlyingSource = struct { const UnderlyingSource = struct {
start: ?js.Function = null, start: ?js.Function = null,
pull: ?js.Function = null, pull: ?js.Function.Global = null,
cancel: ?js.Function = null, cancel: ?js.Function.Global = null,
type: ?[]const u8 = null, type: ?[]const u8 = null,
}; };
@@ -85,7 +85,7 @@ pub fn init(src_: ?UnderlyingSource, strategy_: ?QueueingStrategy, page: *Page)
} }
if (src.pull) |pull| { if (src.pull) |pull| {
self._pull_fn = try pull.persist(); self._pull_fn = pull;
try self.callPullIfNeeded(); try self.callPullIfNeeded();
} }
} }
@@ -137,12 +137,12 @@ pub fn callPullIfNeeded(self: *ReadableStream) !void {
self._pulling = true; self._pulling = true;
const pull_fn = self._pull_fn orelse return; const pull_fn = &(self._pull_fn orelse return);
// Call the pull function // Call the pull function
// Note: In a complete implementation, we'd handle the promise returned by pull // Note: In a complete implementation, we'd handle the promise returned by pull
// and set _pulling = false when it resolves // and set _pulling = false when it resolves
try pull_fn.call(void, .{self._controller}); try pull_fn.local().call(void, .{self._controller});
self._pulling = false; self._pulling = false;
@@ -186,11 +186,11 @@ pub fn cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !js.Promi
} }
// Execute the cancel callback if provided // Execute the cancel callback if provided
if (c.callback) |cb| { if (c.callback) |*cb| {
if (reason) |r| { if (reason) |r| {
try cb.call(void, .{r}); try cb.local().call(void, .{r});
} else { } else {
try cb.call(void, .{}); try cb.local().call(void, .{});
} }
} }
@@ -211,7 +211,7 @@ pub fn cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !js.Promi
} }
const Cancel = struct { const Cancel = struct {
callback: ?js.Function = null, callback: ?js.Function.Global = null,
reason: ?[]const u8 = null, reason: ?[]const u8 = null,
resolver: ?js.PromiseResolver = null, resolver: ?js.PromiseResolver = null,
}; };