From 554f0dc1e54ee2c42bdcc05e1a250cc5601f3f39 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Fri, 20 Dec 2019 15:28:49 -0500 Subject: [PATCH] Add XMLNode class and constructor impl --- core/src/avm1/globals.rs | 10 ++++++++ core/src/avm1/globals/xml.rs | 46 ++++++++++++++++++++++++++++++++++++ core/src/avm1/xml_object.rs | 1 + core/src/xml/tree.rs | 20 ++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 core/src/avm1/globals/xml.rs diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index fd3f0a56a..f6ac046b4 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -20,6 +20,7 @@ mod object; mod sound; mod stage; pub(crate) mod text_field; +mod xml; #[allow(non_snake_case, unused_must_use)] //can't use errors yet pub fn getURL<'a, 'gc>( @@ -172,6 +173,8 @@ pub fn create_globals<'gc>( let array_proto: Object<'gc> = array::create_proto(gc_context, object_proto, function_proto); let color_proto: Object<'gc> = color::create_proto(gc_context, object_proto, function_proto); + let xmlnode_proto: Object<'gc> = + xml::create_xmlnode_proto(gc_context, object_proto, function_proto); //TODO: These need to be constructors and should also set `.prototype` on each one let object = ScriptObject::function( @@ -217,6 +220,12 @@ pub fn create_globals<'gc>( Some(function_proto), Some(array_proto), ); + let xmlnode = ScriptObject::function( + gc_context, + Executable::Native(xml::xmlnode_constructor), + Some(function_proto), + Some(xmlnode_proto), + ); let listeners = SystemListeners::new(gc_context, Some(array_proto)); @@ -228,6 +237,7 @@ pub fn create_globals<'gc>( globals.define_value(gc_context, "MovieClip", movie_clip.into(), EnumSet::empty()); globals.define_value(gc_context, "Sound", sound.into(), EnumSet::empty()); globals.define_value(gc_context, "TextField", text_field.into(), EnumSet::empty()); + globals.define_value(gc_context, "XMLNode", xmlnode.into(), EnumSet::empty()); globals.force_set_function( "Number", number, diff --git a/core/src/avm1/globals/xml.rs b/core/src/avm1/globals/xml.rs new file mode 100644 index 000000000..4f7a4d6c6 --- /dev/null +++ b/core/src/avm1/globals/xml.rs @@ -0,0 +1,46 @@ +//! XML/XMLNode global classes + +use crate::avm1::return_value::ReturnValue; +use crate::avm1::script_object::ScriptObject; +use crate::avm1::{Avm1, Error, Object, TObject, UpdateContext, Value}; +use crate::xml::XMLNode; +use gc_arena::MutationContext; +use std::mem::swap; + +/// XMLNode constructor +pub fn xmlnode_constructor<'gc>( + avm: &mut Avm1<'gc>, + ac: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error> { + match ( + args.get(0).map(|v| v.as_number(avm, ac).map(|v| v as u32)), + args.get(1).map(|v| v.as_string()), + this.as_xml_node(), + ) { + (Some(Ok(1)), Some(Ok(strval)), Some(ref mut this_node)) => { + let mut xmlelement = XMLNode::new_text(ac.gc_context, strval); + swap(&mut xmlelement, this_node); + } + (Some(Ok(3)), Some(Ok(strval)), Some(ref mut this_node)) => { + let mut xmlelement = XMLNode::new_element(ac.gc_context, strval)?; + swap(&mut xmlelement, this_node); + } + //Invalid nodetype ID, string value missing, or not an XMLElement + _ => {} + }; + + Ok(Value::Undefined.into()) +} + +/// Construct the prototype for `XMLNode`. +pub fn create_xmlnode_proto<'gc>( + gc_context: MutationContext<'gc, '_>, + proto: Object<'gc>, + _fn_proto: Object<'gc>, +) -> Object<'gc> { + let xmlnode_proto = ScriptObject::object(gc_context, Some(proto)); + + xmlnode_proto.into() +} diff --git a/core/src/avm1/xml_object.rs b/core/src/avm1/xml_object.rs index fa4bfae00..f8f593082 100644 --- a/core/src/avm1/xml_object.rs +++ b/core/src/avm1/xml_object.rs @@ -101,6 +101,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> { self.base().call(avm, context, this, args) } + #[allow(clippy::new_ret_no_self)] fn new( &self, _avm: &mut Avm1<'gc>, diff --git a/core/src/xml/tree.rs b/core/src/xml/tree.rs index 6792ce931..df0127128 100644 --- a/core/src/xml/tree.rs +++ b/core/src/xml/tree.rs @@ -31,12 +31,20 @@ impl XMLName { Self::from_bytes_cow(Cow::Borrowed(bytes)) } + pub fn from_str(strval: &str) -> Result { + Self::from_str_cow(Cow::Borrowed(strval)) + } + pub fn from_bytes_cow(bytes: Cow<[u8]>) -> Result { let full_name = match bytes { Cow::Borrowed(ln) => Cow::Borrowed(std::str::from_utf8(ln)?), Cow::Owned(ln) => Cow::Owned(String::from_utf8(ln)?), }; + Self::from_str_cow(full_name) + } + + pub fn from_str_cow(full_name: Cow) -> Result { if let Some(colon_index) = full_name.find(':') { Ok(Self { namespace: Some(full_name[0..colon_index].to_owned()), @@ -99,6 +107,18 @@ impl<'gc> XMLNode<'gc> { )) } + /// Construct a new XML element node. + pub fn new_element(mc: MutationContext<'gc, '_>, element_name: &str) -> Result { + Ok(XMLNode(GcCell::allocate( + mc, + XMLNodeData::Element { + tag_name: XMLName::from_str(element_name)?, + attributes: BTreeMap::new(), + children: Vec::new(), + }, + ))) + } + /// Construct an XML node from a `quick_xml` `BytesStart` event. /// /// The returned node will always be an `Element`, and it must only contain