mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1516 from lightpanda-io/unhandle_rejection_callback
Adds PromiseRejectionCallback
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,
|
||||
};
|
||||
}
|
||||
@@ -879,6 +879,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