dom: add NamedNodeMap implementation

and create Attr type
This commit is contained in:
Pierre Tachoire
2023-12-02 10:54:12 +01:00
parent dcb095d9df
commit ad5c6236a7
7 changed files with 244 additions and 0 deletions

15
src/dom/attribute.zig Normal file
View File

@@ -0,0 +1,15 @@
const std = @import("std");
const parser = @import("../netsurf.zig");
const Node = @import("node.zig").Node;
const DOMException = @import("exceptions.zig").DOMException;
// WEB IDL https://dom.spec.whatwg.org/#attr
pub const Attr = struct {
pub const Self = parser.Attribute;
pub const prototype = *Node;
pub const mem_guarantied = true;
pub const Exception = DOMException;
};

View File

@@ -3,12 +3,14 @@ const generate = @import("../generate.zig");
const DOMException = @import("exceptions.zig").DOMException;
const EventTarget = @import("event_target.zig").EventTarget;
const DOMImplementation = @import("implementation.zig").DOMImplementation;
const NamedNodeMap = @import("namednodemap.zig").NamedNodeMap;
const Nod = @import("node.zig");
pub const Interfaces = generate.Tuple(.{
DOMException,
EventTarget,
DOMImplementation,
NamedNodeMap,
Nod.Node,
Nod.Interfaces,
});

View File

