From a5f09d3c09ef8801220f02d4a0cbf7b64bdb4a35 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Thu, 29 Oct 2020 19:44:01 -0400 Subject: [PATCH] avm2: Implement `removeChild`. --- .../flash/display/displayobjectcontainer.rs | 50 ++++++++++++++++++ core/tests/regression_tests.rs | 1 + .../output.txt | 10 ++++ .../test.fla | Bin 0 -> 6065 bytes .../test.swf | Bin 0 -> 1180 bytes 5 files changed, 61 insertions(+) create mode 100644 core/tests/swfs/avm2/displayobjectcontainer_removechild/output.txt create mode 100644 core/tests/swfs/avm2/displayobjectcontainer_removechild/test.fla create mode 100644 core/tests/swfs/avm2/displayobjectcontainer_removechild/test.swf diff --git a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs index 4767d4a3e..52b996046 100644 --- a/core/src/avm2/globals/flash/display/displayobjectcontainer.rs +++ b/core/src/avm2/globals/flash/display/displayobjectcontainer.rs @@ -65,6 +65,28 @@ fn validate_add_operation<'gc>( Ok(()) } +/// Validate if we can add remove a child from a given parent. +/// +/// There are several conditions which should cause an add operation to fail: +/// +/// * The child is not a child of the parent +fn validate_remove_operation<'gc>( + old_parent: DisplayObject<'gc>, + proposed_child: DisplayObject<'gc>, +) -> Result<(), Error> { + let _ = old_parent + .as_movie_clip() + .ok_or("ArgumentError: Parent is not a movieclip")?; + + for child in old_parent.children() { + if DisplayObject::ptr_eq(child, proposed_child) { + return Ok(()); + } + } + + Err("ArgumentError: Cannot remove object from display list it is not a child of.".into()) +} + /// Remove an element from it's parent display list. /// /// ActionScript 3 works in child indexes, even though the underlying display @@ -212,6 +234,30 @@ pub fn add_child_at<'gc>( Ok(Value::Undefined) } +/// Implements `DisplayObjectContainer.removeChild` +pub fn remove_child<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(parent) = this.and_then(|this| this.as_display_object()) { + let child = args + .get(0) + .cloned() + .unwrap_or(Value::Undefined) + .coerce_to_object(activation)? + .as_display_object() + .ok_or("ArgumentError: Child not a valid display object")?; + + validate_remove_operation(parent, child)?; + remove_child_from_displaylist(&mut activation.context, child); + + return Ok(child.object2()); + } + + Ok(Value::Undefined) +} + /// Implements `DisplayObjectContainer.numChildren` pub fn num_children<'gc>( _activation: &mut Activation<'_, 'gc, '_>, @@ -260,6 +306,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> QName::new(Namespace::public_namespace(), "addChildAt"), Method::from_builtin(add_child_at), )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public_namespace(), "removeChild"), + Method::from_builtin(remove_child), + )); class } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index a1ce0e679..c5ed78554 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -415,6 +415,7 @@ swf_tests! { (as3_displayobjectcontainer_getchildbyname, "avm2/displayobjectcontainer_getchildbyname", 1), (as3_displayobjectcontainer_addchild, "avm2/displayobjectcontainer_addchild", 1), (as3_displayobjectcontainer_addchildat, "avm2/displayobjectcontainer_addchildat", 1), + (as3_displayobjectcontainer_removechild, "avm2/displayobjectcontainer_removechild", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/core/tests/swfs/avm2/displayobjectcontainer_removechild/output.txt b/core/tests/swfs/avm2/displayobjectcontainer_removechild/output.txt new file mode 100644 index 000000000..11f2162e8 --- /dev/null +++ b/core/tests/swfs/avm2/displayobjectcontainer_removechild/output.txt @@ -0,0 +1,10 @@ +//var chx = this.getChildAt(0) +//var chy = this.getChildAt(1) +//var chz = this.getChildAt(2) +//this.removeChild(chy) +//chx === this.getChildAt(0) +true +//chz === this.getChildAt(1) +true +//this.numChildren +2 diff --git a/core/tests/swfs/avm2/displayobjectcontainer_removechild/test.fla b/core/tests/swfs/avm2/displayobjectcontainer_removechild/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..45fec6d8ea6cd8d59e6d49c87c239fff21b12581 GIT binary patch literal 6065 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 literal 0 HcmV?d00001 diff --git a/core/tests/swfs/avm2/displayobjectcontainer_removechild/test.swf b/core/tests/swfs/avm2/displayobjectcontainer_removechild/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..b49c8037eb63540e4910c73709f2ba92783ff052 GIT binary patch literal 1180 zcmV;N1Y`R{S5qtB2LJ$goQ0K5Y~w@}$7dWnw)52_ZJjp#O1EjZrEToCP}o4}mX=*A zSXH73aY)s;&ZP0;k6=6L#}x?#;sysQI4t+2hpK0~AT8(Ikzg-eQF_@EQkk)xG=%_G zqIvK4#{YTq-e`OXm|XyR9sw`_!A61u0IXA2S66SO02qtc2D1xUeLs8W-OYp2vHjLw zseWXfqAOmF<0Js1d7a(b#k>;^+RXzdn{;z&~XY}c#`uBme zuSd?GfGa+ZXHhu(NiYcU0PbS{b7E5<{Y~l*Unc-i^7QxQ=`#qHP5{w)d*g?vzdS$v z?d-Fa^G#oeZs`QtIspuazxYmi=IJn?^z4jTor^(8stANQVBSI+3Olieg>1GWeimRx(aSwQ(z)^@^&M)|_c+W~6nl za-HWY@1N(;_1w13Q5T(snRfWNOL%c6C5kSzkZ#tI%crr_Ot=FKi=vz5O1C`S;BnXB z!c5XvH)^^IEo6i{?3i3C-6Q+iRxGvZO#I81JH4P+*`B`@GkT-$iGOrS=-%9*cM$Gx z<_qg)2^FkbxvZgrVp^y`lCcZ+LD??Ib$zkmci40ll$OG-S0{z+sBl}Lh2Q`uz!c4J zOpNQ{;zEK;3cW%f*DnY{iW?9Hg|sll4Ra&hC^sf#gmK{(Hz8z&Np335rEjOF(=+K8 z(zEF~YAVRmkm2}Hn2JPWJ@G`cx36DF4GgA-hDS!n0-5n!6WPfrfrSi9u`JEAbcm(H zEED0QkQX7(Lp}ugFyxmY|1#uXfqVqAbe@gA%2Kbf)CxOk!A|V*P7`K9qujF#=n|UVj0|2uf^F&Q78L literal 0 HcmV?d00001