mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-23 05:04:42 +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.
|
// Temp variants stored in HashMaps for O(1) early cleanup.
|
||||||
// Key is global.data_ptr.
|
// Key is global.data_ptr.
|
||||||
global_values_temp: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
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,
|
global_functions_temp: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
||||||
|
|
||||||
// Our module cache: normalized module specifier => module.
|
// 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();
|
var it = self.global_functions_temp.valueIterator();
|
||||||
while (it.next()) |global| {
|
while (it.next()) |global| {
|
||||||
@@ -309,6 +317,7 @@ pub fn release(self: *Context, item: anytype) void {
|
|||||||
|
|
||||||
var map = switch (@TypeOf(item)) {
|
var map = switch (@TypeOf(item)) {
|
||||||
js.Value.Temp => &self.global_values_temp,
|
js.Value.Temp => &self.global_values_temp,
|
||||||
|
js.Promise.Temp => &self.global_promises_temp,
|
||||||
js.Function.Temp => &self.global_functions_temp,
|
js.Function.Temp => &self.global_functions_temp,
|
||||||
else => |T| @compileError("Context.release cannot be called with a " ++ @typeName(T)),
|
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,
|
.call_arena = ctx.call_arena,
|
||||||
};
|
};
|
||||||
|
|
||||||
const value =
|
const page = ctx.page;
|
||||||
if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value|
|
page.window.unhandledPromiseRejection(.{
|
||||||
js.Value.toStringSlice(.{ .local = &local, .handle = v8_value }) catch |err| @errorName(err)
|
.local = &local,
|
||||||
else
|
.handle = &message_handle,
|
||||||
"no value";
|
}, page) catch |err| {
|
||||||
|
log.warn(.browser, "unhandled rejection handler", .{ .err = err });
|
||||||
log.debug(.js, "unhandled rejection", .{
|
};
|
||||||
.value = value,
|
|
||||||
.stack = local.stackTrace() catch |err| @errorName(err) orelse "???",
|
|
||||||
.note = "This should be updated to call window.unhandledrejection",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fatalCallback(c_location: [*c]const u8, c_message: [*c]const u8) callconv(.c) void {
|
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.Value.Temp,
|
||||||
js.Object.Global,
|
js.Object.Global,
|
||||||
js.Promise.Global,
|
js.Promise.Global,
|
||||||
|
js.Promise.Temp,
|
||||||
js.PromiseResolver.Global,
|
js.PromiseResolver.Global,
|
||||||
js.Module.Global => return .{ .local = self, .handle = @ptrCast(value.local(self).handle) },
|
js.Module.Global => return .{ .local = self, .handle = @ptrCast(value.local(self).handle) },
|
||||||
else => {}
|
else => {}
|
||||||
@@ -619,15 +620,19 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
|||||||
return try obj.persist();
|
return try obj.persist();
|
||||||
},
|
},
|
||||||
|
|
||||||
js.Promise.Global => {
|
js.Promise.Global, js.Promise.Temp => {
|
||||||
if (!js_val.isPromise()) {
|
if (!js_val.isPromise()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const promise = js.Promise{
|
const js_promise = js.Promise{
|
||||||
.ctx = self,
|
.local = self,
|
||||||
.handle = @ptrCast(js_val.handle),
|
.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 => {
|
string.String => {
|
||||||
const js_str = js_val.isString() orelse return null;
|
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;
|
return error.PromiseChainFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn persist(self: Promise) !Global {
|
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 ctx = self.local.ctx;
|
||||||
|
|
||||||
var global: v8.Global = undefined;
|
var global: v8.Global = undefined;
|
||||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||||
|
if (comptime is_global) {
|
||||||
try ctx.global_promises.append(ctx.arena, 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 };
|
return .{ .handle = global };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Global = struct {
|
pub const Temp = G(0);
|
||||||
|
pub const Global = G(1);
|
||||||
|
|
||||||
|
fn G(comptime discriminator: u8) type {
|
||||||
|
return struct {
|
||||||
handle: v8.Global,
|
handle: v8.Global,
|
||||||
|
|
||||||
pub fn deinit(self: *Global) void {
|
// 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);
|
v8.v8__Global__Reset(&self.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local(self: *const Global, l: *const js.Local) Promise {
|
pub fn local(self: *const Self, l: *const js.Local) Promise {
|
||||||
return .{
|
return .{
|
||||||
.local = l,
|
.local = l,
|
||||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
.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/MouseEvent.zig"),
|
||||||
@import("../webapi/event/PointerEvent.zig"),
|
@import("../webapi/event/PointerEvent.zig"),
|
||||||
@import("../webapi/event/KeyboardEvent.zig"),
|
@import("../webapi/event/KeyboardEvent.zig"),
|
||||||
|
@import("../webapi/event/PromiseRejectionEvent.zig"),
|
||||||
@import("../webapi/MessageChannel.zig"),
|
@import("../webapi/MessageChannel.zig"),
|
||||||
@import("../webapi/MessagePort.zig"),
|
@import("../webapi/MessagePort.zig"),
|
||||||
@import("../webapi/media/MediaError.zig"),
|
@import("../webapi/media/MediaError.zig"),
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub const BigInt = @import("BigInt.zig");
|
|||||||
pub const Number = @import("Number.zig");
|
pub const Number = @import("Number.zig");
|
||||||
pub const Integer = @import("Integer.zig");
|
pub const Integer = @import("Integer.zig");
|
||||||
pub const PromiseResolver = @import("PromiseResolver.zig");
|
pub const PromiseResolver = @import("PromiseResolver.zig");
|
||||||
|
pub const PromiseRejection = @import("PromiseRejection.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
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(24, screen.pixelDepth);
|
||||||
testing.expectEqual(screen, window.screen);
|
testing.expectEqual(screen, window.screen);
|
||||||
</script>
|
</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"),
|
page_transition_event: *@import("event/PageTransitionEvent.zig"),
|
||||||
pop_state_event: *@import("event/PopStateEvent.zig"),
|
pop_state_event: *@import("event/PopStateEvent.zig"),
|
||||||
ui_event: *@import("event/UIEvent.zig"),
|
ui_event: *@import("event/UIEvent.zig"),
|
||||||
|
promise_rejection_event: *@import("event/PromiseRejectionEvent.zig"),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Options = struct {
|
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,
|
.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,
|
.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,
|
.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| {
|
.ui_event => |e| {
|
||||||
if (T == @import("event/UIEvent.zig")) {
|
if (T == @import("event/UIEvent.zig")) {
|
||||||
return e;
|
return e;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub fn entangle(port1: *MessagePort, port2: *MessagePort) void {
|
|||||||
port2._entangled_port = port1;
|
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) {
|
if (self._closed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ pub fn setOnMessageError(self: *MessagePort, cb: ?js.Function.Global) !void {
|
|||||||
|
|
||||||
const PostMessageCallback = struct {
|
const PostMessageCallback = struct {
|
||||||
port: *MessagePort,
|
port: *MessagePort,
|
||||||
message: js.Value.Global,
|
message: js.Value.Temp,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
|
|
||||||
fn deinit(self: *PostMessageCallback) void {
|
fn deinit(self: *PostMessageCallback) void {
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ const CSSStyleProperties = @import("css/CSSStyleProperties.zig");
|
|||||||
const CustomElementRegistry = @import("CustomElementRegistry.zig");
|
const CustomElementRegistry = @import("CustomElementRegistry.zig");
|
||||||
const Selection = @import("Selection.zig");
|
const Selection = @import("Selection.zig");
|
||||||
|
|
||||||
|
const IS_DEBUG = builtin.mode == .Debug;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const Window = @This();
|
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 {
|
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
||||||
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
||||||
.@"error" = try err.persist(),
|
.@"error" = try err.temp(),
|
||||||
.message = err.toStringSlice() catch "Unknown error",
|
.message = err.toStringSlice() catch "Unknown error",
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
.cancelable = true,
|
.cancelable = true,
|
||||||
@@ -342,7 +344,7 @@ pub fn getComputedStyle(_: *const Window, element: *Element, pseudo_element: ?[]
|
|||||||
return CSSStyleProperties.init(element, true, page);
|
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
|
// For now, we ignore targetOrigin checking and just dispatch the message
|
||||||
// In a full implementation, we would validate the origin
|
// In a full implementation, we would validate the origin
|
||||||
_ = target_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 {
|
const ScheduleOpts = struct {
|
||||||
repeat: bool,
|
repeat: bool,
|
||||||
params: []js.Value.Temp,
|
params: []js.Value.Temp,
|
||||||
@@ -626,7 +650,7 @@ const PostMessageCallback = struct {
|
|||||||
page: *Page,
|
page: *Page,
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
origin: []const u8,
|
origin: []const u8,
|
||||||
message: js.Value.Global,
|
message: js.Value.Temp,
|
||||||
|
|
||||||
fn deinit(self: *PostMessageCallback) void {
|
fn deinit(self: *PostMessageCallback) void {
|
||||||
self.page.releaseArena(self.arena);
|
self.page.releaseArena(self.arena);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const String = @import("../../..//string.zig").String;
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const String = @import("../../..//string.zig").String;
|
const String = @import("../../../string.zig").String;
|
||||||
|
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
@@ -27,11 +27,11 @@ const Allocator = std.mem.Allocator;
|
|||||||
const CustomEvent = @This();
|
const CustomEvent = @This();
|
||||||
|
|
||||||
_proto: *Event,
|
_proto: *Event,
|
||||||
_detail: ?js.Value.Global = null,
|
_detail: ?js.Value.Temp = null,
|
||||||
_arena: Allocator,
|
_arena: Allocator,
|
||||||
|
|
||||||
const CustomEventOptions = struct {
|
const CustomEventOptions = struct {
|
||||||
detail: ?js.Value.Global = null,
|
detail: ?js.Value.Temp = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
||||||
@@ -61,7 +61,7 @@ pub fn initCustomEvent(
|
|||||||
event_string: []const u8,
|
event_string: []const u8,
|
||||||
bubbles: ?bool,
|
bubbles: ?bool,
|
||||||
cancelable: ?bool,
|
cancelable: ?bool,
|
||||||
detail_: ?js.Value.Global,
|
detail_: ?js.Value.Temp,
|
||||||
) !void {
|
) !void {
|
||||||
// This function can only be called after the constructor has called.
|
// This function can only be called after the constructor has called.
|
||||||
// So we assume proto is initialized already by constructor.
|
// So we assume proto is initialized already by constructor.
|
||||||
@@ -73,14 +73,18 @@ pub fn initCustomEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *CustomEvent, shutdown: bool) void {
|
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 {
|
pub fn asEvent(self: *CustomEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDetail(self: *const CustomEvent) ?js.Value.Global {
|
pub fn getDetail(self: *const CustomEvent) ?js.Value.Temp {
|
||||||
return self._detail;
|
return self._detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ _message: []const u8 = "",
|
|||||||
_filename: []const u8 = "",
|
_filename: []const u8 = "",
|
||||||
_line_number: u32 = 0,
|
_line_number: u32 = 0,
|
||||||
_column_number: u32 = 0,
|
_column_number: u32 = 0,
|
||||||
_error: ?js.Value.Global = null,
|
_error: ?js.Value.Temp = null,
|
||||||
_arena: Allocator,
|
_arena: Allocator,
|
||||||
|
|
||||||
pub const ErrorEventOptions = struct {
|
pub const ErrorEventOptions = struct {
|
||||||
@@ -40,7 +40,7 @@ pub const ErrorEventOptions = struct {
|
|||||||
filename: ?[]const u8 = null,
|
filename: ?[]const u8 = null,
|
||||||
lineno: u32 = 0,
|
lineno: u32 = 0,
|
||||||
colno: u32 = 0,
|
colno: u32 = 0,
|
||||||
@"error": ?js.Value.Global = null,
|
@"error": ?js.Value.Temp = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
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 {
|
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 {
|
pub fn asEvent(self: *ErrorEvent) *Event {
|
||||||
@@ -103,7 +107,7 @@ pub fn getColumnNumber(self: *const ErrorEvent) u32 {
|
|||||||
return self._column_number;
|
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;
|
return self._error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ const Allocator = std.mem.Allocator;
|
|||||||
const MessageEvent = @This();
|
const MessageEvent = @This();
|
||||||
|
|
||||||
_proto: *Event,
|
_proto: *Event,
|
||||||
_data: ?js.Value.Global = null,
|
_data: ?js.Value.Temp = null,
|
||||||
_origin: []const u8 = "",
|
_origin: []const u8 = "",
|
||||||
_source: ?*Window = null,
|
_source: ?*Window = null,
|
||||||
|
|
||||||
const MessageEventOptions = struct {
|
const MessageEventOptions = struct {
|
||||||
data: ?js.Value.Global = null,
|
data: ?js.Value.Temp = null,
|
||||||
origin: ?[]const u8 = null,
|
origin: ?[]const u8 = null,
|
||||||
source: ?*Window = 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 {
|
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 {
|
pub fn asEvent(self: *MessageEvent) *Event {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getData(self: *const MessageEvent) ?js.Value.Global {
|
pub fn getData(self: *const MessageEvent) ?js.Value.Temp {
|
||||||
return self._data;
|
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 page = self._page;
|
||||||
|
const js_ctx = page.js;
|
||||||
if (self._on_ready_state_change) |func| {
|
if (self._on_ready_state_change) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const proto = self._proto;
|
const proto = self._proto;
|
||||||
if (proto._on_abort) |func| {
|
if (proto._on_abort) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_error) |func| {
|
if (proto._on_error) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_load) |func| {
|
if (proto._on_load) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_load_end) |func| {
|
if (proto._on_load_end) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_load_start) |func| {
|
if (proto._on_load_start) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_progress) |func| {
|
if (proto._on_progress) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
if (proto._on_timeout) |func| {
|
if (proto._on_timeout) |func| {
|
||||||
page.js.release(func);
|
js_ctx.release(func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user