diff --git a/core/src/avm2/object/loaderinfo_object.rs b/core/src/avm2/object/loaderinfo_object.rs index fffa207a5..57f0e6f6e 100644 --- a/core/src/avm2/object/loaderinfo_object.rs +++ b/core/src/avm2/object/loaderinfo_object.rs @@ -8,7 +8,7 @@ use crate::avm2::Avm2; use crate::avm2::Error; use crate::avm2::EventObject; use crate::context::UpdateContext; -use crate::display_object::DisplayObject; +use crate::display_object::{DisplayObject, TDisplayObject}; use crate::tag_utils::SwfMovie; use gc_arena::{Collect, GcCell, MutationContext}; use std::cell::{Ref, RefMut}; @@ -27,7 +27,8 @@ pub fn loaderinfo_allocator<'gc>( base, loaded_stream: None, loader: None, - events_fired: false, + init_event_fired: false, + complete_event_fired: false, shared_events: activation .context .avm2 @@ -75,8 +76,11 @@ pub struct LoaderInfoObjectData<'gc> { loader: Option>, - /// Whether or not we've fired our 'init' and 'complete' events - events_fired: bool, + /// Whether or not we've fired our 'init' event + init_event_fired: bool, + + /// Whether or not we've fired our 'complete' event + complete_event_fired: bool, /// The `EventDispatcher` used for `LoaderInfo.sharedEvents`. // FIXME: If we ever implement sandboxing, then ensure that we allow @@ -102,7 +106,8 @@ impl<'gc> LoaderInfoObject<'gc> { base, loaded_stream, loader, - events_fired: false, + init_event_fired: false, + complete_event_fired: false, shared_events: activation .context .avm2 @@ -138,7 +143,8 @@ impl<'gc> LoaderInfoObject<'gc> { base, loaded_stream: Some(LoaderStream::NotYetLoaded(movie, root_clip)), loader, - events_fired: false, + init_event_fired: false, + complete_event_fired: false, shared_events: activation .context .avm2 @@ -164,8 +170,8 @@ impl<'gc> LoaderInfoObject<'gc> { } pub fn fire_init_and_complete_events(&self, context: &mut UpdateContext<'_, 'gc, '_>) { - if !self.0.read().events_fired { - self.0.write(context.gc_context).events_fired = true; + if !self.0.read().init_event_fired { + self.0.write(context.gc_context).init_event_fired = true; // TODO - 'init' should be fired earlier during the download. // Right now, we fire it when downloading is fully completed. @@ -177,14 +183,29 @@ impl<'gc> LoaderInfoObject<'gc> { e ); } + } - let complete_evt = EventObject::bare_default_event(context, "complete"); + if !self.0.read().complete_event_fired { + // NOTE: We have to check load progress here because this function + // is called unconditionally at the end of every frame. + let should_complete = match self.0.read().loaded_stream { + Some(LoaderStream::Swf(_, root)) => root + .as_movie_clip() + .map(|mc| mc.loaded_bytes() >= mc.total_bytes()) + .unwrap_or(false), + _ => false, + }; - if let Err(e) = Avm2::dispatch_event(context, complete_evt, (*self).into()) { - log::error!( - "Encountered AVM2 error when broadcasting `complete` event: {}", - e - ); + if should_complete { + self.0.write(context.gc_context).complete_event_fired = true; + let complete_evt = EventObject::bare_default_event(context, "complete"); + + if let Err(e) = Avm2::dispatch_event(context, complete_evt, (*self).into()) { + log::error!( + "Encountered AVM2 error when broadcasting `complete` event: {}", + e + ); + } } } }