dom: implement innerHTML setter

This commit is contained in:
Pierre Tachoire
2024-02-19 18:56:48 +01:00
parent f7c0bcceae
commit c0e2377e16
2 changed files with 90 additions and 0 deletions

View File

@@ -89,6 +89,28 @@ pub const Element = struct {
return buf.toOwnedSlice(); 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 { pub fn _hasAttributes(self: *parser.Element) !bool {
return try parser.nodeHasAttributes(parser.elementToNode(self)); return try parser.nodeHasAttributes(parser.elementToNode(self));
} }
@@ -435,6 +457,11 @@ pub fn testExecFn(
var innerHTML = [_]Case{ var innerHTML = [_]Case{
.{ .src = "document.getElementById('para').innerHTML", .ex = " And" }, .{ .src = "document.getElementById('para').innerHTML", .ex = " And" },
.{ .src = "document.getElementById('para-empty').innerHTML.trim()", .ex = "<span id=\"para-empty-child\"></span>" }, .{ .src = "document.getElementById('para-empty').innerHTML.trim()", .ex = "<span id=\"para-empty-child\"></span>" },
.{ .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 = "<span id=\"para-empty-child\"></span>" },
}; };
try checkCases(js_env, &innerHTML); try checkCases(js_env, &innerHTML);
} }

View File

@@ -1514,6 +1514,22 @@ pub const Video = struct { base: *c.dom_html_element };
// Document Fragment // Document Fragment
pub const DocumentFragment = c.dom_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 // Document Position
pub const DocumentPosition = enum(u2) { pub const DocumentPosition = enum(u2) {
@@ -1859,6 +1875,53 @@ pub fn documentHTMLParse(reader: anytype, enc: ?[:0]const u8) !*DocumentHTML {
return @as(*DocumentHTML, @ptrCast(doc.?)); 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(&params, 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. // documentHTMLClose closes the document.
pub fn documentHTMLClose(doc: *DocumentHTML) !void { pub fn documentHTMLClose(doc: *DocumentHTML) !void {
const err = documentHTMLVtable(doc).close.?(doc); const err = documentHTMLVtable(doc).close.?(doc);