From 152014fe16bfe1484227998d3d41b0519276e2d0 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 3 Jan 2024 14:20:42 +0100 Subject: [PATCH] dom: implement Attr interface --- src/dom/attribute.zig | 73 +++++++++++++++++++++++++++++++++++++++++++ src/dom/element.zig | 4 +++ src/netsurf.zig | 47 ++++++++++++++++++++++++++++ src/run_tests.zig | 2 ++ 4 files changed, 126 insertions(+) diff --git a/src/dom/attribute.zig b/src/dom/attribute.zig index 758a71fb..292d58c9 100644 --- a/src/dom/attribute.zig +++ b/src/dom/attribute.zig @@ -1,5 +1,9 @@ const std = @import("std"); +const jsruntime = @import("jsruntime"); +const Case = jsruntime.test_utils.Case; +const checkCases = jsruntime.test_utils.checkCases; + const parser = @import("../netsurf.zig"); const Node = @import("node.zig").Node; @@ -10,4 +14,73 @@ pub const Attr = struct { pub const Self = parser.Attribute; pub const prototype = *Node; pub const mem_guarantied = true; + + pub fn get_namespaceURI(self: *parser.Attribute) !?[]const u8 { + return try parser.nodeGetNamespace(parser.attributeToNode(self)); + } + + pub fn get_prefix(self: *parser.Attribute) !?[]const u8 { + return try parser.nodeGetPrefix(parser.attributeToNode(self)); + } + + pub fn get_localName(self: *parser.Attribute) ![]const u8 { + return try parser.nodeLocalName(parser.attributeToNode(self)); + } + + pub fn get_name(self: *parser.Attribute) ![]const u8 { + return try parser.attributeGetName(self); + } + + pub fn get_value(self: *parser.Attribute) !?[]const u8 { + return try parser.attributeGetValue(self); + } + + pub fn set_value(self: *parser.Attribute, v: []const u8) !?[]const u8 { + try parser.attributeSetValue(self, v); + return v; + } + + pub fn get_ownerElement(self: *parser.Attribute) !?*parser.Element { + return try parser.attributeGetOwnerElement(self); + } + + pub fn get_specified(_: *parser.Attribute) bool { + return true; + } }; + +// Tests +// ----- + +pub fn testExecFn( + _: std.mem.Allocator, + js_env: *jsruntime.Env, + comptime _: []jsruntime.API, +) !void { + var getters = [_]Case{ + .{ .src = "let a = document.createAttributeNS('foo', 'bar')", .ex = "undefined" }, + .{ .src = "a.namespaceURI", .ex = "foo" }, + .{ .src = "a.prefix", .ex = "null" }, + .{ .src = "a.localName", .ex = "bar" }, + .{ .src = "a.name", .ex = "bar" }, + .{ .src = "a.value", .ex = "" }, + // TODO: libdom has a bug here: the created attr has no parent, it + // causes a panic w/ libdom when setting the value. + //.{ .src = "a.value = 'nok'", .ex = "nok" }, + .{ .src = "a.ownerElement", .ex = "null" }, + }; + try checkCases(js_env, &getters); + + var attr = [_]Case{ + .{ .src = "let b = document.getElementById('link').getAttributeNode('class')", .ex = "undefined" }, + .{ .src = "b.name", .ex = "class" }, + .{ .src = "b.value", .ex = "ok" }, + .{ .src = "b.value = 'nok'", .ex = "nok" }, + .{ .src = "b.value", .ex = "nok" }, + .{ .src = "b.value = null", .ex = "null" }, + .{ .src = "b.value", .ex = "null" }, + .{ .src = "b.value = 'ok'", .ex = "ok" }, + .{ .src = "b.ownerElement.id", .ex = "link" }, + }; + try checkCases(js_env, &attr); +} diff --git a/src/dom/element.zig b/src/dom/element.zig index 5827604a..f8acfbe9 100644 --- a/src/dom/element.zig +++ b/src/dom/element.zig @@ -127,6 +127,10 @@ pub const Element = struct { return true; } + pub fn _getAttributeNode(self: *parser.Element, name: []const u8) !?*parser.Attribute { + return try parser.elementGetAttributeNode(self, name); + } + pub fn _getElementsByTagName( self: *parser.Element, alloc: std.mem.Allocator, diff --git a/src/netsurf.zig b/src/netsurf.zig index 2455b35a..9fd658b6 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -840,6 +840,46 @@ pub const ProcessingInstruction = c.dom_processing_instruction; // Attribute pub const Attribute = c.dom_attr; +fn attributeVtable(a: *Attribute) c.dom_attr_vtable { + return getVtable(c.dom_attr_vtable, Attribute, a); +} + +pub fn attributeGetName(a: *Attribute) ![]const u8 { + var s: ?*String = undefined; + const err = attributeVtable(a).dom_attr_get_name.?(a, &s); + try DOMErr(err); + + return strToData(s.?); +} + +pub fn attributeGetValue(a: *Attribute) !?[]const u8 { + var s: ?*String = undefined; + const err = attributeVtable(a).dom_attr_get_value.?(a, &s); + try DOMErr(err); + if (s == null) return null; + + return strToData(s.?); +} + +pub fn attributeSetValue(a: *Attribute, v: []const u8) !void { + const err = attributeVtable(a).dom_attr_set_value.?(a, try strFromData(v)); + try DOMErr(err); +} + +pub fn attributeGetOwnerElement(a: *Attribute) !?*Element { + var elt: ?*Element = undefined; + const err = attributeVtable(a).dom_attr_get_owner_element.?(a, &elt); + try DOMErr(err); + if (elt == null) return null; + + return elt.?; +} + +// attributeToNode is an helper to convert an attribute to a node. +pub inline fn attributeToNode(a: *Attribute) *Node { + return @as(*Node, @ptrCast(a)); +} + // Element pub const Element = c.dom_element; @@ -877,6 +917,13 @@ pub fn elementHasAttribute(elem: *Element, qname: []const u8) !bool { return res; } +pub fn elementGetAttributeNode(elem: *Element, name: []const u8) !?*Attribute { + var a: ?*Attribute = undefined; + const err = elementVtable(elem).dom_element_get_attribute_node.?(elem, try strFromData(name), &a); + try DOMErr(err); + return a; +} + pub fn elementHasClass(elem: *Element, class: []const u8) !bool { var res: bool = undefined; const err = elementVtable(elem).dom_element_has_class.?( diff --git a/src/run_tests.zig b/src/run_tests.zig index 0f541c79..44aa432a 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -18,6 +18,7 @@ const DOMImplementationExecFn = @import("dom/implementation.zig").testExecFn; const NamedNodeMapExecFn = @import("dom/namednodemap.zig").testExecFn; const DOMTokenListExecFn = @import("dom/token_list.zig").testExecFn; const NodeListTestExecFn = @import("dom/nodelist.zig").testExecFn; +const AttrTestExecFn = @import("dom/attribute.zig").testExecFn; var doc: *parser.DocumentHTML = undefined; @@ -67,6 +68,7 @@ fn testsAllExecFn( NamedNodeMapExecFn, DOMTokenListExecFn, NodeListTestExecFn, + AttrTestExecFn, }; inline for (testFns) |testFn| {