core: Replace `on_exit_frame` with iteration over Loaders
This removes the need to traverse the entire display object tree - we instead just try to fire events (when ready) on corresponding `MovieClip`s for our `Loader::Movie` instances
This commit is contained in:
parent
94bdf6b9c6
commit
514b5ad774
|
@ -259,12 +259,15 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
self.0.complete_event_fired.set(false);
|
||||
}
|
||||
|
||||
/// Fires the 'init' and 'complete' events if they haven't been fired yet.
|
||||
/// Returns `true` if both events have been fired (either as a result of
|
||||
/// this call, or due to a previous call).
|
||||
pub fn fire_init_and_complete_events(
|
||||
&self,
|
||||
context: &mut UpdateContext<'gc>,
|
||||
status: u16,
|
||||
redirected: bool,
|
||||
) {
|
||||
) -> bool {
|
||||
self.0.expose_content.set(true);
|
||||
if !self.0.init_event_fired.get() {
|
||||
self.0.init_event_fired.set(true);
|
||||
|
@ -313,8 +316,11 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
self.0.complete_event_fired.set(true);
|
||||
let complete_evt = EventObject::bare_default_event(context, "complete");
|
||||
Avm2::dispatch_event(context, complete_evt, (*self).into());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Unwrap this object's loader stream
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::avm2::{
|
|||
};
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::drawing::Drawing;
|
||||
use crate::loader::LoadManager;
|
||||
use crate::prelude::*;
|
||||
use crate::string::{AvmString, WString};
|
||||
use crate::tag_utils::SwfMovie;
|
||||
|
@ -2070,15 +2071,7 @@ pub trait TDisplayObject<'gc>:
|
|||
let dobject_constr = context.avm2.classes().display_object;
|
||||
Avm2::broadcast_event(context, exit_frame_evt, dobject_constr);
|
||||
|
||||
self.on_exit_frame(context);
|
||||
}
|
||||
|
||||
fn on_exit_frame(&self, context: &mut UpdateContext<'gc>) {
|
||||
if let Some(container) = self.as_container() {
|
||||
for child in container.iter_render_list() {
|
||||
child.on_exit_frame(context);
|
||||
}
|
||||
}
|
||||
LoadManager::run_exit_frame(context);
|
||||
}
|
||||
|
||||
/// Called before the child is about to be rendered.
|
||||
|
|
|
@ -503,6 +503,21 @@ impl<'gc> MovieClip<'gc> {
|
|||
self.0.write(gc_context).set_initialized(true);
|
||||
}
|
||||
|
||||
/// Tries to fire events from our `LoaderInfo` object if we're ready - returns
|
||||
/// `true` if both `init` and `complete` have been fired
|
||||
pub fn try_fire_loaderinfo_events(self, context: &mut UpdateContext<'gc>) -> bool {
|
||||
if self.0.read().initialized() {
|
||||
if let Some(loader_info) = self
|
||||
.loader_info()
|
||||
.as_ref()
|
||||
.and_then(|o| o.as_loader_info_object())
|
||||
{
|
||||
return loader_info.fire_init_and_complete_events(context, 0, false);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Preload a chunk of the movie.
|
||||
///
|
||||
/// A "chunk" is an implementor-chosen number of tags that are parsed
|
||||
|
@ -2725,27 +2740,6 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_exit_frame(&self, context: &mut UpdateContext<'gc>) {
|
||||
// Attempt to fire an "init" event on our `LoaderInfo`.
|
||||
// This fires after we've exited our first frame, but before
|
||||
// but before we enter a new frame. `loader_stream_init`
|
||||
// keeps track if an "init" event has already been fired,
|
||||
// so this becomes a no-op after the event has been fired.
|
||||
if self.0.read().initialized() {
|
||||
if let Some(loader_info) = self
|
||||
.loader_info()
|
||||
.as_ref()
|
||||
.and_then(|o| o.as_loader_info_object())
|
||||
{
|
||||
loader_info.fire_init_and_complete_events(context, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
for child in self.iter_render_list() {
|
||||
child.on_exit_frame(context);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_self(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
self.0.read().drawing.render(context);
|
||||
self.render_children(context);
|
||||
|
|
|
@ -96,9 +96,6 @@ pub fn run_all_phases_avm2(context: &mut UpdateContext<'_>) {
|
|||
stage.run_frame_scripts(context);
|
||||
|
||||
*context.frame_phase = FramePhase::Exit;
|
||||
Avm2::each_orphan_obj(context, |orphan, context| {
|
||||
orphan.on_exit_frame(context);
|
||||
});
|
||||
stage.exit_frame(context);
|
||||
|
||||
// We cannot easily remove dead `GcWeak` instances from the orphan list
|
||||
|
@ -169,9 +166,6 @@ pub fn run_inner_goto_frame<'gc>(
|
|||
}
|
||||
|
||||
*context.frame_phase = FramePhase::Exit;
|
||||
Avm2::each_orphan_obj(context, |orphan, context| {
|
||||
orphan.on_exit_frame(context);
|
||||
});
|
||||
stage.exit_frame(context);
|
||||
|
||||
// We cannot easily remove dead `GcWeak` instances from the orphan list
|
||||
|
|
|
@ -600,6 +600,30 @@ impl<'gc> LoadManager<'gc> {
|
|||
did_finish
|
||||
}
|
||||
|
||||
pub fn run_exit_frame(context: &mut UpdateContext<'gc>) {
|
||||
// The root movie might not have come from a loader, so check it separately.
|
||||
// `fire_init_and_complete_events` is idempotent, so we unconditionally call it here
|
||||
if let Some(movie) = context
|
||||
.stage
|
||||
.child_by_index(0)
|
||||
.and_then(|o| o.as_movie_clip())
|
||||
{
|
||||
movie.try_fire_loaderinfo_events(context);
|
||||
}
|
||||
let handles: Vec<_> = context.load_manager.0.iter().map(|(h, _)| h).collect();
|
||||
for handle in handles {
|
||||
let Some(Loader::Movie { target_clip, .. }) = context.load_manager.get_loader(handle)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if let Some(movie) = target_clip.as_movie_clip() {
|
||||
if movie.try_fire_loaderinfo_events(context) {
|
||||
context.load_manager.remove_loader(handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Display a dialog allowing a user to select a file
|
||||
///
|
||||
/// Returns a future that will be resolved when a file is selected
|
||||
|
@ -2764,7 +2788,10 @@ impl<'gc> Loader<'gc> {
|
|||
false,
|
||||
);
|
||||
}
|
||||
true
|
||||
// If the movie was loaded from avm1, clean it up now. If a movie (including an AVM1 movie)
|
||||
// was loaded from avm2, clean it up in `run_exit_frame`, after we have a chance to fire
|
||||
// the AVM2-side events
|
||||
matches!(vm_data, MovieLoaderVMData::Avm1 { .. })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue