avm2: Perform early allocation and exposing of SimpleButton object

The allocated-but-unconstructed object should be set on
the parent field before we construct the 'up state' object - this
is observable by ActionScript
This commit is contained in:
Aaron Hill 2023-11-26 14:00:43 -05:00 committed by Lord-McSweeney
parent 9f6ab2b7c6
commit 19c3df7cb6
1 changed files with 24 additions and 25 deletions

View File

@ -483,6 +483,21 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
let needs_frame_construction = self.0.read().needs_frame_construction;
if needs_frame_construction {
if needs_avm2_construction {
let mut activation = Avm2Activation::from_nothing(context.reborrow());
match Avm2StageObject::for_display_object(&mut activation, (*self).into(), class) {
Ok(object) => {
self.0.write(activation.context.gc_context).object = Some(object.into())
}
Err(e) => tracing::error!("Got {} when constructing AVM2 side of button", e),
};
if !self.placed_by_script() {
// This is run before we actually call the constructor - the un-constructed object
// is exposed to ActionScript via `parent.<childName>`.
self.set_on_parent_field(&mut activation.context);
}
}
// Prevent re-entrantly constructing this button (since we may run a nested frame here)
self.0.write(context.gc_context).needs_frame_construction = false;
let (up_state, up_should_fire) = self.create_state(context, swf::ButtonState::UP);
@ -548,36 +563,20 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
if needs_avm2_construction {
self.set_state(context, ButtonState::Up);
let mut activation = Avm2Activation::from_nothing(context.reborrow());
match Avm2StageObject::for_display_object(&mut activation, (*self).into(), class) {
Ok(object) => {
self.0.write(activation.context.gc_context).object = Some(object.into())
}
Err(e) => tracing::error!("Got {} when constructing AVM2 side of button", e),
};
if !self.placed_by_script() {
// This is run before we actually call the constructor - the un-constructed object
// is exposed to ActionScript via `parent.<childName>`.
self.set_on_parent_field(&mut activation.context);
}
if has_movie_clip_state && self.movie().version() > 9 {
self.0
.write(activation.context.gc_context)
.weird_framescript_order = true;
self.0.write(context.gc_context).weird_framescript_order = true;
let stage = activation.context.stage;
stage.construct_frame(&mut activation.context);
stage.frame_constructed(&mut activation.context);
self.set_state(&mut activation.context, ButtonState::Up);
stage.run_frame_scripts(&mut activation.context);
stage.exit_frame(&mut activation.context);
let stage = context.stage;
stage.construct_frame(context);
stage.frame_constructed(context);
self.set_state(context, ButtonState::Up);
stage.run_frame_scripts(context);
stage.exit_frame(context);
}
let avm2_object = self.0.read().object;
if let Some(avm2_object) = avm2_object {
let mut activation = Avm2Activation::from_nothing(context.reborrow());
if let Err(e) = class.call_native_init(avm2_object.into(), &[], &mut activation)
{
tracing::error!("Got {} when constructing AVM2 side of button", e);
@ -586,7 +585,7 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
// Note - we do *not* call `on_construction_complete` here, since we already
// set the button object on a parent field (and we don't want to re-run a setter)
self.fire_added_events(&mut activation.context);
self.fire_added_events(context);
}
}
}