Skip to content

Commit e82240a

Browse files
committed
Setting innerHTML now captures head elements
I couldn't find where the behavior is described. AND, browsers seem to behave differently depending on the state of the page (blank document vs actual page). Still, some sites use innerHTML to load <script> tags, and, in libdom at least, these are created in the implicit head. We cannot just copy the body nodes. To keep it simple, I now copy all head and body elements.
1 parent 72083c8 commit e82240a

File tree

2 files changed

+39
-22
lines changed

2 files changed

+39
-22
lines changed

src/browser/dom/element.zig

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,40 @@ pub const Element = struct {
127127
// remove existing children
128128
try Node.removeChildren(node);
129129

130-
// get fragment body children
131-
const children = try parser.documentFragmentBodyChildren(fragment) orelse return;
132-
133-
// append children to the node
134-
const ln = try parser.nodeListLength(children);
135-
for (0..ln) |_| {
136-
// always index 0, because ndoeAppendChild moves the node out of
137-
// the nodeList and into the new tree
138-
const child = try parser.nodeListItem(children, 0) orelse continue;
139-
_ = try parser.nodeAppendChild(node, child);
130+
// I'm not sure what the exact behavior is supposed to be. Initially,
131+
// we were only copying the body of the document fragment. But it seems
132+
// like head elements should be copied too. Specifically, some sites
133+
// create script tags via innerHTML, which we need to capture.
134+
// If you play with this in a browser, you should notice that the
135+
// behavior is different depending on whether you're in a blank page
136+
// or an actual document. In a blank page, something like:
137+
// x.innerHTML = '<script></script>';
138+
// does _not_ create an empty script, but in a real page, it does. Weird.
139+
const fragment_node = parser.documentFragmentToNode(fragment);
140+
const html = try parser.nodeFirstChild(fragment_node) orelse return;
141+
const head = try parser.nodeFirstChild(html) orelse return;
142+
{
143+
// First, copy some of the head element
144+
const children = try parser.nodeGetChildNodes(head);
145+
const ln = try parser.nodeListLength(children);
146+
for (0..ln) |_| {
147+
// always index 0, because nodeAppendChild moves the node out of
148+
// the nodeList and into the new tree
149+
const child = try parser.nodeListItem(children, 0) orelse continue;
150+
_ = try parser.nodeAppendChild(node, child);
151+
}
152+
}
153+
154+
{
155+
const body = try parser.nodeNextSibling(head) orelse return;
156+
const children = try parser.nodeGetChildNodes(body);
157+
const ln = try parser.nodeListLength(children);
158+
for (0..ln) |_| {
159+
// always index 0, because nodeAppendChild moves the node out of
160+
// the nodeList and into the new tree
161+
const child = try parser.nodeListItem(children, 0) orelse continue;
162+
_ = try parser.nodeAppendChild(node, child);
163+
}
140164
}
141165
}
142166

@@ -688,5 +712,10 @@ test "Browser.DOM.Element" {
688712

689713
try runner.testCases(&.{
690714
.{ "document.createElement('a').hasAttributes()", "false" },
715+
.{ "var fc; (fc = document.createElement('div')).innerHTML = '<script><\\/script>'", null },
716+
.{ "fc.outerHTML", "<div><script></script></div>" },
717+
718+
.{ "fc; (fc = document.createElement('div')).innerHTML = '<script><\\/script><p>hello</p>'", null },
719+
.{ "fc.outerHTML", "<div><script></script><p>hello</p></div>" },
691720
}, .{});
692721
}

src/browser/netsurf.zig

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,18 +1925,6 @@ pub inline fn documentFragmentToNode(doc: *DocumentFragment) *Node {
19251925
return @as(*Node, @alignCast(@ptrCast(doc)));
19261926
}
19271927

1928-
pub fn documentFragmentBodyChildren(doc: *DocumentFragment) !?*NodeList {
1929-
const node = documentFragmentToNode(doc);
1930-
const html = try nodeFirstChild(node) orelse return null;
1931-
// TODO unref
1932-
const head = try nodeFirstChild(html) orelse return null;
1933-
// TODO unref
1934-
const body = try nodeNextSibling(head) orelse return null;
1935-
// TODO unref
1936-
1937-
return try nodeGetChildNodes(body);
1938-
}
1939-
19401928
// Document Position
19411929

19421930
pub const DocumentPosition = enum(u32) {

0 commit comments

Comments
 (0)