diff --git a/src/dom/element.zig b/src/dom/element.zig index c5f9a645..0410ad1e 100644 --- a/src/dom/element.zig +++ b/src/dom/element.zig @@ -2,6 +2,10 @@ 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 Node = @import("node.zig").Node; const HTMLElem = @import("../html/elements.zig"); pub const Union = @import("../html/elements.zig").Union; @@ -22,4 +26,50 @@ pub const Element = struct { pub fn get_localName(self: *parser.Element) ![]const u8 { return try parser.elementLocalName(self); } + + pub fn _getAttribute(self: *parser.Element, qname: []const u8) !?[]const u8 { + return try parser.elementGetAttribute(self, qname); + } + + pub fn _setAttribute(self: *parser.Element, qname: []const u8, value: []const u8) !void { + return try parser.elementSetAttribute(self, qname, value); + } + + pub fn _removeAttribute(self: *parser.Element, qname: []const u8) !void { + return try parser.elementRemoveAttribute(self, qname); + } + + pub fn _hasAttribute(self: *parser.Element, qname: []const u8) !bool { + return try parser.elementHasAttribute(self, qname); + } }; + +// Tests +// ----- + +pub fn testExecFn( + _: std.mem.Allocator, + js_env: *jsruntime.Env, + comptime _: []jsruntime.API, +) !void { + var attribute = [_]Case{ + .{ .src = "let div = document.getElementById('content')", .ex = "undefined" }, + .{ .src = "div.getAttribute('id')", .ex = "content" }, + + .{ .src = "div.hasAttribute('foo')", .ex = "false" }, + .{ .src = "div.getAttribute('foo')", .ex = "null" }, + + .{ .src = "div.setAttribute('foo', 'bar')", .ex = "undefined" }, + .{ .src = "div.hasAttribute('foo')", .ex = "true" }, + .{ .src = "div.getAttribute('foo')", .ex = "bar" }, + + .{ .src = "div.setAttribute('foo', 'baz')", .ex = "undefined" }, + .{ .src = "div.hasAttribute('foo')", .ex = "true" }, + .{ .src = "div.getAttribute('foo')", .ex = "baz" }, + + .{ .src = "div.removeAttribute('foo')", .ex = "undefined" }, + .{ .src = "div.hasAttribute('foo')", .ex = "false" }, + .{ .src = "div.getAttribute('foo')", .ex = "null" }, + }; + try checkCases(js_env, &attribute); +} diff --git a/src/netsurf.zig b/src/netsurf.zig index a7d0a22f..1c857ad6 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -749,6 +749,34 @@ pub fn elementGetAttribute(elem: *Element, name: []const u8) !?[]const u8 { return stringToData(s.?); } +pub fn elementSetAttribute(elem: *Element, qname: []const u8, value: []const u8) !void { + const err = elementVtable(elem).dom_element_set_attribute.?( + elem, + try stringFromData(qname), + try stringFromData(value), + ); + try DOMErr(err); +} + +pub fn elementRemoveAttribute(elem: *Element, qname: []const u8) !void { + const err = elementVtable(elem).dom_element_remove_attribute.?( + elem, + try stringFromData(qname), + ); + try DOMErr(err); +} + +pub fn elementHasAttribute(elem: *Element, qname: []const u8) !bool { + var res: bool = undefined; + const err = elementVtable(elem).dom_element_has_attribute.?( + elem, + try stringFromData(qname), + &res, + ); + try DOMErr(err); + return res; +} + 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 9edab848..e478b84f 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -11,6 +11,7 @@ const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn; const nodeTestExecFn = @import("dom/node.zig").testExecFn; const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn; const textTestExecFn = @import("dom/text.zig").testExecFn; +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; @@ -56,6 +57,7 @@ fn testsAllExecFn( nodeTestExecFn, characterDataTestExecFn, textTestExecFn, + elementTestExecFn, HTMLCollectionTestExecFn, DOMExceptionTestExecFn, DOMImplementationExecFn, diff --git a/tests/wpt/dom/nodes/Element-hasAttribute.html b/tests/wpt/dom/nodes/Element-hasAttribute.html new file mode 100644 index 00000000..26528d75 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-hasAttribute.html @@ -0,0 +1,32 @@ + + +Element.prototype.hasAttribute + + + + + + + diff --git a/tests/wpt/dom/nodes/Element-hasAttributes.html b/tests/wpt/dom/nodes/Element-hasAttributes.html new file mode 100644 index 00000000..fbb9c233 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-hasAttributes.html @@ -0,0 +1,40 @@ + + + + + + + + +
+

