mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
LibWeb: Don't crash when appending to an XML document template element
When the XML parser appends child nodes to a template element, it must actually append the template element's contents. This special behavior caused us to return to the wrong parent element after adding child nodes to a template element, leading to a crash.
This commit is contained in:
parent
7cc718f1c1
commit
80ccb12a12
Notes:
github-actions[bot]
2025-07-14 07:16:52 +00:00
Author: https://github.com/tcl3
Commit: 80ccb12a12
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5429
Reviewed-by: https://github.com/gmta ✅
Reviewed-by: https://github.com/shannonbooth
4 changed files with 54 additions and 1 deletions
|
@ -53,6 +53,7 @@ ErrorOr<Variant<ByteString, Vector<XML::MarkupDeclaration>>> resolve_xml_resourc
|
||||||
|
|
||||||
XMLDocumentBuilder::XMLDocumentBuilder(DOM::Document& document, XMLScriptingSupport scripting_support)
|
XMLDocumentBuilder::XMLDocumentBuilder(DOM::Document& document, XMLScriptingSupport scripting_support)
|
||||||
: m_document(document)
|
: m_document(document)
|
||||||
|
, m_template_node_stack(document.realm().heap())
|
||||||
, m_current_node(m_document)
|
, m_current_node(m_document)
|
||||||
, m_scripting_support(scripting_support)
|
, m_scripting_support(scripting_support)
|
||||||
{
|
{
|
||||||
|
@ -148,6 +149,7 @@ void XMLDocumentBuilder::element_start(const XML::Name& name, HashMap<XML::Name,
|
||||||
}
|
}
|
||||||
if (m_current_node->is_html_template_element()) {
|
if (m_current_node->is_html_template_element()) {
|
||||||
// When an XML parser would append a node to a template element, it must instead append it to the template element's template contents (a DocumentFragment node).
|
// When an XML parser would append a node to a template element, it must instead append it to the template element's template contents (a DocumentFragment node).
|
||||||
|
m_template_node_stack.append(*m_current_node);
|
||||||
MUST(static_cast<HTML::HTMLTemplateElement&>(*m_current_node).content()->append_child(node));
|
MUST(static_cast<HTML::HTMLTemplateElement&>(*m_current_node).content()->append_child(node));
|
||||||
} else {
|
} else {
|
||||||
MUST(m_current_node->append_child(node));
|
MUST(m_current_node->append_child(node));
|
||||||
|
@ -229,7 +231,12 @@ void XMLDocumentBuilder::element_end(const XML::Name& name)
|
||||||
script_element.process_the_script_element();
|
script_element.process_the_script_element();
|
||||||
};
|
};
|
||||||
|
|
||||||
m_current_node = m_current_node->parent_node();
|
auto* parent = m_current_node->parent_node();
|
||||||
|
if (parent->is_document_fragment()) {
|
||||||
|
auto template_parent_node = m_template_node_stack.take_last();
|
||||||
|
parent = template_parent_node.ptr();
|
||||||
|
}
|
||||||
|
m_current_node = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XMLDocumentBuilder::text(StringView data)
|
void XMLDocumentBuilder::text(StringView data)
|
||||||
|
@ -267,6 +274,7 @@ void XMLDocumentBuilder::document_end()
|
||||||
// NOTE: Noop.
|
// NOTE: Noop.
|
||||||
|
|
||||||
// Set the insertion point to undefined.
|
// Set the insertion point to undefined.
|
||||||
|
m_template_node_stack.clear();
|
||||||
m_current_node = nullptr;
|
m_current_node = nullptr;
|
||||||
|
|
||||||
// Update the current document readiness to "interactive".
|
// Update the current document readiness to "interactive".
|
||||||
|
|
|
@ -41,6 +41,7 @@ private:
|
||||||
Optional<FlyString> namespace_for_name(XML::Name const&);
|
Optional<FlyString> namespace_for_name(XML::Name const&);
|
||||||
|
|
||||||
GC::Ref<DOM::Document> m_document;
|
GC::Ref<DOM::Document> m_document;
|
||||||
|
GC::RootVector<GC::Ref<DOM::Node>> m_template_node_stack;
|
||||||
GC::Ptr<DOM::Node> m_current_node;
|
GC::Ptr<DOM::Node> m_current_node;
|
||||||
XMLScriptingSupport m_scripting_support { XMLScriptingSupport::Enabled };
|
XMLScriptingSupport m_scripting_support { XMLScriptingSupport::Enabled };
|
||||||
bool m_has_error { false };
|
bool m_has_error { false };
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 3 tests
|
||||||
|
|
||||||
|
3 Pass
|
||||||
|
Pass XMLHttpRequest: template element parsing
|
||||||
|
Pass XMLHttpRequest: template element parsing 1
|
||||||
|
Pass XMLHttpRequest: template element parsing 2
|
36
Tests/LibWeb/Text/input/wpt-import/xhr/template-element.html
Normal file
36
Tests/LibWeb/Text/input/wpt-import/xhr/template-element.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>XMLHttpRequest: template element parsing</title>
|
||||||
|
<script src=../resources/testharness.js></script>
|
||||||
|
<script src=../resources/testharnessreport.js></script>
|
||||||
|
<div id=log></div>
|
||||||
|
<script>
|
||||||
|
async_test(t => {
|
||||||
|
const client = new XMLHttpRequest
|
||||||
|
client.open("GET", "data:text/xml,<template xmlns='http://www.w3.org/1999/xhtml'><test/></template>")
|
||||||
|
client.send()
|
||||||
|
client.onload = t.step_func_done(() => {
|
||||||
|
assert_equals(client.responseXML.documentElement.childElementCount, 0)
|
||||||
|
assert_equals(client.responseXML.documentElement.content.firstChild.localName, "test")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async_test(t => {
|
||||||
|
const client = new XMLHttpRequest
|
||||||
|
client.open("GET", "data:text/xml,<template><test/></template>")
|
||||||
|
client.send()
|
||||||
|
client.onload = t.step_func_done(() => {
|
||||||
|
assert_equals(client.responseXML.documentElement.childElementCount, 1)
|
||||||
|
assert_equals(client.responseXML.documentElement.firstChild.localName, "test")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async_test(t => {
|
||||||
|
const client = new XMLHttpRequest
|
||||||
|
client.open("GET", "data:text/xml,<template xmlns='http://www.w3.org/2000/svg'><test/></template>")
|
||||||
|
client.send()
|
||||||
|
client.onload = t.step_func_done(() => {
|
||||||
|
assert_equals(client.responseXML.documentElement.childElementCount, 1)
|
||||||
|
assert_equals(client.responseXML.documentElement.firstChild.localName, "test")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue