diff --git a/src/dom/element.zig b/src/dom/element.zig
index 792bbcb9..27d4870f 100644
--- a/src/dom/element.zig
+++ b/src/dom/element.zig
@@ -89,6 +89,28 @@ pub const Element = struct {
return buf.toOwnedSlice();
}
+ pub fn set_innerHTML(self: *parser.Element, str: []const u8) !void {
+ const node = parser.elementToNode(self);
+ const doc = try parser.nodeOwnerDocument(node) orelse return parser.DOMError.WrongDocument;
+ // parse the fragment
+ const fragment = try parser.documentParseFragmentFromStr(doc, str);
+
+ // remove existing children
+ try Node.removeChildren(node);
+
+ // get fragment body children
+ const children = try parser.documentFragmentBodyChildren(fragment) orelse return;
+
+ // append children to the node
+ const ln = try parser.nodeListLength(children);
+ var i: u32 = 0;
+ while (i < ln) {
+ defer i += 1;
+ const child = try parser.nodeListItem(children, i) orelse continue;
+ _ = try parser.nodeAppendChild(node, child);
+ }
+ }
+
pub fn _hasAttributes(self: *parser.Element) !bool {
return try parser.nodeHasAttributes(parser.elementToNode(self));
}
@@ -435,6 +457,11 @@ pub fn testExecFn(
var innerHTML = [_]Case{
.{ .src = "document.getElementById('para').innerHTML", .ex = " And" },
.{ .src = "document.getElementById('para-empty').innerHTML.trim()", .ex = "" },
+
+ .{ .src = "let h = document.getElementById('para-empty')", .ex = "undefined" },
+ .{ .src = "const prev = h.innerHTML", .ex = "undefined" },
+ .{ .src = "h.innerHTML = prev; true", .ex = "true" },
+ .{ .src = "document.getElementById('para-empty').innerHTML.trim()", .ex = "" },
};
try checkCases(js_env, &innerHTML);
}
diff --git a/src/netsurf.zig b/src/netsurf.zig
index 442a0fb7..a3ef1a8f 100644
--- a/src/netsurf.zig
+++ b/src/netsurf.zig
@@ -1514,6 +1514,22 @@ pub const Video = struct { base: *c.dom_html_element };
// Document Fragment
pub const DocumentFragment = c.dom_document_fragment;
+pub inline fn documentFragmentToNode(doc: *DocumentFragment) *Node {
+ return @as(*Node, @ptrCast(doc));
+}
+
+pub fn documentFragmentBodyChildren(doc: *DocumentFragment) !?*NodeList {
+ const node = documentFragmentToNode(doc);
+ const html = try nodeFirstChild(node) orelse return null;
+ // TODO unref
+ const head = try nodeFirstChild(html) orelse return null;
+ // TODO unref
+ const body = try nodeNextSibling(head) orelse return null;
+ // TODO unref
+
+ return try nodeGetChildNodes(body);
+}
+
// Document Position
pub const DocumentPosition = enum(u2) {
@@ -1859,6 +1875,53 @@ pub fn documentHTMLParse(reader: anytype, enc: ?[:0]const u8) !*DocumentHTML {
return @as(*DocumentHTML, @ptrCast(doc.?));
}
+pub fn documentParseFragmentFromStr(self: *Document, str: []const u8) !*DocumentFragment {
+ var fbs = std.io.fixedBufferStream(str);
+ return try documentParseFragment(self, fbs.reader(), "UTF-8");
+}
+
+pub fn documentParseFragment(self: *Document, reader: anytype, enc: ?[:0]const u8) !*DocumentFragment {
+ var parser: ?*c.dom_hubbub_parser = undefined;
+ var fragment: ?*c.dom_document_fragment = undefined;
+ var err: c.hubbub_error = undefined;
+
+ var params = c.dom_hubbub_parser_params{
+ .enc = null,
+ .fix_enc = true,
+ .msg = null,
+ .script = null,
+ .enable_script = false,
+ .ctx = null,
+ .daf = null,
+ };
+
+ if (enc) |e| params.enc = e;
+
+ err = c.dom_hubbub_fragment_parser_create(¶ms, self, &parser, &fragment);
+ try parserErr(err);
+ defer c.dom_hubbub_parser_destroy(parser);
+
+ var buffer: [1024]u8 = undefined;
+ var ln = buffer.len;
+ while (ln > 0) {
+ ln = try reader.read(&buffer);
+ err = c.dom_hubbub_parser_parse_chunk(parser, &buffer, ln);
+ // TODO handle encoding change error return.
+ // When the HTML contains a META tag with a different encoding than the
+ // original one, a c.DOM_HUBBUB_HUBBUB_ERR_ENCODINGCHANGE error is
+ // returned.
+ // In this case, we must restart the parsing with the new detected
+ // encoding. The detected encoding is stored in the document and we can
+ // get it with documentGetInputEncoding().
+ try parserErr(err);
+ }
+
+ err = c.dom_hubbub_parser_completed(parser);
+ try parserErr(err);
+
+ return @as(*DocumentFragment, @ptrCast(fragment.?));
+}
+
// documentHTMLClose closes the document.
pub fn documentHTMLClose(doc: *DocumentHTML) !void {
const err = documentHTMLVtable(doc).close.?(doc);