From ed6fa6b2fe701926f9aa10854090972380c909dc Mon Sep 17 00:00:00 2001 From: sleepycatcoding <131554884+sleepycatcoding@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:05:21 +0300 Subject: [PATCH] avm2: Support indices in XMLList.child --- core/src/avm2/globals/xml.rs | 24 +--------- core/src/avm2/globals/xml_list.rs | 30 ++++++++----- core/src/avm2/object/xml_object.rs | 45 ++++++++++++++++++- .../e4x/XMLList/e13_5_4_4/test.toml | 1 - 4 files changed, 64 insertions(+), 36 deletions(-) diff --git a/core/src/avm2/globals/xml.rs b/core/src/avm2/globals/xml.rs index b5e193bd8..1135ee99c 100644 --- a/core/src/avm2/globals/xml.rs +++ b/core/src/avm2/globals/xml.rs @@ -216,29 +216,9 @@ pub fn child<'gc>( ) -> Result, Error<'gc>> { let xml = this.as_xml_object().unwrap(); let multiname = name_to_multiname(activation, &args[0], false)?; - let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() { - if let Some(local_name) = multiname.local_name() { - if let Ok(index) = local_name.parse::() { - let children = if let Some(node) = children.get(index) { - vec![E4XOrXml::E4X(*node)] - } else { - Vec::new() - }; - return Ok(XmlListObject::new(activation, children, None, None).into()); - } - } - children - .iter() - .filter(|node| node.matches_name(&multiname)) - .map(|node| E4XOrXml::E4X(*node)) - .collect() - } else { - Vec::new() - }; - - // FIXME: If name is not a number index, then we should call [[Get]] (get_property_local) with the name. - Ok(XmlListObject::new(activation, children, Some(xml.into()), Some(multiname)).into()) + let list = xml.child(&multiname, activation); + Ok(list.into()) } pub fn child_index<'gc>( diff --git a/core/src/avm2/globals/xml_list.rs b/core/src/avm2/globals/xml_list.rs index 22b114ef4..de1903ae6 100644 --- a/core/src/avm2/globals/xml_list.rs +++ b/core/src/avm2/globals/xml_list.rs @@ -166,21 +166,26 @@ pub fn child<'gc>( this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - let list = this.as_xml_list_object().unwrap(); + let this = this.as_xml_list_object().unwrap(); let multiname = name_to_multiname(activation, &args[0], false)?; - let children = list.children(); - let mut sub_children = Vec::new(); - for child in &*children { - if let E4XNodeKind::Element { ref children, .. } = &*child.node().kind() { - sub_children.extend( - children - .iter() - .filter(|node| node.matches_name(&multiname)) - .map(|node| E4XOrXml::E4X(*node)), - ); + let mut children = this.children_mut(activation.gc()); + + // 1. Let m be a new XMLList with m.[[TargetObject]] = list + let list = XmlListObject::new(activation, Some(this.into()), None); + + // 2. For i = 0 to list.[[Length]]-1 + for child in &mut *children { + // 2.a. Let r = list[i].child(propertyName) + let child = child.get_or_create_xml(activation); + let r = child.child(&multiname, activation); + // 2.b. If r.[[Length]] > 0, call the [[Append]] method of m with argument r + if r.length() > 0 { + list.append(r.into(), activation.gc()); } } - Ok(XmlListObject::new(activation, sub_children, Some(list.into()), None).into()) + + // 3. Return m + Ok(list.into()) } pub fn children<'gc>( @@ -308,6 +313,7 @@ pub fn descendants<'gc>( } } +// ECMA-357 13.5.4.20 XMLList.prototype.text ( ) pub fn text<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, diff --git a/core/src/avm2/object/xml_object.rs b/core/src/avm2/object/xml_object.rs index 252bcee0b..82af8e364 100644 --- a/core/src/avm2/object/xml_object.rs +++ b/core/src/avm2/object/xml_object.rs @@ -14,7 +14,7 @@ use gc_arena::{Collect, GcCell, GcWeakCell, Mutation}; use ruffle_wstr::WString; use std::cell::{Ref, RefMut}; -use super::xml_list_object::E4XOrXml; +use super::xml_list_object::{E4XOrXml, XmlOrXmlListObject}; use super::PrimitiveObject; /// A class instance allocator that allocates XML objects. @@ -70,6 +70,49 @@ impl<'gc> XmlObject<'gc> { )) } + pub fn child( + &self, + name: &Multiname<'gc>, + activation: &mut Activation<'_, 'gc>, + ) -> XmlListObject<'gc> { + let children = if let E4XNodeKind::Element { children, .. } = &*self.node().kind() { + if let Some(local_name) = name.local_name() { + if let Ok(index) = local_name.parse::() { + let children = if let Some(node) = children.get(index) { + vec![E4XOrXml::E4X(*node)] + } else { + Vec::new() + }; + + let list = XmlListObject::new_with_children(activation, children, None, None); + + if list.length() > 0 { + // NOTE: Since avmplus uses appendNode here, when the node exists, that implicitly sets the target_dirty flag. + list.set_dirty_flag(activation.gc()); + } + + return list; + } + } + + children + .iter() + .filter(|node| node.matches_name(name)) + .map(|node| E4XOrXml::E4X(*node)) + .collect() + } else { + Vec::new() + }; + + // FIXME: If name is not a number index, then we should call [[Get]] (get_property_local) with the name. + XmlListObject::new_with_children( + activation, + children, + Some(XmlOrXmlListObject::Xml(*self)), + Some(name.clone()), + ) + } + pub fn length(&self) -> Option { self.node().length() } diff --git a/tests/tests/swfs/from_avmplus/e4x/XMLList/e13_5_4_4/test.toml b/tests/tests/swfs/from_avmplus/e4x/XMLList/e13_5_4_4/test.toml index 29f3cef79..cf6123969 100644 --- a/tests/tests/swfs/from_avmplus/e4x/XMLList/e13_5_4_4/test.toml +++ b/tests/tests/swfs/from_avmplus/e4x/XMLList/e13_5_4_4/test.toml @@ -1,2 +1 @@ num_ticks = 1 -known_failure = true