dom: implement Attr interface

This commit is contained in:
Pierre Tachoire
2024-01-03 14:20:42 +01:00
parent 18901ea72b
commit 152014fe16
4 changed files with 126 additions and 0 deletions

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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.?(

View File

@@ -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| {