From 57dcbe1ba923064d68f64436a9a95bc01146f31a Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Thu, 23 Nov 2023 17:14:01 +0100 Subject: [PATCH 1/4] Add DOMException Signed-off-by: Francis Bouvier --- src/dom/dom.zig | 2 + src/dom/event_target.zig | 3 + src/dom/exceptions.zig | 133 +++++++++++++++++++++++++++++++++++++++ src/dom/node.zig | 4 +- src/netsurf.zig | 53 +++++++++++++++- src/run_tests.zig | 2 + 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 src/dom/exceptions.zig diff --git a/src/dom/dom.zig b/src/dom/dom.zig index c5d5da7f..9fb3ad79 100644 --- a/src/dom/dom.zig +++ b/src/dom/dom.zig @@ -1,9 +1,11 @@ const generate = @import("../generate.zig"); +const DOMException = @import("exceptions.zig").DOMException; const EventTarget = @import("event_target.zig").EventTarget; const Nod = @import("node.zig"); pub const Interfaces = generate.Tuple(.{ + DOMException, EventTarget, Nod.Node, Nod.Interfaces, diff --git a/src/dom/event_target.zig b/src/dom/event_target.zig index 7d0fccdc..9058f9c0 100644 --- a/src/dom/event_target.zig +++ b/src/dom/event_target.zig @@ -1,6 +1,9 @@ const parser = @import("../netsurf.zig"); +const DOMException = @import("exceptions.zig").DOMException; + pub const EventTarget = struct { pub const Self = parser.EventTarget; + pub const Exception = DOMException; pub const mem_guarantied = true; }; diff --git a/src/dom/exceptions.zig b/src/dom/exceptions.zig new file mode 100644 index 00000000..945f7d9d --- /dev/null +++ b/src/dom/exceptions.zig @@ -0,0 +1,133 @@ +const std = @import("std"); +const allocPrint = std.fmt.allocPrint; + +const jsruntime = @import("jsruntime"); +const Case = jsruntime.test_utils.Case; +const checkCases = jsruntime.test_utils.checkCases; + +const parser = @import("../netsurf.zig"); + +pub const DOMException = struct { + err: parser.DOMError, + str: []const u8, + + pub const mem_guarantied = true; + + pub const ErrorSet = parser.DOMError; + + // static attributes + pub const _INDEX_SIZE_ERR = 1; + pub const _DOMSTRING_SIZE_ERR = 2; + pub const _HIERARCHY_REQUEST_ERR = 3; + pub const _WRONG_DOCUMENT_ERR = 4; + pub const _INVALID_CHARACTER_ERR = 5; + pub const _NO_DATA_ALLOWED_ERR = 6; + pub const _NO_MODIFICATION_ALLOWED_ERR = 7; + pub const _NOT_FOUND_ERR = 8; + pub const _NOT_SUPPORTED_ERR = 9; + pub const _INUSE_ATTRIBUTE_ERR = 10; + pub const _INVALID_STATE_ERR = 11; + pub const _SYNTAX_ERR = 12; + pub const _INVALID_MODIFICATION_ERR = 13; + pub const _NAMESPACE_ERR = 14; + pub const _INVALID_ACCESS_ERR = 15; + pub const _VALIDATION_ERR = 16; + pub const _TYPE_MISMATCH_ERR = 17; + + // TODO: deinit + pub fn init(alloc: std.mem.Allocator, err: parser.DOMError, callerName: []const u8) anyerror!DOMException { + const errName = DOMException.name(err); + const str = switch (err) { + error.HierarchyRequest => try allocPrint( + alloc, + "{s}: Failed to execute '{s}' on 'Node': The new child element contains the parent.", + .{ errName, callerName }, + ), + error.NoError => unreachable, + else => "", // TODO: implement other messages + }; + return .{ .err = err, .str = str }; + } + + fn name(err: parser.DOMError) []const u8 { + return switch (err) { + error.IndexSize => "IndexSizeError", + error.StringSize => "StringSizeError", + error.HierarchyRequest => "HierarchyRequestError", + error.WrongDocument => "WrongDocumentError", + error.InvalidCharacter => "InvalidCharacterError", + error.NoDataAllowed => "NoDataAllowedError", + error.NoModificationAllowed => "NoModificationAllowedError", + error.NotFound => "NotFoundError", + error.NotSupported => "NotSupportedError", + error.InuseAttribute => "InuseAttributeError", + error.InvalidState => "InvalidStateError", + error.Syntax => "SyntaxError", + error.InvalidModification => "InvalidModificationError", + error.Namespace => "NamespaceError", + error.InvalidAccess => "InvalidAccessError", + error.Validation => "ValidationError", + error.TypeMismatch => "TypeMismatchError", + error.NoError => unreachable, + }; + } + + // JS properties and methods + + pub fn get_code(self: DOMException) u8 { + return switch (self.err) { + error.IndexSize => 1, + error.StringSize => 2, + error.HierarchyRequest => 3, + error.WrongDocument => 4, + error.InvalidCharacter => 5, + error.NoDataAllowed => 6, + error.NoModificationAllowed => 7, + error.NotFound => 8, + error.NotSupported => 9, + error.InuseAttribute => 10, + error.InvalidState => 11, + error.Syntax => 12, + error.InvalidModification => 13, + error.Namespace => 14, + error.InvalidAccess => 15, + error.Validation => 16, + error.TypeMismatch => 17, + error.NoError => unreachable, + }; + } + + pub fn get_name(self: DOMException) []const u8 { + return DOMException.name(self.err); + } + + pub fn get_message(self: DOMException) []const u8 { + const errName = DOMException.name(self.err); + return self.str[errName.len + 2 ..]; + } + + pub fn _toString(self: DOMException) []const u8 { + return self.str; + } +}; + +// Tests +// ----- + +pub fn testExecFn( + _: std.mem.Allocator, + js_env: *jsruntime.Env, + comptime _: []jsruntime.API, +) !void { + const err = "Failed to execute 'appendChild' on 'Node': The new child element contains the parent."; + var cases = [_]Case{ + .{ .src = "let content = document.getElementById('content')", .ex = "undefined" }, + .{ .src = "let link = document.getElementById('link')", .ex = "undefined" }, + // HierarchyRequestError + .{ .src = "var HierarchyRequestError; try {link.appendChild(content)} catch (error) {HierarchyRequestError = error} HierarchyRequestError.name", .ex = "HierarchyRequestError" }, + .{ .src = "HierarchyRequestError.code", .ex = "3" }, + .{ .src = "HierarchyRequestError.message", .ex = err }, + .{ .src = "HierarchyRequestError.toString()", .ex = "HierarchyRequestError: " ++ err }, + }; + try checkCases(js_env, &cases); +} diff --git a/src/dom/node.zig b/src/dom/node.zig index 7125eec9..7fd67c12 100644 --- a/src/dom/node.zig +++ b/src/dom/node.zig @@ -150,9 +150,9 @@ pub const Node = struct { // Methods - pub fn _appendChild(self: *parser.Node, child: *parser.Node) Union { + pub fn _appendChild(self: *parser.Node, child: *parser.Node) !Union { // TODO: DocumentFragment special case - const res = parser.nodeAppendChild(self, child); + const res = try parser.nodeAppendChild(self, child); return Node.toInterface(res); } diff --git a/src/netsurf.zig b/src/netsurf.zig index 7afaafec..5aa69f72 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -274,6 +274,54 @@ pub const Tag = enum(u8) { } }; +// DOMException + +pub const DOMError = error{ + NoError, + IndexSize, + StringSize, + HierarchyRequest, + WrongDocument, + InvalidCharacter, + NoDataAllowed, + NoModificationAllowed, + NotFound, + NotSupported, + InuseAttribute, + InvalidState, + Syntax, + InvalidModification, + Namespace, + InvalidAccess, + Validation, + TypeMismatch, +}; + +const DOMException = c.dom_exception; + +fn DOMExceptionError(except: DOMException) DOMError { + return switch (except) { + c.DOM_INDEX_SIZE_ERR => DOMError.IndexSize, + c.DOM_DOMSTRING_SIZE_ERR => DOMError.StringSize, + c.DOM_HIERARCHY_REQUEST_ERR => DOMError.HierarchyRequest, + c.DOM_WRONG_DOCUMENT_ERR => DOMError.WrongDocument, + c.DOM_INVALID_CHARACTER_ERR => DOMError.InvalidCharacter, + c.DOM_NO_DATA_ALLOWED_ERR => DOMError.NoDataAllowed, + c.DOM_NO_MODIFICATION_ALLOWED_ERR => DOMError.NoModificationAllowed, + c.DOM_NOT_FOUND_ERR => DOMError.NotFound, + c.DOM_NOT_SUPPORTED_ERR => DOMError.NotSupported, + c.DOM_INUSE_ATTRIBUTE_ERR => DOMError.InuseAttribute, + c.DOM_INVALID_STATE_ERR => DOMError.InvalidState, + c.DOM_SYNTAX_ERR => DOMError.Syntax, + c.DOM_INVALID_MODIFICATION_ERR => DOMError.InvalidModification, + c.DOM_NAMESPACE_ERR => DOMError.Namespace, + c.DOM_INVALID_ACCESS_ERR => DOMError.InvalidAccess, + c.DOM_VALIDATION_ERR => DOMError.Validation, + c.DOM_TYPE_MISMATCH_ERR => DOMError.TypeMismatch, + else => DOMError.NoError, + }; +} + // EventTarget pub const EventTarget = c.dom_event_target; @@ -451,9 +499,10 @@ pub fn nodeSetTextContent(node: *Node, value: []const u8) void { _ = nodeVtable(node).dom_node_set_text_content.?(node, s); } -pub fn nodeAppendChild(node: *Node, child: *Node) *Node { +pub fn nodeAppendChild(node: *Node, child: *Node) DOMError!*Node { var res: ?*Node = undefined; - _ = nodeVtable(node).dom_node_append_child.?(node, child, &res); + const err = nodeVtable(node).dom_node_append_child.?(node, child, &res); + if (err != 0) return DOMExceptionError(err); return res.?; } diff --git a/src/run_tests.zig b/src/run_tests.zig index d65a520d..8acad71c 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -12,6 +12,7 @@ const nodeTestExecFn = @import("dom/node.zig").testExecFn; const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn; const textTestExecFn = @import("dom/text.zig").testExecFn; const HTMLCollectionTestExecFn = @import("dom/html_collection.zig").testExecFn; +const DOMExceptionTestExecFn = @import("dom/exceptions.zig").testExecFn; var doc: *parser.DocumentHTML = undefined; @@ -53,6 +54,7 @@ fn testsAllExecFn( characterDataTestExecFn, textTestExecFn, HTMLCollectionTestExecFn, + DOMExceptionTestExecFn, }; inline for (testFns) |testFn| { From 372c93d0f5ed18f1c729fdf1374bf5c2381787e5 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Fri, 24 Nov 2023 18:26:03 +0100 Subject: [PATCH 2/4] DOMException: implements reviewer comments Co-authored-by: Pierre Tachoire Signed-off-by: Francis Bouvier --- src/dom/exceptions.zig | 1 + src/netsurf.zig | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dom/exceptions.zig b/src/dom/exceptions.zig index 945f7d9d..1c6326c0 100644 --- a/src/dom/exceptions.zig +++ b/src/dom/exceptions.zig @@ -7,6 +7,7 @@ const checkCases = jsruntime.test_utils.checkCases; const parser = @import("../netsurf.zig"); +// https://webidl.spec.whatwg.org/#idl-DOMException pub const DOMException = struct { err: parser.DOMError, str: []const u8, diff --git a/src/netsurf.zig b/src/netsurf.zig index 5aa69f72..f0fc26ee 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -299,8 +299,9 @@ pub const DOMError = error{ const DOMException = c.dom_exception; -fn DOMExceptionError(except: DOMException) DOMError { +fn DOMErr(except: DOMException) DOMError!void { return switch (except) { + c.DOM_NO_ERR => return, c.DOM_INDEX_SIZE_ERR => DOMError.IndexSize, c.DOM_DOMSTRING_SIZE_ERR => DOMError.StringSize, c.DOM_HIERARCHY_REQUEST_ERR => DOMError.HierarchyRequest, @@ -318,7 +319,7 @@ fn DOMExceptionError(except: DOMException) DOMError { c.DOM_INVALID_ACCESS_ERR => DOMError.InvalidAccess, c.DOM_VALIDATION_ERR => DOMError.Validation, c.DOM_TYPE_MISMATCH_ERR => DOMError.TypeMismatch, - else => DOMError.NoError, + else => unreachable, }; } @@ -502,7 +503,7 @@ pub fn nodeSetTextContent(node: *Node, value: []const u8) void { pub fn nodeAppendChild(node: *Node, child: *Node) DOMError!*Node { var res: ?*Node = undefined; const err = nodeVtable(node).dom_node_append_child.?(node, child, &res); - if (err != 0) return DOMExceptionError(err); + try DOMErr(err); return res.?; } From 86489f3635c95a650ecfc527756a2d5b1f71b754 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Mon, 27 Nov 2023 16:20:31 +0100 Subject: [PATCH 3/4] dom_exception: add errors not handled by netsurf Signed-off-by: Francis Bouvier --- src/dom/exceptions.zig | 24 ++++++++++++++++++++++++ src/netsurf.zig | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/src/dom/exceptions.zig b/src/dom/exceptions.zig index 1c6326c0..dfb3fdcf 100644 --- a/src/dom/exceptions.zig +++ b/src/dom/exceptions.zig @@ -34,6 +34,14 @@ pub const DOMException = struct { pub const _INVALID_ACCESS_ERR = 15; pub const _VALIDATION_ERR = 16; pub const _TYPE_MISMATCH_ERR = 17; + pub const _SECURITY_ERR = 18; + pub const _NETWORK_ERR = 19; + pub const _ABORT_ERR = 20; + pub const _URL_MISMATCH_ERR = 21; + pub const _QUOTA_EXCEEDED_ERR = 22; + pub const _TIMEOUT_ERR = 23; + pub const _INVALID_NODE_TYPE_ERR = 24; + pub const _DATA_CLONE_ERR = 25; // TODO: deinit pub fn init(alloc: std.mem.Allocator, err: parser.DOMError, callerName: []const u8) anyerror!DOMException { @@ -69,6 +77,14 @@ pub const DOMException = struct { error.InvalidAccess => "InvalidAccessError", error.Validation => "ValidationError", error.TypeMismatch => "TypeMismatchError", + error.Security => "SecurityError", + error.Network => "NetworkError", + error.Abort => "AbortError", + error.URLismatch => "URLismatchError", + error.QuotaExceeded => "QuotaExceededError", + error.Timeout => "TimeoutError", + error.InvalidNodeType => "InvalidNodeTypeError", + error.DataClone => "DataCloneError", error.NoError => unreachable, }; } @@ -94,6 +110,14 @@ pub const DOMException = struct { error.InvalidAccess => 15, error.Validation => 16, error.TypeMismatch => 17, + error.Security => 18, + error.Network => 19, + error.Abort => 20, + error.URLismatch => 21, + error.QuotaExceeded => 22, + error.Timeout => 23, + error.InvalidNodeType => 24, + error.DataClone => 25, error.NoError => unreachable, }; } diff --git a/src/netsurf.zig b/src/netsurf.zig index f0fc26ee..dbb3e61e 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -295,6 +295,14 @@ pub const DOMError = error{ InvalidAccess, Validation, TypeMismatch, + Security, + Network, + Abort, + URLismatch, + QuotaExceeded, + Timeout, + InvalidNodeType, + DataClone, }; const DOMException = c.dom_exception; From 0ba0ae5b4e2e57db61e415bfa40a6fde5f866295 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Mon, 27 Nov 2023 16:44:26 +0100 Subject: [PATCH 4/4] dom_exception: adapt to interface definition change Signed-off-by: Francis Bouvier --- src/dom/exceptions.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dom/exceptions.zig b/src/dom/exceptions.zig index dfb3fdcf..32792d86 100644 --- a/src/dom/exceptions.zig +++ b/src/dom/exceptions.zig @@ -44,9 +44,10 @@ pub const DOMException = struct { pub const _DATA_CLONE_ERR = 25; // TODO: deinit - pub fn init(alloc: std.mem.Allocator, err: parser.DOMError, callerName: []const u8) anyerror!DOMException { - const errName = DOMException.name(err); - const str = switch (err) { + pub fn init(alloc: std.mem.Allocator, err: anyerror, callerName: []const u8) anyerror!DOMException { + const errCast = @as(parser.DOMError, @errSetCast(err)); + const errName = DOMException.name(errCast); + const str = switch (errCast) { error.HierarchyRequest => try allocPrint( alloc, "{s}: Failed to execute '{s}' on 'Node': The new child element contains the parent.", @@ -55,7 +56,7 @@ pub const DOMException = struct { error.NoError => unreachable, else => "", // TODO: implement other messages }; - return .{ .err = err, .str = str }; + return .{ .err = errCast, .str = str }; } fn name(err: parser.DOMError) []const u8 {