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| {
|
break :blk js.String.toSliceZ(.{ .local = &local, .handle = resource_name.? }) catch |err| {
|
||||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback1" });
|
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| {
|
const specifier = js.String.toSliceZ(.{ .local = &local, .handle = v8_specifier.? }) catch |err| {
|
||||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback2" });
|
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(
|
const normalized_specifier = self.script_manager.?.resolveSpecifier(
|
||||||
@@ -560,14 +560,14 @@ pub fn dynamicModuleCallback(
|
|||||||
specifier,
|
specifier,
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback3" });
|
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: {
|
const promise = self._dynamicModuleCallback(normalized_specifier, resource, &local) catch |err| blk: {
|
||||||
log.err(.js, "dynamic module callback", .{
|
log.err(.js, "dynamic module callback", .{
|
||||||
.err = err,
|
.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);
|
return @constCast(promise.handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1206,9 +1206,9 @@ pub fn stackTrace(self: *const Local) !?[]const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// == Promise Helpers ==
|
// == 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);
|
var resolver = js.PromiseResolver.init(self);
|
||||||
resolver.reject("Local.rejectPromise", value);
|
resolver.rejectError("Local.rejectPromise", kind);
|
||||||
return resolver.promise();
|
return resolver.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ const v8 = js.v8;
|
|||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const DOMException = @import("../webapi/DOMException.zig");
|
const DOMException = @import("../webapi/DOMException.zig");
|
||||||
|
|
||||||
|
const DOMException = @import("../webapi/DOMException.zig");
|
||||||
|
|
||||||
const PromiseResolver = @This();
|
const PromiseResolver = @This();
|
||||||
|
|
||||||
local: *const js.Local,
|
local: *const js.Local,
|
||||||
@@ -65,20 +67,43 @@ pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const RejectError = union(enum) {
|
pub const RejectError = union(enum(u4)) {
|
||||||
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,
|
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) {
|
/// Rejects the promise w/ an error object.
|
||||||
.type_error => |str| self.local.isolate.createTypeError(str),
|
pub fn rejectError(
|
||||||
.generic => |str| self.local.isolate.createError(str),
|
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| {
|
.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;
|
return;
|
||||||
},
|
},
|
||||||
|
inline else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
self._reject(js.Value{ .handle = handle, .local = self.local }) catch |reject_err| {
|
self._reject(js.Value{ .handle = handle, .local = self.local }) catch |reject_err| {
|
||||||
log.err(.bug, "rejectError", .{ .source = source, .err = reject_err, .persistent = false });
|
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);
|
return local.resolvePromise(definition.constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
validateName(name) catch |err| {
|
validateName(name) catch |err| switch (err) {
|
||||||
return local.rejectPromise(DOMException.fromError(err) orelse unreachable);
|
error.SyntaxError => return local.rejectPromise(.{ .dom_exception = .{ .err = error.SyntaxError } }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const gop = try self._when_defined.getOrPut(page.arena, name);
|
const gop = try self._when_defined.getOrPut(page.arena, name);
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ pub fn generateKey(
|
|||||||
key_usages: []const []const u8,
|
key_usages: []const []const u8,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
) !js.Promise {
|
) !js.Promise {
|
||||||
const key_or_pair = CryptoKey.init(algorithm, extractable, key_usages, page) catch |err| {
|
const key_or_pair = CryptoKey.init(algorithm, extractable, key_usages, page) catch {
|
||||||
return page.js.local.?.rejectPromise(@errorName(err));
|
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.SyntaxError } });
|
||||||
};
|
};
|
||||||
|
|
||||||
return page.js.local.?.resolvePromise(key_or_pair);
|
return page.js.local.?.resolvePromise(key_or_pair);
|
||||||
@@ -112,7 +112,7 @@ pub fn exportKey(
|
|||||||
page: *Page,
|
page: *Page,
|
||||||
) !js.Promise {
|
) !js.Promise {
|
||||||
if (!key.canExportKey()) {
|
if (!key.canExportKey()) {
|
||||||
return error.InvalidAccessError;
|
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.eql(u8, format, "raw")) {
|
if (std.mem.eql(u8, format, "raw")) {
|
||||||
@@ -124,9 +124,10 @@ pub fn exportKey(
|
|||||||
|
|
||||||
if (is_unsupported) {
|
if (is_unsupported) {
|
||||||
log.warn(.not_implemented, "SubtleCrypto.exportKey", .{ .format = format });
|
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.
|
/// Derive a secret key from a master key.
|
||||||
@@ -148,7 +149,7 @@ pub fn deriveBits(
|
|||||||
log.warn(.not_implemented, "SubtleCrypto.deriveBits", .{ .name = name });
|
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 => {
|
.hmac => {
|
||||||
// Verify algorithm.
|
// Verify algorithm.
|
||||||
if (!algorithm.isHMAC()) {
|
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.
|
// Call sign for HMAC.
|
||||||
const result = key.signHMAC(data, page) catch |err| {
|
const result = key.signHMAC(data, page) catch {
|
||||||
return page.js.local.?.rejectPromise(@errorName(err));
|
return page.js.local.?.rejectPromise(.{ .dom_exception = .{ .err = error.InvalidAccessError } });
|
||||||
};
|
};
|
||||||
|
|
||||||
return page.js.local.?.resolvePromise(result);
|
return page.js.local.?.resolvePromise(result);
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
log.warn(.not_implemented, "SubtleCrypto.sign", .{ .key_type = key._type });
|
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.
|
data: []const u8, // ArrayBuffer.
|
||||||
page: *Page,
|
page: *Page,
|
||||||
) !js.Promise {
|
) !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) {
|
return switch (key._type) {
|
||||||
.hmac => key.verifyHMAC(signature, data, page),
|
.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 {
|
pub fn digest(_: *const SubtleCrypto, algorithm: []const u8, data: js.TypedArray(u8), page: *Page) !js.Promise {
|
||||||
const local = page.js.local.?;
|
const local = page.js.local.?;
|
||||||
if (algorithm.len > 10) {
|
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);
|
const normalized = std.ascii.lowerString(&page.buf, algorithm);
|
||||||
if (std.mem.eql(u8, normalized, "sha-1")) {
|
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], .{});
|
Sha512.hash(data.values, page.buf[0..Sha512.digest_length], .{});
|
||||||
return local.resolvePromise(js.ArrayBuffer{ .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.
|
/// Returns the desired digest by its name.
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
|||||||
defer ls.deinit();
|
defer ls.deinit();
|
||||||
|
|
||||||
// fetch() must reject with a TypeError on network errors per spec
|
// 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 {
|
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 {
|
pub fn json(self: *const Request, page: *Page) !js.Promise {
|
||||||
const body = self._body orelse "";
|
const body = self._body orelse "";
|
||||||
const local = page.js.local.?;
|
const local = page.js.local.?;
|
||||||
const value = local.parseJSON(body) catch |err| {
|
const value = local.parseJSON(body) catch {
|
||||||
return local.rejectPromise(.{@errorName(err)});
|
return local.rejectPromise(.{ .syntax_error = "failed to parse" });
|
||||||
};
|
};
|
||||||
return local.resolvePromise(try value.persist());
|
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 {
|
pub fn getJson(self: *Response, page: *Page) !js.Promise {
|
||||||
const body = self._body orelse "";
|
const body = self._body orelse "";
|
||||||
const local = page.js.local.?;
|
const local = page.js.local.?;
|
||||||
const value = local.parseJSON(body) catch |err| {
|
const value = local.parseJSON(body) catch {
|
||||||
return local.rejectPromise(.{@errorName(err)});
|
return local.rejectPromise(.{ .syntax_error = "failed to parse" });
|
||||||
};
|
};
|
||||||
return local.resolvePromise(try value.persist());
|
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.
|
/// Returns a promise that resolves when piping is complete.
|
||||||
pub fn pipeTo(self: *ReadableStream, destination: *WritableStream, page: *Page) !js.Promise {
|
pub fn pipeTo(self: *ReadableStream, destination: *WritableStream, page: *Page) !js.Promise {
|
||||||
if (self.getLocked()) {
|
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.?;
|
const local = page.js.local.?;
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ pub const ReadResult = struct {
|
|||||||
|
|
||||||
pub fn read(self: *ReadableStreamDefaultReader, page: *Page) !js.Promise {
|
pub fn read(self: *ReadableStreamDefaultReader, page: *Page) !js.Promise {
|
||||||
const stream = self._stream orelse {
|
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) {
|
if (stream._state == .errored) {
|
||||||
const err = stream._stored_error orelse "Stream errored";
|
//const err = stream._stored_error orelse "Stream errored";
|
||||||
return page.js.local.?.rejectPromise(err);
|
return page.js.local.?.rejectPromise(.{ .type_error = "Stream errored" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream._controller.dequeue()) |chunk| {
|
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 {
|
pub fn cancel(self: *ReadableStreamDefaultReader, reason_: ?[]const u8, page: *Page) !js.Promise {
|
||||||
const stream = self._stream orelse {
|
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();
|
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 {
|
pub fn write(self: *WritableStreamDefaultWriter, chunk: js.Value, page: *Page) !js.Promise {
|
||||||
const stream = self._stream orelse {
|
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) {
|
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);
|
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 {
|
pub fn close(self: *WritableStreamDefaultWriter, page: *Page) !js.Promise {
|
||||||
const stream = self._stream orelse {
|
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) {
|
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);
|
try stream.closeStream(page);
|
||||||
@@ -67,7 +67,7 @@ pub fn releaseLock(self: *WritableStreamDefaultWriter) void {
|
|||||||
|
|
||||||
pub fn getClosed(self: *WritableStreamDefaultWriter, page: *Page) !js.Promise {
|
pub fn getClosed(self: *WritableStreamDefaultWriter, page: *Page) !js.Promise {
|
||||||
const stream = self._stream orelse {
|
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) {
|
if (stream._state == .closed) {
|
||||||
|
|||||||
Reference in New Issue
Block a user