diff --git a/core/src/avm1/globals/xml.rs b/core/src/avm1/globals/xml.rs index ff766e2f4..8a024bf8d 100644 --- a/core/src/avm1/globals/xml.rs +++ b/core/src/avm1/globals/xml.rs @@ -532,18 +532,18 @@ pub fn xml_constructor<'gc>( this.as_xml_node(), ) { (Some(Ok(ref string)), Some(ref mut this_node)) => { - let xmldoc = XMLDocument::from_str(ac.gc_context, string)?; - xmldoc - .as_node() - .introduce_script_object(ac.gc_context, this); - this_node.swap(ac.gc_context, xmldoc.as_node()); + let xmldoc = XMLDocument::new(ac.gc_context); + let mut xmlnode = xmldoc.as_node(); + xmlnode.introduce_script_object(ac.gc_context, this); + this_node.swap(ac.gc_context, xmlnode); + + this_node.replace_with_str(ac.gc_context, string)?; } (None, Some(ref mut this_node)) => { let xmldoc = XMLDocument::new(ac.gc_context); - xmldoc - .as_node() - .introduce_script_object(ac.gc_context, this); - this_node.swap(ac.gc_context, xmldoc.as_node()); + let mut xmlnode = xmldoc.as_node(); + xmlnode.introduce_script_object(ac.gc_context, this); + this_node.swap(ac.gc_context, xmlnode); } //Non-string argument or not an XML document _ => {} diff --git a/core/src/xml/document.rs b/core/src/xml/document.rs index 87730fbf4..27233cfcd 100644 --- a/core/src/xml/document.rs +++ b/core/src/xml/document.rs @@ -1,9 +1,7 @@ //! XML Document -use crate::xml::{Error, XMLNode}; +use crate::xml::XMLNode; use gc_arena::{Collect, GcCell, MutationContext}; -use quick_xml::events::Event; -use quick_xml::Reader; use std::fmt; /// The entirety of an XML document. @@ -29,63 +27,6 @@ impl<'gc> XMLDocument<'gc> { document } - /// Ensure that a newly-encountered node is added to an ongoing parsing - /// stack, or to the document itself if the parsing stack is empty. - fn add_child_to_tree( - &mut self, - mc: MutationContext<'gc, '_>, - open_tags: &mut Vec>, - child: XMLNode<'gc>, - ) -> Result<(), Error> { - if let Some(node) = open_tags.last_mut() { - node.append_child(mc, child)?; - } else { - self.as_node().append_child(mc, child)?; - } - - Ok(()) - } - - pub fn from_str(mc: MutationContext<'gc, '_>, data: &str) -> Result { - let mut parser = Reader::from_str(data); - let mut buf = Vec::new(); - let mut document = Self::new(mc); - let mut open_tags: Vec> = Vec::new(); - - loop { - match parser.read_event(&mut buf)? { - Event::Start(bs) => { - let child = XMLNode::from_start_event(mc, bs, document)?; - document.add_child_to_tree(mc, &mut open_tags, child)?; - open_tags.push(child); - } - Event::Empty(bs) => { - let child = XMLNode::from_start_event(mc, bs, document)?; - document.add_child_to_tree(mc, &mut open_tags, child)?; - } - Event::End(_) => { - open_tags.pop(); - } - Event::Text(bt) => { - let child = XMLNode::text_from_text_event(mc, bt, document)?; - if child.node_value().as_deref() != Some("") { - document.add_child_to_tree(mc, &mut open_tags, child)?; - } - } - Event::Comment(bt) => { - let child = XMLNode::comment_from_text_event(mc, bt, document)?; - if child.node_value().as_deref() != Some("") { - document.add_child_to_tree(mc, &mut open_tags, child)?; - } - } - Event::Eof => break, - _ => {} - } - } - - Ok(document) - } - /// Yield the document in node form. /// /// If the document does not have a node, then this function will panic. diff --git a/core/src/xml/tests.rs b/core/src/xml/tests.rs index 31956b545..32a6b251d 100644 --- a/core/src/xml/tests.rs +++ b/core/src/xml/tests.rs @@ -8,7 +8,10 @@ use gc_arena::rootless_arena; #[test] fn parse_single_element() { rootless_arena(|mc| { - let xml = XMLDocument::from_str(mc, "").expect("Parsed document"); + let xml = XMLDocument::new(mc); + xml.as_node() + .replace_with_str(mc, "") + .expect("Parsed document"); dbg!(xml); let mut roots = xml .as_node() @@ -30,11 +33,13 @@ fn parse_single_element() { #[test] fn double_ended_children() { rootless_arena(|mc| { - let xml = XMLDocument::from_str( - mc, - "", - ) - .expect("Parsed document"); + let xml = XMLDocument::new(mc); + xml.as_node() + .replace_with_str( + mc, + "", + ) + .expect("Parsed document"); let mut roots = xml .as_node() diff --git a/core/src/xml/tree.rs b/core/src/xml/tree.rs index 71343125e..aa4578b7c 100644 --- a/core/src/xml/tree.rs +++ b/core/src/xml/tree.rs @@ -2,13 +2,13 @@ use crate::avm1::xml_attributes_object::XMLAttributesObject; use crate::avm1::xml_object::XMLObject; -use crate::avm1::Object; +use crate::avm1::{Object, TObject}; use crate::xml; use crate::xml::{Error, XMLDocument, XMLName}; use gc_arena::{Collect, GcCell, MutationContext}; use quick_xml::events::attributes::Attribute; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; -use quick_xml::Writer; +use quick_xml::{Reader, Writer}; use std::borrow::Cow; use std::collections::BTreeMap; use std::fmt; @@ -166,6 +166,73 @@ impl<'gc> XMLNode<'gc> { )) } + /// Ensure that a newly-encountered node is added to an ongoing parsing + /// stack, or to the document root itself if the parsing stack is empty. + fn add_child_to_tree( + &mut self, + mc: MutationContext<'gc, '_>, + open_tags: &mut Vec>, + child: XMLNode<'gc>, + ) -> Result<(), Error> { + if let Some(node) = open_tags.last_mut() { + node.append_child(mc, child)?; + } else { + self.append_child(mc, child)?; + } + + Ok(()) + } + + /// Replace the contents of this node with the result of parsing a string. + /// + /// Node replacements are only supported on document root nodes; elements + /// may work but will be incorrect. + /// + /// Also, this method does not yet actually remove existing node contents. + pub fn replace_with_str( + &mut self, + mc: MutationContext<'gc, '_>, + data: &str, + ) -> Result<(), Error> { + let mut parser = Reader::from_str(data); + let mut buf = Vec::new(); + let document = self.document(); + let mut open_tags: Vec> = Vec::new(); + + loop { + match parser.read_event(&mut buf)? { + Event::Start(bs) => { + let child = XMLNode::from_start_event(mc, bs, document)?; + self.add_child_to_tree(mc, &mut open_tags, child)?; + open_tags.push(child); + } + Event::Empty(bs) => { + let child = XMLNode::from_start_event(mc, bs, document)?; + self.add_child_to_tree(mc, &mut open_tags, child)?; + } + Event::End(_) => { + open_tags.pop(); + } + Event::Text(bt) => { + let child = XMLNode::text_from_text_event(mc, bt, document)?; + if child.node_value().as_deref() != Some("") { + self.add_child_to_tree(mc, &mut open_tags, child)?; + } + } + Event::Comment(bt) => { + let child = XMLNode::comment_from_text_event(mc, bt, document)?; + if child.node_value().as_deref() != Some("") { + self.add_child_to_tree(mc, &mut open_tags, child)?; + } + } + Event::Eof => break, + _ => {} + } + } + + Ok(()) + } + /// Construct an XML Element node from a `quick_xml` `BytesStart` event. /// /// The returned node will always be an `Element`, and it must only contain @@ -762,7 +829,8 @@ impl<'gc> XMLNode<'gc> { /// /// After this function completes, the current `XMLNode` will contain all /// data present in the `other` node, and vice versa. References to the node - /// within the tree will *not* be updated. + /// will *not* be updated: it is a logic error to swap nodes that have + /// existing referents. pub fn swap(&mut self, gc_context: MutationContext<'gc, '_>, other: Self) { if !GcCell::ptr_eq(self.0, other.0) { swap(