mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1925 from lightpanda-io/nikneym/promise-error
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / wba-demo-scripts (push) Has been cancelled
e2e-test / wba-test (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig fmt (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / wba-demo-scripts (push) Has been cancelled
e2e-test / wba-test (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig fmt (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Return correct errors in promise rejections
This commit is contained in:
@@ -545,13 +545,13 @@ pub fn dynamicModuleCallback(
|
||||
|
||||
break :blk js.String.toSliceZ(.{ .local = &local, .handle = resource_name.? }) catch |err| {
|
||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback1" });
|
||||
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
||||
return @constCast(local.rejectPromise(.{ .generic_error = "Out of memory" }).handle);
|
||||
};
|
||||
};
|
||||
|
||||
const specifier = js.String.toSliceZ(.{ .local = &local, .handle = v8_specifier.? }) catch |err| {
|
||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback2" });
|
||||
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
||||
return @constCast(local.rejectPromise(.{ .generic_error = "Out of memory" }).handle);
|
||||
};
|
||||
|
||||
const normalized_specifier = self.script_manager.?.resolveSpecifier(
|
||||
@@ -560,14 +560,14 @@ pub fn dynamicModuleCallback(
|
||||
specifier,
|
||||
) catch |err| {
|
||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback3" });
|
||||
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
||||
return @constCast(local.rejectPromise(.{ .generic_error = "Out of memory" }).handle);
|
||||
};
|
||||
|
||||
const promise = self._dynamicModuleCallback(normalized_specifier, resource, &local) catch |err| blk: {
|
||||
log.err(.js, "dynamic module callback", .{
|
||||
.err = err,
|
||||
});
|
||||
break :blk local.rejectPromise("Failed to load module") catch return null;
|
||||
break :blk local.rejectPromise(.{ .generic_error = "Out of memory" });
|
||||
};
|
||||
return @constCast(promise.handle);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,21 @@ pub fn createError(self: Isolate, msg: []const u8) *const v8.Value {
|
||||
return v8.v8__Exception__Error(message).?;
|
||||
}
|
||||
|
||||
pub fn createRangeError(self: Isolate, msg: []const u8) *const v8.Value {
|
||||
const message = self.initStringHandle(msg);
|
||||
return v8.v8__Exception__RangeError(message).?;
|
||||
}
|
||||
|
||||
pub fn createReferenceError(self: Isolate, msg: []const u8) *const v8.Value {
|
||||
const message = self.initStringHandle(msg);
|
||||
return v8.v8__Exception__ReferenceError(message).?;
|
||||
}
|
||||
|
||||
pub fn createSyntaxError(self: Isolate, msg: []const u8) *const v8.Value {
|
||||
const message = self.initStringHandle(msg);
|
||||
return v8.v8__Exception__SyntaxError(message).?;
|
||||
}
|
||||
|
||||
pub fn createTypeError(self: Isolate, msg: []const u8) *const v8.Value {
|
||||
const message = self.initStringHandle(msg);
|
||||
return v8.v8__Exception__TypeError(message).?;
|
||||
|
||||
@@ -1206,9 +1206,9 @@ pub fn stackTrace(self: *const Local) !?[]const u8 {
|
||||
}
|
||||
|
||||
// == Promise Helpers ==
|
||||
pub fn rejectPromise(self: *const Local, value: anytype) !js.Promise {
|
||||
pub fn rejectPromise(self: *const Local, err: js.PromiseResolver.RejectError) js.Promise {
|
||||
var resolver = js.PromiseResolver.init(self);
|
||||
resolver.reject("Local.rejectPromise", value);
|
||||
resolver.rejectError("Local.rejectPromise", err);
|
||||
return resolver.promise();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const js = @import("js.zig");
|
||||
const v8 = js.v8;
|
||||
|
||||
const log = @import("../../log.zig");
|
||||
|
||||
const DOMException = @import("../webapi/DOMException.zig");
|
||||
|
||||
const PromiseResolver = @This();
|
||||
@@ -66,19 +67,37 @@ pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype
|
||||
}
|
||||
|
||||
pub const RejectError = union(enum) {
|
||||
generic: []const u8,
|
||||
/// Not to be confused with `DOMException`; this is bare `Error`.
|
||||
generic_error: []const u8,
|
||||
range_error: []const u8,
|
||||
reference_error: []const u8,
|
||||
syntax_error: []const u8,
|
||||
type_error: []const u8,
|
||||
dom_exception: anyerror,
|
||||
/// DOM exceptions are unknown to V8, belongs to web standards.
|
||||
dom_exception: struct { err: anyerror },
|
||||
};
|
||||
pub fn rejectError(self: PromiseResolver, comptime source: []const u8, err: RejectError) void {
|
||||
|
||||
/// Rejects the promise w/ an error object.
|
||||
pub fn rejectError(
|
||||
self: PromiseResolver,
|
||||
comptime source: []const u8,
|
||||
err: RejectError,
|
||||
) void {
|
||||
const handle = switch (err) {
|
||||
.type_error => |str| self.local.isolate.createTypeError(str),
|
||||
.generic => |str| self.local.isolate.createError(str),
|
||||
.generic_error => |msg| self.local.isolate.createError(msg),
|
||||
.range_error => |msg| self.local.isolate.createRangeError(msg),
|
||||
.reference_error => |msg| self.local.isolate.createReferenceError(msg),
|
||||
.syntax_error => |msg| self.local.isolate.createSyntaxError(msg),
|
||||
.type_error => |msg| self.local.isolate.createTypeError(msg),
|
||||
// "Exceptional".
|
||||
.dom_exception => |exception| {
|
||||
self.reject(source, DOMException.fromError(exception));
|
||||
self._reject(DOMException.fromError(exception.err) orelse unreachable) catch |reject_err| {
|
||||
log.err(.bug, "rejectDomException", .{ .source = source, .err = reject_err, .persistent = false });
|
||||
};
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
self._reject(js.Value{ .handle = handle, .local = self.local }) catch |reject_err| {
|
||||
log.err(.bug, "rejectError", .{ .source = source, .err = reject_err, .persistent = false });
|
||||
};
|
||||
|
||||
@@ -125,8 +125,8 @@ pub fn whenDefined(self: *CustomElementRegistry, name: []const u8, page: *Page)
|
||||
return local.resolvePromise(definition.constructor);
|
||||
}
|
||||
|
||||
validateName(name) catch |err| {
|
||||
return local.rejectPromise(DOMException.fromError(err) orelse unreachable);
|
||||
validateName(name) catch |err| switch (err) {
|
||||
error.SyntaxError => return local.rejectPromise(.{ .dom_exception = .{ .err = error.SyntaxError } }),
|
||||
};
|
||||
|
||||
const gop = try self._when_defined.getOrPut(page.arena, name);
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn getStorage(self: *Navigator) *StorageManager {
|
||||
|
||||
pub fn getBattery(_: *const Navigator, page: *Page) !js.Promise {
|
||||
log.info(.not_implemented, "navigator.getBattery", .{});
|
||||
return page.js.local.?.rejectErrorPromise(.{ .dom_exception = error.NotSupported });
|
||||
return page.js.local.?.rejectErrorPromise(.{ .dom_exception = .{ .err = error.NotSupported } });
|
||||
}
|
||||
|
||||
pub fn registerProtocolHandler(_: *const Navigator, scheme: []const u8, url: [:0]const u8, page: *const Page) !void {
|
||||
|
||||
@@ -96,8 +96,8 @@ pub fn generateKey(
|
||||
key_usages: []const []const u8,
|
||||
page: *Page,
|
||||
) !js.Promise {
|
||||
const key_or_pair = CryptoKey.init(algorithm, extractable, key_usages, page) catch |err| {
|
||||
return page.js.local.?.rejectPromise(@errorName(err));
|
||||
const key_or_pair = CryptoKey.init(algorithm, extractable, key_usages, page) catch {
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.SyntaxError } });
|
||||
};
|
||||
|
||||
return page.js.local.?.resolvePromise(key_or_pair);
|
||||
@@ -112,7 +112,7 @@ pub fn exportKey(
|
||||
page: *Page,
|
||||
) !js.Promise {
|
||||
if (!key.canExportKey()) {
|
||||
return error.InvalidAccessError;
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, format, "raw")) {
|
||||
@@ -124,9 +124,10 @@ pub fn exportKey(
|
||||
|
||||
if (is_unsupported) {
|
||||
log.warn(.not_implemented, "SubtleCrypto.exportKey", .{ .format = format });
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.NotSupported } });
|
||||
}
|
||||
|
||||
return page.js.local.?.rejectPromise(@errorName(error.NotSupported));
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "invalid format" });
|
||||
}
|
||||
|
||||
/// Derive a secret key from a master key.
|
||||
@@ -148,7 +149,7 @@ pub fn deriveBits(
|
||||
log.warn(.not_implemented, "SubtleCrypto.deriveBits", .{ .name = name });
|
||||
}
|
||||
|
||||
return page.js.local.?.rejectPromise(@errorName(error.NotSupported));
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.NotSupported } });
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -185,19 +186,19 @@ pub fn sign(
|
||||
.hmac => {
|
||||
// Verify algorithm.
|
||||
if (!algorithm.isHMAC()) {
|
||||
return page.js.local.?.rejectPromise(@errorName(error.InvalidAccessError));
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
}
|
||||
|
||||
// Call sign for HMAC.
|
||||
const result = key.signHMAC(data, page) catch |err| {
|
||||
return page.js.local.?.rejectPromise(@errorName(err));
|
||||
const result = key.signHMAC(data, page) catch {
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
};
|
||||
|
||||
return page.js.local.?.resolvePromise(result);
|
||||
},
|
||||
else => {
|
||||
log.warn(.not_implemented, "SubtleCrypto.sign", .{ .key_type = key._type });
|
||||
return page.js.local.?.rejectPromise(@errorName(error.InvalidAccessError));
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -211,18 +212,20 @@ pub fn verify(
|
||||
data: []const u8, // ArrayBuffer.
|
||||
page: *Page,
|
||||
) !js.Promise {
|
||||
if (!algorithm.isHMAC()) return error.InvalidAccessError;
|
||||
if (!algorithm.isHMAC()) {
|
||||
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||
}
|
||||
|
||||
return switch (key._type) {
|
||||
.hmac => key.verifyHMAC(signature, data, page),
|
||||
else => return error.InvalidAccessError,
|
||||
else => page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } }),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn digest(_: *const SubtleCrypto, algorithm: []const u8, data: js.TypedArray(u8), page: *Page) !js.Promise {
|
||||
const local = page.js.local.?;
|
||||
if (algorithm.len > 10) {
|
||||
return local.rejectPromise(DOMException.fromError(error.NotSupported));
|
||||
return local.rejectPromise(.{ .dom_exception = .{ .err = error.NotSupported } });
|
||||
}
|
||||
const normalized = std.ascii.lowerString(&page.buf, algorithm);
|
||||
if (std.mem.eql(u8, normalized, "sha-1")) {
|
||||
@@ -245,7 +248,7 @@ pub fn digest(_: *const SubtleCrypto, algorithm: []const u8, data: js.TypedArray
|
||||
Sha512.hash(data.values, page.buf[0..Sha512.digest_length], .{});
|
||||
return local.resolvePromise(js.ArrayBuffer{ .values = page.buf[0..Sha512.digest_length] });
|
||||
}
|
||||
return local.rejectPromise(DOMException.fromError(error.NotSupported));
|
||||
return local.rejectPromise(.{ .dom_exception = .{ .err = error.NotSupported } });
|
||||
}
|
||||
|
||||
/// Returns the desired digest by its name.
|
||||
|
||||
@@ -243,7 +243,7 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
||||
defer ls.deinit();
|
||||
|
||||
// fetch() must reject with a TypeError on network errors per spec
|
||||
ls.toLocal(self._resolver).rejectError("fetch error", .{ .type_error = @errorName(err) });
|
||||
ls.toLocal(self._resolver).rejectError("fetch error", .{ .type_error = "fetch error" });
|
||||
}
|
||||
|
||||
fn httpShutdownCallback(ctx: *anyopaque) void {
|
||||
|
||||
@@ -192,8 +192,8 @@ pub fn text(self: *const Request, page: *Page) !js.Promise {
|
||||
pub fn json(self: *const Request, page: *Page) !js.Promise {
|
||||
const body = self._body orelse "";
|
||||
const local = page.js.local.?;
|
||||
const value = local.parseJSON(body) catch |err| {
|
||||
return local.rejectPromise(.{@errorName(err)});
|
||||
const value = local.parseJSON(body) catch {
|
||||
return local.rejectPromise(.{ .syntax_error = "failed to parse" });
|
||||
};
|
||||
return local.resolvePromise(try value.persist());
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@ pub fn getText(self: *const Response, page: *Page) !js.Promise {
|
||||
pub fn getJson(self: *Response, page: *Page) !js.Promise {
|
||||
const body = self._body orelse "";
|
||||
const local = page.js.local.?;
|
||||
const value = local.parseJSON(body) catch |err| {
|
||||
return local.rejectPromise(.{@errorName(err)});
|
||||
const value = local.parseJSON(body) catch {
|
||||
return local.rejectPromise(.{ .syntax_error = "failed to parse" });
|
||||
};
|
||||
return local.resolvePromise(try value.persist());
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ pub fn pipeThrough(self: *ReadableStream, transform: PipeTransform, page: *Page)
|
||||
/// Returns a promise that resolves when piping is complete.
|
||||
pub fn pipeTo(self: *ReadableStream, destination: *WritableStream, page: *Page) !js.Promise {
|
||||
if (self.getLocked()) {
|
||||
return page.js.local.?.rejectPromise("ReadableStream is locked");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "ReadableStream is locked" });
|
||||
}
|
||||
|
||||
const local = page.js.local.?;
|
||||
|
||||
@@ -58,12 +58,12 @@ pub const ReadResult = struct {
|
||||
|
||||
pub fn read(self: *ReadableStreamDefaultReader, page: *Page) !js.Promise {
|
||||
const stream = self._stream orelse {
|
||||
return page.js.local.?.rejectPromise("Reader has been released");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Reader has been released" });
|
||||
};
|
||||
|
||||
if (stream._state == .errored) {
|
||||
const err = stream._stored_error orelse "Stream errored";
|
||||
return page.js.local.?.rejectPromise(err);
|
||||
//const err = stream._stored_error orelse "Stream errored";
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Stream errored" });
|
||||
}
|
||||
|
||||
if (stream._controller.dequeue()) |chunk| {
|
||||
@@ -95,7 +95,7 @@ pub fn releaseLock(self: *ReadableStreamDefaultReader) void {
|
||||
|
||||
pub fn cancel(self: *ReadableStreamDefaultReader, reason_: ?[]const u8, page: *Page) !js.Promise {
|
||||
const stream = self._stream orelse {
|
||||
return page.js.local.?.rejectPromise("Reader has been released");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Reader has been released" });
|
||||
};
|
||||
|
||||
self.releaseLock();
|
||||
|
||||
@@ -32,11 +32,11 @@ pub fn init(stream: *WritableStream, page: *Page) !*WritableStreamDefaultWriter
|
||||
|
||||
pub fn write(self: *WritableStreamDefaultWriter, chunk: js.Value, page: *Page) !js.Promise {
|
||||
const stream = self._stream orelse {
|
||||
return page.js.local.?.rejectPromise("Writer has been released");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Writer has been released" });
|
||||
};
|
||||
|
||||
if (stream._state != .writable) {
|
||||
return page.js.local.?.rejectPromise("Stream is not writable");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Stream is not writable" });
|
||||
}
|
||||
|
||||
try stream.writeChunk(chunk, page);
|
||||
@@ -46,11 +46,11 @@ pub fn write(self: *WritableStreamDefaultWriter, chunk: js.Value, page: *Page) !
|
||||
|
||||
pub fn close(self: *WritableStreamDefaultWriter, page: *Page) !js.Promise {
|
||||
const stream = self._stream orelse {
|
||||
return page.js.local.?.rejectPromise("Writer has been released");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Writer has been released" });
|
||||
};
|
||||
|
||||
if (stream._state != .writable) {
|
||||
return page.js.local.?.rejectPromise("Stream is not writable");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Stream is not writable" });
|
||||
}
|
||||
|
||||
try stream.closeStream(page);
|
||||
@@ -67,7 +67,7 @@ pub fn releaseLock(self: *WritableStreamDefaultWriter) void {
|
||||
|
||||
pub fn getClosed(self: *WritableStreamDefaultWriter, page: *Page) !js.Promise {
|
||||
const stream = self._stream orelse {
|
||||
return page.js.local.?.rejectPromise("Writer has been released");
|
||||
return page.js.local.?.rejectPromise(.{ .type_error = "Writer has been released" });
|
||||
};
|
||||
|
||||
if (stream._state == .closed) {
|
||||
|
||||
Reference in New Issue
Block a user