diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 7e07c0cf0..0cd45b4d8 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -1798,37 +1798,52 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { if let Some(avm2_object) = avm2_object { if let Some(frame_id) = write.queued_script_frame { - let is_fresh_frame = write.queued_script_frame != write.last_queued_script_frame; - - write.last_queued_script_frame = Some(frame_id); - write.queued_script_frame = None; - write + // If we are already executing frame scripts, then we shouldn't + // run frame scripts recursively. This is because AVM2 can run + // gotos, which will both queue and run frame scripts for the + // whole movie again. If a goto is attempting to queue frame + // scripts on us AGAIN, we should allow the current stack to + // wind down before handling that. + if !write .flags - .insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); + .contains(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT) + { + let is_fresh_frame = + write.queued_script_frame != write.last_queued_script_frame; - if is_fresh_frame { - while let Some(fs) = write.frame_scripts.get(index) { - if fs.frame_id == frame_id { - let callable = fs.callable; - drop(write); - if let Err(e) = Avm2::run_stack_frame_for_callable( - callable, - Some(avm2_object), - &[], - context, - ) { - log::error!("Error occured when running AVM2 frame script: {}", e); + write.last_queued_script_frame = Some(frame_id); + write.queued_script_frame = None; + write + .flags + .insert(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); + + if is_fresh_frame { + while let Some(fs) = write.frame_scripts.get(index) { + if fs.frame_id == frame_id { + let callable = fs.callable; + drop(write); + if let Err(e) = Avm2::run_stack_frame_for_callable( + callable, + Some(avm2_object), + &[], + context, + ) { + log::error!( + "Error occured when running AVM2 frame script: {}", + e + ); + } + write = self.0.write(context.gc_context); } - write = self.0.write(context.gc_context); + + index += 1; } - - index += 1; } - } - write - .flags - .remove(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); + write + .flags + .remove(MovieClipFlags::EXECUTING_AVM2_FRAME_SCRIPT); + } } } diff --git a/tests/tests/regression_tests.rs b/tests/tests/regression_tests.rs index 8c37df5bd..93c2b0565 100644 --- a/tests/tests/regression_tests.rs +++ b/tests/tests/regression_tests.rs @@ -356,6 +356,7 @@ swf_tests! { (as3_movieclip_goto_during_frame_script, "avm2/movieclip_goto_during_frame_script", 1), (as3_movieclip_gotoandplay, "avm2/movieclip_gotoandplay", 5), (as3_movieclip_gotoandstop, "avm2/movieclip_gotoandstop", 5), + (as3_movieclip_gotoandstop_queueing, "avm2/movieclip_gotoandstop_queueing", 2), (as3_movieclip_next_frame, "avm2/movieclip_next_frame", 5), (as3_movieclip_next_scene, "avm2/movieclip_next_scene", 5), (as3_movieclip_play, "avm2/movieclip_play", 5), diff --git a/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/output.txt b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/output.txt new file mode 100644 index 000000000..4b37a20d2 --- /dev/null +++ b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/output.txt @@ -0,0 +1,12 @@ +///Parent frame 1 +///this.gotoAndStop(2); +///this.child.gotoAndStop(3); +///Child frame 3 +///this.gotoAndStop(2); +///this.parent.gotoAndStop(5); +///End child frame 3 +///End parent frame 1 +///Parent frame 5 +///this.child.gotoAndStop(5); +///Child frame 5 +///End parent frame 5 diff --git a/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.fla b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.fla new file mode 100644 index 000000000..17c37eaea Binary files /dev/null and b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.fla differ diff --git a/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.swf b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.swf new file mode 100644 index 000000000..8cf3858a0 Binary files /dev/null and b/tests/tests/swfs/avm2/movieclip_gotoandstop_queueing/test.swf differ