@@ -10,12 +10,16 @@ const Node = @import("node.zig").Node;
const HTMLElem = @import("../html/elements.zig");
pub const Union = @import("../html/elements.zig").Union;
const DOMException = @import("exceptions.zig").DOMException;
// WEB IDL https://dom.spec.whatwg.org/#element
pub const Element = struct {
pub const Self = parser.Element;
pub const prototype = *Node;
pub const mem_guarantied = true;
pub const Exception = DOMException;
pub fn toInterface(e: *parser.Element) !Union {
return try HTMLElem.toInterface(Union, e);
}
@@ -27,6 +31,10 @@ pub const Element = struct {
return try parser.elementLocalName(self);
}
pub fn get_attributes(self: *parser.Element) !*parser.NamedNodeMap {
return try parser.nodeGetAttributes(parser.elementToNode(self));
}
pub fn _hasAttributes(self: *parser.Element) !bool {
return try parser.nodeHasAttributes(parser.elementToNode(self));
}
@@ -89,6 +97,8 @@ pub fn testExecFn(
var attribute = [_]Case{
.{ .src = "let a = document.getElementById('content')", .ex = "undefined" },
.{ .src = "a.hasAttributes()", .ex = "true" },
.{ .src = "a.attributes.length", .ex = "1" },
.{ .src = "a.getAttribute('id')", .ex = "content" },
.{ .src = "a.hasAttribute('foo')", .ex = "false" },

80
src/dom/namednodemap.zig Normal file
View File

@@ -0,0 +1,80 @@
const std = @import("std");
const parser = @import("../netsurf.zig");
const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases;
const DOMException = @import("exceptions.zig").DOMException;
// WEB IDL https://dom.spec.whatwg.org/#namednodemap
pub const NamedNodeMap = struct {
pub const Self = parser.NamedNodeMap;
pub const mem_guarantied = true;
pub const Exception = DOMException;
// TODO implement LegacyUnenumerableNamedProperties.
// https://webidl.spec.whatwg.org/#LegacyUnenumerableNamedProperties
pub fn get_length(self: *parser.NamedNodeMap) !u32 {
return try parser.namedNodeMapGetLength(self);
}
pub fn _item(self: *parser.NamedNodeMap, index: u32) !?*parser.Attribute {
return try parser.namedNodeMapItem(self, index);
}
pub fn _getNamedItem(self: *parser.NamedNodeMap, qname: []const u8) !?*parser.Attribute {
return try parser.namedNodeMapGetNamedItem(self, qname);
}
pub fn _getNamedItemNS(
self: *parser.NamedNodeMap,
namespace: []const u8,
localname: []const u8,
) !?*parser.Attribute {
return try parser.namedNodeMapGetNamedItemNS(self, namespace, localname);
}
pub fn _setNamedItem(self: *parser.NamedNodeMap, attr: *parser.Attribute) !?*parser.Attribute {
return try parser.namedNodeMapSetNamedItem(self, attr);
}
pub fn _setNamedItemNS(self: *parser.NamedNodeMap, attr: *parser.Attribute) !?*parser.Attribute {
return try parser.namedNodeMapSetNamedItemNS(self, attr);
}
pub fn _removeNamedItem(self: *parser.NamedNodeMap, qname: []const u8) !*parser.Attribute {
return try parser.namedNodeMapRemoveNamedItem(self, qname);
}
pub fn _removeNamedItemNS(
self: *parser.NamedNodeMap,
namespace: []const u8,
localname: []const u8,
) !*parser.Attribute {
return try parser.namedNodeMapRemoveNamedItemNS(self, namespace, localname);
}
};
// Tests
// -----
pub fn testExecFn(
_: std.mem.Allocator,
js_env: *jsruntime.Env,
comptime _: []jsruntime.API,
) !void {
var setItem = [_]Case{
.{ .src = "let a = document.getElementById('content').attributes", .ex = "undefined" },
.{ .src = "a.length", .ex = "1" },
.{ .src = "a.item(0)", .ex = "[object Attr]" },
.{ .src = "a.item(1)", .ex = "null" },
.{ .src = "a.getNamedItem('id')", .ex = "[object Attr]" },
.{ .src = "a.getNamedItem('foo')", .ex = "null" },
.{ .src = "a.setNamedItem(a.getNamedItem('id'))", .ex = "[object Attr]" },
};
try checkCases(js_env, &setItem);
}

View File

@@ -11,6 +11,7 @@ const parser = @import("../netsurf.zig");
const EventTarget = @import("event_target.zig").EventTarget;
// DOM
const Attr = @import("attribute.zig").Attr;
const CData = @import("character_data.zig");
const Element = @import("element.zig").Element;
const Document = @import("document.zig").Document;
@@ -23,6 +24,7 @@ const HTMLElem = @import("../html/elements.zig");
// Node interfaces
pub const Interfaces = generate.Tuple(.{
Attr,
CData.CharacterData,
CData.Interfaces,
Element,
@@ -52,6 +54,7 @@ pub const Node = struct {
.text => .{ .Text = @as(*parser.Text, @ptrCast(node)) },
.document => .{ .HTMLDocument = @as(*parser.DocumentHTML, @ptrCast(node)) },
.document_type => .{ .DocumentType = @as(*parser.DocumentType, @ptrCast(node)) },
.attribute => .{ .Attr = @as(*parser.Attribute, @ptrCast(node)) },
else => @panic("node type not handled"), // TODO
};
}

View File

@@ -336,6 +336,131 @@ fn DOMErr(except: DOMException) DOMError!void {
// EventTarget
pub const EventTarget = c.dom_event_target;
// NamedNodeMap
pub const NamedNodeMap = c.dom_namednodemap;
pub fn namedNodeMapGetLength(nnm: *NamedNodeMap) !u32 {
var ln: u32 = undefined;
const err = c.dom_namednodemap_get_length(nnm, &ln);
try DOMErr(err);
return ln;
}
pub fn namedNodeMapItem(nnm: *NamedNodeMap, index: u32) !?*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_item(nnm, index, &n);
try DOMErr(err);
if (n == null) {
return null;
}
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
pub fn namedNodeMapGetNamedItem(nnm: *NamedNodeMap, qname: []const u8) !?*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_get_named_item(nnm, try stringFromData(qname), &n);
try DOMErr(err);
if (n == null) {
return null;
}
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
pub fn namedNodeMapGetNamedItemNS(
nnm: *NamedNodeMap,
namespace: []const u8,
localname: []const u8,
) !?*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_get_named_item_ns(
nnm,
try stringFromData(namespace),
try stringFromData(localname),
&n,
);
try DOMErr(err);
if (n == null) {
return null;
}
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
// Convert a parser pointer to a public dom_node pointer.
fn toDOMNode(comptime T: type, v: *T) [*c]c.dom_node {
const v_aligned: *align(@alignOf([*c]c.dom_node)) T = @alignCast(v);
return @ptrCast(v_aligned);
}
pub fn namedNodeMapSetNamedItem(nnm: *NamedNodeMap, attr: *Attribute) !?*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_set_named_item(
nnm,
toDOMNode(Attribute, attr),
&n,
);
try DOMErr(err);
if (n == null) {
return null;
}
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
pub fn namedNodeMapSetNamedItemNS(nnm: *NamedNodeMap, attr: *Attribute) !?*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_set_named_item_ns(
nnm,
toDOMNode(Attribute, attr),
&n,
);
try DOMErr(err);
if (n == null) {
return null;
}
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
pub fn namedNodeMapRemoveNamedItem(nnm: *NamedNodeMap, qname: []const u8) !*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_remove_named_item(nnm, try stringFromData(qname), &n);
try DOMErr(err);
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
pub fn namedNodeMapRemoveNamedItemNS(
nnm: *NamedNodeMap,
namespace: []const u8,
localname: []const u8,
) !*Attribute {
var n: [*c]c.dom_node = undefined;
const err = c._dom_namednodemap_remove_named_item_ns(
nnm,
try stringFromData(namespace),
try stringFromData(localname),
&n,
);
try DOMErr(err);
// cast [*c]c.dom_node into *Attribute
return @as(*Attribute, @ptrCast(n));
}
// NodeType
pub const NodeType = enum(u4) {
@@ -637,6 +762,13 @@ pub fn nodeHasAttributes(node: *Node) !bool {
return res;
}
pub fn nodeGetAttributes(node: *Node) !*NamedNodeMap {
var res: ?*NamedNodeMap = undefined;
const err = nodeVtable(node).dom_node_get_attributes.?(node, &res);
try DOMErr(err);
return res.?;
}
// nodeToElement is an helper to convert a node to an element.
pub inline fn nodeToElement(node: *Node) *Element {
return @as(*Element, @ptrCast(node));

View File

@@ -15,6 +15,7 @@ const elementTestExecFn = @import("dom/element.zig").testExecFn;
const HTMLCollectionTestExecFn = @import("dom/html_collection.zig").testExecFn;
const DOMExceptionTestExecFn = @import("dom/exceptions.zig").testExecFn;
const DOMImplementationExecFn = @import("dom/implementation.zig").testExecFn;
const NamedNodeMapExecFn = @import("dom/namednodemap.zig").testExecFn;
var doc: *parser.DocumentHTML = undefined;
@@ -61,6 +62,7 @@ fn testsAllExecFn(
HTMLCollectionTestExecFn,
DOMExceptionTestExecFn,
DOMImplementationExecFn,
NamedNodeMapExecFn,
};
inline for (testFns) |testFn| {