diff --git a/core/src/avm1/globals/xml.rs b/core/src/avm1/globals/xml.rs index 8dcf97517..7df2a4944 100644 --- a/core/src/avm1/globals/xml.rs +++ b/core/src/avm1/globals/xml.rs @@ -82,7 +82,7 @@ pub fn xmlnode_append_child<'gc>( args.get(0) .and_then(|n| n.coerce_to_object(activation).as_xml_node()), ) { - if let Ok(None) = child_xmlnode.parent() { + if !xmlnode.has_child(child_xmlnode) { let position = xmlnode.children_len(); if let Err(e) = xmlnode.insert_child(activation.context.gc_context, position, child_xmlnode) @@ -111,7 +111,7 @@ pub fn xmlnode_insert_before<'gc>( args.get(1) .and_then(|n| n.coerce_to_object(activation).as_xml_node()), ) { - if let Ok(None) = child_xmlnode.parent() { + if !xmlnode.has_child(child_xmlnode) { if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) { if let Err(e) = xmlnode.insert_child(activation.context.gc_context, position, child_xmlnode) diff --git a/core/src/xml/tree.rs b/core/src/xml/tree.rs index b1338ca0d..b220b0bc1 100644 --- a/core/src/xml/tree.rs +++ b/core/src/xml/tree.rs @@ -665,6 +665,12 @@ impl<'gc> XmlNode<'gc> { return Err(Error::CannotInsertIntoSelf); } + if let Some(mut it) = self.ancestors() { + if it.find(|ancestor| GcCell::ptr_eq(ancestor.0, child.0)).is_some() { + return Err(Error::CannotInsertIntoSelf); + } + } + match &mut *self.0.write(mc) { XmlNodeData::Element { ref mut children, .. @@ -779,6 +785,15 @@ impl<'gc> XmlNode<'gc> { None } + /// Checks if `child` is a direct descendant of `self`. + pub fn has_child(self, child: XmlNode<'gc>) -> bool { + child + .parent() + .unwrap_or(None) + .filter(|p| GcCell::ptr_eq(self.0, p.0)) + .is_some() + } + /// Retrieve a given child by index (e.g. position in the document). pub fn get_child_by_index(self, index: usize) -> Option> { match &*self.0.read() { diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index 6afeed601..51f795013 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -203,6 +203,7 @@ swf_tests! { (xml_append_child, "avm1/xml_append_child", 1), (xml_append_child_with_parent, "avm1/xml_append_child_with_parent", 1), (xml_remove_node, "avm1/xml_remove_node", 1), + (xml_reparenting, "avm1/xml_reparenting", 1), (xml_insert_before, "avm1/xml_insert_before", 1), (xml_to_string, "avm1/xml_to_string", 1), (xml_to_string_comment, "avm1/xml_to_string_comment", 1), diff --git a/core/tests/swfs/avm1/xml_reparenting/Test.as b/core/tests/swfs/avm1/xml_reparenting/Test.as new file mode 100644 index 000000000..be6ddbf5a --- /dev/null +++ b/core/tests/swfs/avm1/xml_reparenting/Test.as @@ -0,0 +1,29 @@ +// Compile with: +// mtasc -main -header 200:150:30 Test.as -swf test.swf +class Test { + static function main(current) { + trace("// reparenting in self"); + var xml = new XML(""); + trace(xml); + xml.appendChild(xml.firstChild.firstChild); + trace("// after"); + trace(xml); + + trace("// reparenting in other"); + xml = new XML(""); + var other = new XML(""); + trace(xml); + trace(other); + other.insertBefore(xml.firstChild.firstChild, other.firstChild); + trace("// after"); + trace(xml); + trace(other); + + trace("// can't reparent in descendent"); + xml = new XML(""); + trace(xml); + xml.firstChild.firstChild.appendChild(xml.firstChild); + trace("// after"); + trace(xml); + } +} \ No newline at end of file diff --git a/core/tests/swfs/avm1/xml_reparenting/output.txt b/core/tests/swfs/avm1/xml_reparenting/output.txt new file mode 100644 index 000000000..4dc482b6e --- /dev/null +++ b/core/tests/swfs/avm1/xml_reparenting/output.txt @@ -0,0 +1,14 @@ +// reparenting in self + +// after + +// reparenting in other + + +// after + + +// can't reparent in descendent + +// after + \ No newline at end of file diff --git a/core/tests/swfs/avm1/xml_reparenting/test.swf b/core/tests/swfs/avm1/xml_reparenting/test.swf new file mode 100644 index 000000000..b66d20058 Binary files /dev/null and b/core/tests/swfs/avm1/xml_reparenting/test.swf differ