mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-21 20:24:42 +00:00
return correct errors from promises
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);
|
||||
}
|
||||
|
||||
@@ -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, comptime kind: js.PromiseResolver.RejectError) js.Promise {
|
||||
var resolver = js.PromiseResolver.init(self);
|
||||
resolver.reject("Local.rejectPromise", value);
|
||||
resolver.rejectError("Local.rejectPromise", kind);
|
||||
return resolver.promise();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ const v8 = js.v8;
|
||||
const log = @import("../../log.zig");
|
||||
const DOMException = @import("../webapi/DOMException.zig");
|
||||
|
||||
const DOMException = @import("../webapi/DOMException.zig");
|
||||
|
||||
const PromiseResolver = @This();
|
||||
|
||||
local: *const js.Local,
|
||||
@@ -65,20 +67,43 @@ pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype
|
||||
};
|
||||
}
|
||||
|
||||
pub const RejectError = union(enum) {
|
||||
generic: []const u8,
|
||||
pub const RejectError = union(enum(u4)) {
|
||||
/// 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,
|
||||
wasm_compile_error: void, // TODO.
|
||||
wasm_link_error: void, // TODO.
|
||||
wasm_runtime_error: void, // TODO.
|
||||
wasm_suspend_error: void, // TODO.
|
||||
/// 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 {
|
||||
const handle = switch (err) {
|
||||
.type_error => |str| self.local.isolate.createTypeError(str),
|
||||
.generic => |str| self.local.isolate.createError(str),
|
||||
|
||||
/// Rejects the promise w/ an error object.
|
||||
pub fn rejectError(
|
||||
self: PromiseResolver,
|
||||
comptime source: []const u8,
|
||||
comptime kind: RejectError,
|
||||
) void {
|
||||
const handle = switch (kind) {
|
||||
.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;
|
||||
},
|
||||
inline else => unreachable,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
@@ -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