+ + + diff --git a/tests/wpt/dom/nodes/Element-removeAttribute.html b/tests/wpt/dom/nodes/Element-removeAttribute.html new file mode 100644 index 00000000..df79e62c --- /dev/null +++ b/tests/wpt/dom/nodes/Element-removeAttribute.html @@ -0,0 +1,58 @@ + + +Element.prototype.removeAttribute + + + + + diff --git a/tests/wpt/dom/nodes/Element-removeAttributeNS.html b/tests/wpt/dom/nodes/Element-removeAttributeNS.html new file mode 100644 index 00000000..a2773e6f --- /dev/null +++ b/tests/wpt/dom/nodes/Element-removeAttributeNS.html @@ -0,0 +1,18 @@ + +Element.removeAttributeNS + + + +
+ diff --git a/tests/wpt/dom/nodes/Element-setAttribute-crbug-1138487.html b/tests/wpt/dom/nodes/Element-setAttribute-crbug-1138487.html new file mode 100644 index 00000000..9aa9ed81 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-setAttribute-crbug-1138487.html @@ -0,0 +1,22 @@ + + + + diff --git a/tests/wpt/dom/nodes/Element-setAttribute.html b/tests/wpt/dom/nodes/Element-setAttribute.html new file mode 100644 index 00000000..76094068 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-setAttribute.html @@ -0,0 +1,38 @@ + + +Element.prototype.setAttribute + + + + + diff --git a/tests/wpt/dom/nodes/attributes.html b/tests/wpt/dom/nodes/attributes.html new file mode 100644 index 00000000..c6db7eb8 --- /dev/null +++ b/tests/wpt/dom/nodes/attributes.html @@ -0,0 +1,858 @@ + + +Attributes tests + + + + + + + +
+ + + + + + + + diff --git a/tests/wpt/dom/nodes/attributes.js b/tests/wpt/dom/nodes/attributes.js new file mode 100644 index 00000000..ef32bf6a --- /dev/null +++ b/tests/wpt/dom/nodes/attributes.js @@ -0,0 +1,18 @@ +function attr_is(attr, v, ln, ns, p, n) { + assert_equals(attr.value, v) + assert_equals(attr.nodeValue, v) + assert_equals(attr.textContent, v) + assert_equals(attr.localName, ln) + assert_equals(attr.namespaceURI, ns) + assert_equals(attr.prefix, p) + assert_equals(attr.name, n) + assert_equals(attr.nodeName, n); + assert_equals(attr.specified, true) +} + +function attributes_are(el, l) { + for (var i = 0, il = l.length; i < il; i++) { + attr_is(el.attributes[i], l[i][1], l[i][0], (l[i].length < 3) ? null : l[i][2], null, l[i][0]) + assert_equals(el.attributes[i].ownerElement, el) + } +} diff --git a/tests/wpt/dom/nodes/productions.js b/tests/wpt/dom/nodes/productions.js new file mode 100644 index 00000000..218797fc --- /dev/null +++ b/tests/wpt/dom/nodes/productions.js @@ -0,0 +1,3 @@ +var invalid_names = ["", "invalid^Name", "\\", "'", '"', "0", "0:a"] // XXX +var valid_names = ["x", "X", ":", "a:0"] +var invalid_qnames = [":a", "b:", "x:y:z"] // XXX