mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Adds PromiseRectionCallback
Fires the window.onunhandledrejection. This API is a bit different than everything else, because it's entered from the Isolate/Env. So there's a bit more js -> webapi awareness baked into Env now to handle it. Also touched up existing Events that have .Global data and changed it to .Temp and cleaned it up in their deinit.
This commit is contained in:
@@ -105,6 +105,7 @@ global_promise_resolvers: std.ArrayList(v8.Global) = .empty,
|
||||
// Temp variants stored in HashMaps for O(1) early cleanup.
|
||||
// Key is global.data_ptr.
|
||||
global_values_temp: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
||||
global_promises_temp: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
||||
global_functions_temp: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
||||
|
||||
// Our module cache: normalized module specifier => module.
|
||||
@@ -233,6 +234,13 @@ pub fn deinit(self: *Context) void {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.global_promises_temp.valueIterator();
|
||||
while (it.next()) |global| {
|
||||
v8.v8__Global__Reset(global);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.global_functions_temp.valueIterator();
|
||||
while (it.next()) |global| {
|
||||
@@ -309,6 +317,7 @@ pub fn release(self: *Context, item: anytype) void {
|
||||
|
||||
var map = switch (@TypeOf(item)) {
|
||||
js.Value.Temp => &self.global_values_temp,
|
||||
js.Promise.Temp => &self.global_promises_temp,
|
||||
js.Function.Temp => &self.global_functions_temp,
|
||||
else => |T| @compileError("Context.release cannot be called with a " ++ @typeName(T)),
|
||||
};
|
||||
|
||||
@@ -383,17 +383,13 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v
|
||||
.call_arena = ctx.call_arena,
|
||||
};
|
||||
|
||||
const value =
|
||||
if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value|
|
||||
js.Value.toStringSlice(.{ .local = &local, .handle = v8_value }) catch |err| @errorName(err)
|
||||
else
|
||||
"no value";
|
||||
|
||||
log.debug(.js, "unhandled rejection", .{
|
||||
.value = value,
|
||||
.stack = local.stackTrace() catch |err| @errorName(err) orelse "???",
|
||||
.note = "This should be updated to call window.unhandledrejection",
|
||||
});
|
||||
const page = ctx.page;
|
||||
page.window.unhandledPromiseRejection(.{
|
||||
.local = &local,
|
||||
.handle = &message_handle,
|
||||
}, page) catch |err| {
|
||||
log.warn(.browser, "unhandled rejection handler", .{ .err = err });
|
||||
};
|
||||
}
|
||||
|
||||
fn fatalCallback(c_location: [*c]const u8, c_message: [*c]const u8) callconv(.c) void {
|
||||
|
||||
@@ -323,6 +323,7 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
|
||||
js.Value.Temp,
|
||||
js.Object.Global,
|
||||
js.Promise.Global,
|
||||
js.Promise.Temp,
|
||||
js.PromiseResolver.Global,
|
||||
js.Module.Global => return .{ .local = self, .handle = @ptrCast(value.local(self).handle) },
|
||||
else => {}
|
||||
@@ -619,15 +620,19 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
||||
return try obj.persist();
|
||||
},
|
||||
|
||||
js.Promise.Global => {
|
||||
js.Promise.Global, js.Promise.Temp => {
|
||||
if (!js_val.isPromise()) {
|
||||
return null;
|
||||
}
|
||||
const promise = js.Promise{
|
||||
.ctx = self,
|
||||
const js_promise = js.Promise{
|
||||
.local = self,
|
||||
.handle = @ptrCast(js_val.handle),
|
||||
};
|
||||
return try promise.persist();
|
||||
return switch (T) {
|
||||
js.Promise.Temp => try js_promise.temp(),
|
||||
js.Promise.Global => try js_promise.persist(),
|
||||
else => unreachable,
|
||||
};
|
||||
},
|
||||
string.String => {
|
||||
const js_str = js_val.isString() orelse return null;
|
||||
|
||||
@@ -47,25 +47,49 @@ pub fn thenAndCatch(self: Promise, on_fulfilled: js.Function, on_rejected: js.Fu
|
||||
}
|
||||
return error.PromiseChainFailed;
|
||||
}
|
||||
|
||||
pub fn persist(self: Promise) !Global {
|
||||
return self._persist(true);
|
||||
}
|
||||
|
||||
pub fn temp(self: Promise) !Temp {
|
||||
return self._persist(false);
|
||||
}
|
||||
|
||||
fn _persist(self: *const Promise, comptime is_global: bool) !(if (is_global) Global else Temp) {
|
||||
var ctx = self.local.ctx;
|
||||
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
try ctx.global_promises.append(ctx.arena, global);
|
||||
if (comptime is_global) {
|
||||
try ctx.global_promises.append(ctx.arena, global);
|
||||
} else {
|
||||
try ctx.global_promises_temp.put(ctx.arena, global.data_ptr, global);
|
||||
}
|
||||
return .{ .handle = global };
|
||||
}
|
||||
|
||||
pub const Global = struct {
|
||||
handle: v8.Global,
|
||||
pub const Temp = G(0);
|
||||
pub const Global = G(1);
|
||||
|
||||
pub fn deinit(self: *Global) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
fn G(comptime discriminator: u8) type {
|
||||
return struct {
|
||||
handle: v8.Global,
|
||||
|
||||
pub fn local(self: *const Global, l: *const js.Local) Promise {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
};
|
||||
// makes the types different (G(0) != G(1)), without taking up space
|
||||
comptime _: u8 = discriminator,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
|
||||
pub fn local(self: *const Self, l: *const js.Local) Promise {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
41
src/browser/js/PromiseRejection.zig
Normal file
41
src/browser/js/PromiseRejection.zig
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2023-2026 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 js = @import("js.zig");
|
||||
const v8 = js.v8;
|
||||
|
||||
const PromiseRejection = @This();
|
||||
|
||||
local: *const js.Local,
|
||||
handle: *const v8.PromiseRejectMessage,
|
||||
|
||||
pub fn promise(self: PromiseRejection) js.Promise {
|
||||
return .{
|
||||
.local = self.local,
|
||||
.handle = v8.v8__PromiseRejectMessage__GetPromise(self.handle).?,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reason(self: PromiseRejection) ?js.Value {
|
||||
const value_handle = v8.v8__PromiseRejectMessage__GetValue(self.handle) orelse return null;
|
||||
|
||||
return .{
|
||||
.local = self.local,
|
||||
.handle = value_handle,
|
||||
};
|
||||
}
|
||||
@@ -882,6 +882,7 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/event/MouseEvent.zig"),
|
||||
@import("../webapi/event/PointerEvent.zig"),
|
||||
@import("../webapi/event/KeyboardEvent.zig"),
|
||||
@import("../webapi/event/PromiseRejectionEvent.zig"),
|
||||
@import("../webapi/MessageChannel.zig"),
|
||||
@import("../webapi/MessagePort.zig"),
|
||||
@import("../webapi/media/MediaError.zig"),
|
||||
|
||||
@@ -44,6 +44,7 @@ pub const BigInt = @import("BigInt.zig");
|
||||
pub const Number = @import("Number.zig");
|
||||
pub const Integer = @import("Integer.zig");
|
||||
pub const PromiseResolver = @import("PromiseResolver.zig");
|
||||
pub const PromiseRejection = @import("PromiseRejection.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
|
||||
22
src/browser/tests/event/promise_rejection.html
Normal file
22
src/browser/tests/event/promise_rejection.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=project_rejection>
|
||||
{
|
||||
let e1 = new PromiseRejectionEvent("rejectionhandled");
|
||||
testing.expectEqual(true, e1 instanceof PromiseRejectionEvent);
|
||||
testing.expectEqual(true, e1 instanceof Event);
|
||||
|
||||
testing.expectEqual("rejectionhandled", e1.type);
|
||||
testing.expectEqual(null, e1.reason);
|
||||
testing.expectEqual(null, e1.promise);
|
||||
|
||||
let e2 = new PromiseRejectionEvent("rejectionhandled", {reason: ['tea']});
|
||||
testing.expectEqual(true, e2 instanceof PromiseRejectionEvent);
|
||||
testing.expectEqual(true, e2 instanceof Event);
|
||||
|
||||
testing.expectEqual("rejectionhandled", e2.type);
|
||||
testing.expectEqual(['tea'], e2.reason);
|
||||
testing.expectEqual(null, e2.promise);
|
||||
}
|
||||
</script>
|
||||
@@ -114,3 +114,30 @@
|
||||
testing.expectEqual(24, screen.pixelDepth);
|
||||
testing.expectEqual(screen, window.screen);
|
||||
</script>
|
||||
|
||||
<script id=unhandled_rejection>
|
||||
{
|
||||
let unhandledCalled = 0;
|
||||
window.onunhandledrejection = function(e) {
|
||||
testing.expectEqual(true, e instanceof PromiseRejectionEvent);
|
||||
testing.expectEqual({x: 'Fail'}, e.reason);
|
||||
testing.expectEqual('unhandledrejection', e.type);
|
||||
testing.expectEqual(window, e.target);
|
||||
testing.expectEqual(window, e.srcElement);
|
||||
testing.expectEqual(window, e.currentTarget);
|
||||
unhandledCalled += 1;
|
||||
}
|
||||
|
||||
window.addEventListener('unhandledrejection', function(e) {
|
||||
testing.expectEqual(true, e instanceof PromiseRejectionEvent);
|
||||
testing.expectEqual({x: 'Fail'}, e.reason);
|
||||
testing.expectEqual('unhandledrejection', e.type);
|
||||
testing.expectEqual(window, e.target);
|
||||
testing.expectEqual(window, e.srcElement);
|
||||
testing.expectEqual(window, e.currentTarget);
|
||||
unhandledCalled += 1;
|
||||
});
|
||||
Promise.reject({x: 'Fail'});
|
||||
testing.eventually(() => testing.expectEqual(2, unhandledCalled));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,6 +71,7 @@ pub const Type = union(enum) {
|
||||
page_transition_event: *@import("event/PageTransitionEvent.zig"),
|
||||
pop_state_event: *@import("event/PopStateEvent.zig"),
|
||||
ui_event: *@import("event/UIEvent.zig"),
|
||||
promise_rejection_event: *@import("event/PromiseRejectionEvent.zig"),
|
||||
};
|
||||
|
||||
pub const Options = struct {
|
||||
@@ -150,6 +151,7 @@ pub fn is(self: *Event, comptime T: type) ?*T {
|
||||
.navigation_current_entry_change_event => |e| return if (T == @import("event/NavigationCurrentEntryChangeEvent.zig")) e else null,
|
||||
.page_transition_event => |e| return if (T == @import("event/PageTransitionEvent.zig")) e else null,
|
||||
.pop_state_event => |e| return if (T == @import("event/PopStateEvent.zig")) e else null,
|
||||
.promise_rejection_event => |e| return if (T == @import("event/PromiseRejectionEvent.zig")) e else null,
|
||||
.ui_event => |e| {
|
||||
if (T == @import("event/UIEvent.zig")) {
|
||||
return e;
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn entangle(port1: *MessagePort, port2: *MessagePort) void {
|
||||
port2._entangled_port = port1;
|
||||
}
|
||||
|
||||
pub fn postMessage(self: *MessagePort, message: js.Value.Global, page: *Page) !void {
|
||||
pub fn postMessage(self: *MessagePort, message: js.Value.Temp, page: *Page) !void {
|
||||
if (self._closed) {
|
||||
return;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ pub fn setOnMessageError(self: *MessagePort, cb: ?js.Function.Global) !void {
|
||||
|
||||
const PostMessageCallback = struct {
|
||||
port: *MessagePort,
|
||||
message: js.Value.Global,
|
||||
message: js.Value.Temp,
|
||||
page: *Page,
|
||||
|
||||
fn deinit(self: *PostMessageCallback) void {
|
||||
|
||||
@@ -44,6 +44,8 @@ const CSSStyleProperties = @import("css/CSSStyleProperties.zig");
|
||||
const CustomElementRegistry = @import("CustomElementRegistry.zig");
|
||||
const Selection = @import("Selection.zig");
|
||||
|
||||
const IS_DEBUG = builtin.mode == .Debug;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Window = @This();
|
||||
@@ -278,7 +280,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void {
|
||||
|
||||
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
||||
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
||||
.@"error" = try err.persist(),
|
||||
.@"error" = try err.temp(),
|
||||
.message = err.toStringSlice() catch "Unknown error",
|
||||
.bubbles = false,
|
||||
.cancelable = true,
|
||||
@@ -342,7 +344,7 @@ pub fn getComputedStyle(_: *const Window, element: *Element, pseudo_element: ?[]
|
||||
return CSSStyleProperties.init(element, true, page);
|
||||
}
|
||||
|
||||
pub fn postMessage(self: *Window, message: js.Value.Global, target_origin: ?[]const u8, page: *Page) !void {
|
||||
pub fn postMessage(self: *Window, message: js.Value.Temp, target_origin: ?[]const u8, page: *Page) !void {
|
||||
// For now, we ignore targetOrigin checking and just dispatch the message
|
||||
// In a full implementation, we would validate the origin
|
||||
_ = target_origin;
|
||||
@@ -487,6 +489,28 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn unhandledPromiseRejection(self: *Window, rejection: js.PromiseRejection, page: *Page) !void {
|
||||
if (comptime IS_DEBUG) {
|
||||
log.debug(.js, "unhandled rejection", .{
|
||||
.value = rejection.reason(),
|
||||
.stack = rejection.local.stackTrace() catch |err| @errorName(err) orelse "???",
|
||||
});
|
||||
}
|
||||
|
||||
var event = (try @import("event/PromiseRejectionEvent.zig").init("unhandledrejection", .{
|
||||
.reason = if (rejection.reason()) |r| try r.temp() else null,
|
||||
.promise = try rejection.promise().temp(),
|
||||
}, page)).asEvent();
|
||||
defer if (!event._v8_handoff) event.deinit(false);
|
||||
|
||||
try page._event_manager.dispatchWithFunction(
|
||||
self.asEventTarget(),
|
||||
event,
|
||||
rejection.local.toLocal(self._on_unhandled_rejection),
|
||||
.{ .inject_target = true, .context = "window.unhandledrejection" },
|
||||
);
|
||||
}
|
||||
|
||||
const ScheduleOpts = struct {
|
||||
repeat: bool,
|
||||
params: []js.Value.Temp,
|
||||
@@ -626,7 +650,7 @@ const PostMessageCallback = struct {
|
||||
page: *Page,
|
||||
arena: Allocator,
|
||||
origin: []const u8,
|
||||
message: js.Value.Global,
|
||||
message: js.Value.Temp,
|
||||
|
||||
fn deinit(self: *PostMessageCallback) void {
|
||||
self.page.releaseArena(self.arena);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// 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 String = @import("../../..//string.zig").String;
|
||||
const String = @import("../../../string.zig").String;
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const String = @import("../../..//string.zig").String;
|
||||
const String = @import("../../../string.zig").String;
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
@@ -27,11 +27,11 @@ const Allocator = std.mem.Allocator;
|
||||
const CustomEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_detail: ?js.Value.Global = null,
|
||||
_detail: ?js.Value.Temp = null,
|
||||
_arena: Allocator,
|
||||
|
||||
const CustomEventOptions = struct {
|
||||
detail: ?js.Value.Global = null,
|
||||
detail: ?js.Value.Temp = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
||||
@@ -61,7 +61,7 @@ pub fn initCustomEvent(
|
||||
event_string: []const u8,
|
||||
bubbles: ?bool,
|
||||
cancelable: ?bool,
|
||||
detail_: ?js.Value.Global,
|
||||
detail_: ?js.Value.Temp,
|
||||
) !void {
|
||||
// This function can only be called after the constructor has called.
|
||||
// So we assume proto is initialized already by constructor.
|
||||
@@ -73,14 +73,18 @@ pub fn initCustomEvent(
|
||||
}
|
||||
|
||||
pub fn deinit(self: *CustomEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
const proto = self._proto;
|
||||
if (self._detail) |d| {
|
||||
proto._page.js.release(d);
|
||||
}
|
||||
proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *CustomEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getDetail(self: *const CustomEvent) ?js.Value.Global {
|
||||
pub fn getDetail(self: *const CustomEvent) ?js.Value.Temp {
|
||||
return self._detail;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ _message: []const u8 = "",
|
||||
_filename: []const u8 = "",
|
||||
_line_number: u32 = 0,
|
||||
_column_number: u32 = 0,
|
||||
_error: ?js.Value.Global = null,
|
||||
_error: ?js.Value.Temp = null,
|
||||
_arena: Allocator,
|
||||
|
||||
pub const ErrorEventOptions = struct {
|
||||
@@ -40,7 +40,7 @@ pub const ErrorEventOptions = struct {
|
||||
filename: ?[]const u8 = null,
|
||||
lineno: u32 = 0,
|
||||
colno: u32 = 0,
|
||||
@"error": ?js.Value.Global = null,
|
||||
@"error": ?js.Value.Temp = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||
@@ -80,7 +80,11 @@ fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ErrorEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
const proto = self._proto;
|
||||
if (self._error) |e| {
|
||||
proto._page.js.release(e);
|
||||
}
|
||||
proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *ErrorEvent) *Event {
|
||||
@@ -103,7 +107,7 @@ pub fn getColumnNumber(self: *const ErrorEvent) u32 {
|
||||
return self._column_number;
|
||||
}
|
||||
|
||||
pub fn getError(self: *const ErrorEvent) ?js.Value.Global {
|
||||
pub fn getError(self: *const ErrorEvent) ?js.Value.Temp {
|
||||
return self._error;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ const Allocator = std.mem.Allocator;
|
||||
const MessageEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_data: ?js.Value.Global = null,
|
||||
_data: ?js.Value.Temp = null,
|
||||
_origin: []const u8 = "",
|
||||
_source: ?*Window = null,
|
||||
|
||||
const MessageEventOptions = struct {
|
||||
data: ?js.Value.Global = null,
|
||||
data: ?js.Value.Temp = null,
|
||||
origin: ?[]const u8 = null,
|
||||
source: ?*Window = null,
|
||||
};
|
||||
@@ -73,14 +73,18 @@ fn initWithTrusted(arena: Allocator, typ: String, opts_: ?Options, trusted: bool
|
||||
}
|
||||
|
||||
pub fn deinit(self: *MessageEvent, shutdown: bool) void {
|
||||
self._proto.deinit(shutdown);
|
||||
const proto = self._proto;
|
||||
if (self._data) |d| {
|
||||
proto._page.js.release(d);
|
||||
}
|
||||
proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *MessageEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getData(self: *const MessageEvent) ?js.Value.Global {
|
||||
pub fn getData(self: *const MessageEvent) ?js.Value.Temp {
|
||||
return self._data;
|
||||
}
|
||||
|
||||
|
||||
102
src/browser/webapi/event/PromiseRejectionEvent.zig
Normal file
102
src/browser/webapi/event/PromiseRejectionEvent.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2023-2026 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 String = @import("../../../string.zig").String;
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const Event = @import("../Event.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const PromiseRejectionEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_reason: ?js.Value.Temp = null,
|
||||
_promise: ?js.Promise.Temp = null,
|
||||
|
||||
const PromiseRejectionEventOptions = struct {
|
||||
reason: ?js.Value.Temp = null,
|
||||
promise: ?js.Promise.Temp = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(PromiseRejectionEvent, PromiseRejectionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*PromiseRejectionEvent {
|
||||
const arena = try page.getArena(.{ .debug = "PromiseRejectionEvent" });
|
||||
errdefer page.releaseArena(arena);
|
||||
const type_string = try String.init(arena, typ, .{});
|
||||
|
||||
const opts = opts_ orelse Options{};
|
||||
const event = try page._factory.event(
|
||||
arena,
|
||||
type_string,
|
||||
PromiseRejectionEvent{
|
||||
._proto = undefined,
|
||||
._reason = opts.reason,
|
||||
._promise = opts.promise,
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PromiseRejectionEvent, shutdown: bool) void {
|
||||
const proto = self._proto;
|
||||
const js_ctx = proto._page.js;
|
||||
if (self._reason) |r| {
|
||||
js_ctx.release(r);
|
||||
}
|
||||
if (self._promise) |p| {
|
||||
js_ctx.release(p);
|
||||
}
|
||||
proto.deinit(shutdown);
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *PromiseRejectionEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getReason(self: *const PromiseRejectionEvent) ?js.Value.Temp {
|
||||
return self._reason;
|
||||
}
|
||||
|
||||
pub fn getPromise(self: *const PromiseRejectionEvent) ?js.Promise.Temp {
|
||||
return self._promise;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(PromiseRejectionEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "PromiseRejectionEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const weak = true;
|
||||
pub const finalizer = bridge.finalizer(PromiseRejectionEvent.deinit);
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(PromiseRejectionEvent.init, .{});
|
||||
pub const reason = bridge.accessor(PromiseRejectionEvent.getReason, null, .{});
|
||||
pub const promise = bridge.accessor(PromiseRejectionEvent.getPromise, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: PromiseRejectionEvent" {
|
||||
try testing.htmlRunner("event/promise_rejection.html", .{});
|
||||
}
|
||||
@@ -103,32 +103,33 @@ pub fn deinit(self: *XMLHttpRequest, shutdown: bool) void {
|
||||
}
|
||||
|
||||
const page = self._page;
|
||||
const js_ctx = page.js;
|
||||
if (self._on_ready_state_change) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
|
||||
{
|
||||
const proto = self._proto;
|
||||
if (proto._on_abort) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_error) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_load) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_load_end) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_load_start) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_progress) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
if (proto._on_timeout) |func| {
|
||||
page.js.release(func);
|
||||
js_ctx.release(func);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user