core: Unify loader event handler and AVM2 data into MovieLoaderVMData

`AVM2Data` was always provided with
`MovieLoaderEventHandler::Avm2LoaderInfo`, so we can unify
them into a single enum.
This commit is contained in:
Aaron Hill 2023-06-07 17:20:59 -05:00
parent 16210cbcf6
commit a387476de7
6 changed files with 131 additions and 134 deletions

View File

@ -10,6 +10,7 @@ use crate::backend::navigator::{NavigationMethod, Request};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayObjectContainer}; use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayObjectContainer};
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32}; use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
use crate::loader::MovieLoaderVMData;
use crate::string::{AvmString, SwfStrExt as _, WStr, WString}; use crate::string::{AvmString, SwfStrExt as _, WStr, WString};
use crate::tag_utils::SwfSlice; use crate::tag_utils::SwfSlice;
use crate::vminterface::Instantiator; use crate::vminterface::Instantiator;
@ -1209,8 +1210,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
level, level,
Request::get(url.to_string()), Request::get(url.to_string()),
None, None,
None, MovieLoaderVMData::Avm1 { broadcaster: None },
None,
); );
self.context.navigator.spawn_future(future); self.context.navigator.spawn_future(future);
} }
@ -1340,8 +1340,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
clip_target, clip_target,
request, request,
None, None,
None, MovieLoaderVMData::Avm1 { broadcaster: None },
None,
); );
self.context.navigator.spawn_future(future); self.context.navigator.spawn_future(future);
} }
@ -1362,8 +1361,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
clip_target, clip_target,
Request::get(url.to_utf8_lossy().into_owned()), Request::get(url.to_utf8_lossy().into_owned()),
None, None,
None, MovieLoaderVMData::Avm1 { broadcaster: None },
None,
); );
self.context.navigator.spawn_future(future); self.context.navigator.spawn_future(future);
} }

View File

@ -1546,8 +1546,7 @@ fn load_movie<'gc>(
DisplayObject::MovieClip(target), DisplayObject::MovieClip(target),
request, request,
None, None,
None, crate::loader::MovieLoaderVMData::Avm1 { broadcaster: None },
None,
); );
activation.context.navigator.spawn_future(future); activation.context.navigator.spawn_future(future);

View File

@ -11,7 +11,7 @@ use crate::avm1::{ArrayObject, Object, Value};
use crate::backend::navigator::Request; use crate::backend::navigator::Request;
use crate::context::GcContext; use crate::context::GcContext;
use crate::display_object::{TDisplayObject, TDisplayObjectContainer}; use crate::display_object::{TDisplayObject, TDisplayObjectContainer};
use crate::loader::MovieLoaderEventHandler; use crate::loader::MovieLoaderVMData;
const PROTO_DECLS: &[Declaration] = declare_properties! { const PROTO_DECLS: &[Declaration] = declare_properties! {
"loadClip" => method(load_clip; DONT_ENUM | DONT_DELETE); "loadClip" => method(load_clip; DONT_ENUM | DONT_DELETE);
@ -65,8 +65,9 @@ fn load_clip<'gc>(
target, target,
Request::get(url.to_utf8_lossy().into_owned()), Request::get(url.to_utf8_lossy().into_owned()),
None, None,
Some(MovieLoaderEventHandler::Avm1Broadcast(this)), MovieLoaderVMData::Avm1 {
None, broadcaster: Some(this),
},
); );
activation.context.navigator.spawn_future(future); activation.context.navigator.spawn_future(future);

View File

@ -15,7 +15,7 @@ use crate::avm2_stub_method;
use crate::backend::navigator::{NavigationMethod, Request}; use crate::backend::navigator::{NavigationMethod, Request};
use crate::display_object::LoaderDisplay; use crate::display_object::LoaderDisplay;
use crate::display_object::MovieClip; use crate::display_object::MovieClip;
use crate::loader::{Avm2LoaderData, MovieLoaderEventHandler}; use crate::loader::MovieLoaderVMData;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use std::sync::Arc; use std::sync::Arc;
@ -87,13 +87,13 @@ pub fn load<'gc>(
content.into(), content.into(),
request, request,
Some(url), Some(url),
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)), MovieLoaderVMData::Avm2 {
Some(Avm2LoaderData { loader_info,
context, context,
default_domain: activation default_domain: activation
.caller_domain() .caller_domain()
.expect("Missing caller domain in Loader.load"), .expect("Missing caller domain in Loader.load"),
}), },
); );
activation.context.navigator.spawn_future(future); activation.context.navigator.spawn_future(future);
} }
@ -218,13 +218,13 @@ pub fn load_bytes<'gc>(
activation.context.player.clone(), activation.context.player.clone(),
content.into(), content.into(),
bytearray.bytes().to_vec(), bytearray.bytes().to_vec(),
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)), MovieLoaderVMData::Avm2 {
Some(Avm2LoaderData { loader_info,
context, context,
default_domain: activation default_domain: activation
.caller_domain() .caller_domain()
.expect("Missing caller domain in Loader.loadBytes"), .expect("Missing caller domain in Loader.loadBytes"),
}), },
); );
activation.context.navigator.spawn_future(future); activation.context.navigator.spawn_future(future);
} }

View File

@ -231,7 +231,7 @@ impl<'gc> LoaderInfoObject<'gc> {
Some(LoaderStream::Swf(_, root)) => root Some(LoaderStream::Swf(_, root)) => root
.as_movie_clip() .as_movie_clip()
.map(|mc| mc.loaded_bytes() >= mc.total_bytes()) .map(|mc| mc.loaded_bytes() >= mc.total_bytes())
.unwrap_or(false), .unwrap_or(true),
_ => false, _ => false,
}; };

View File

@ -260,16 +260,14 @@ impl<'gc> LoadManager<'gc> {
target_clip: DisplayObject<'gc>, target_clip: DisplayObject<'gc>,
request: Request, request: Request,
loader_url: Option<String>, loader_url: Option<String>,
event_handler: Option<MovieLoaderEventHandler<'gc>>, vm_data: MovieLoaderVMData<'gc>,
avm2_data: Option<Avm2LoaderData<'gc>>,
) -> OwnedFuture<(), Error> { ) -> OwnedFuture<(), Error> {
let loader = Loader::Movie { let loader = Loader::Movie {
self_handle: None, self_handle: None,
target_clip, target_clip,
event_handler, vm_data,
loader_status: LoaderStatus::Pending, loader_status: LoaderStatus::Pending,
movie: None, movie: None,
avm2_data,
}; };
let handle = self.add_loader(loader); let handle = self.add_loader(loader);
let loader = self.get_loader_mut(handle).unwrap(); let loader = self.get_loader_mut(handle).unwrap();
@ -284,16 +282,14 @@ impl<'gc> LoadManager<'gc> {
player: Weak<Mutex<Player>>, player: Weak<Mutex<Player>>,
target_clip: DisplayObject<'gc>, target_clip: DisplayObject<'gc>,
bytes: Vec<u8>, bytes: Vec<u8>,
event_handler: Option<MovieLoaderEventHandler<'gc>>, vm_data: MovieLoaderVMData<'gc>,
avm2_data: Option<Avm2LoaderData<'gc>>,
) -> OwnedFuture<(), Error> { ) -> OwnedFuture<(), Error> {
let loader = Loader::Movie { let loader = Loader::Movie {
self_handle: None, self_handle: None,
target_clip, target_clip,
event_handler, vm_data,
loader_status: LoaderStatus::Pending, loader_status: LoaderStatus::Pending,
movie: None, movie: None,
avm2_data,
}; };
let handle = self.add_loader(loader); let handle = self.add_loader(loader);
let loader = self.get_loader_mut(handle).unwrap(); let loader = self.get_loader_mut(handle).unwrap();
@ -472,19 +468,19 @@ pub enum LoaderStatus {
#[derive(Collect, Clone, Copy)] #[derive(Collect, Clone, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
pub enum MovieLoaderEventHandler<'gc> { pub enum MovieLoaderVMData<'gc> {
Avm1Broadcast(Object<'gc>), Avm1 {
Avm2LoaderInfo(Avm2Object<'gc>), broadcaster: Option<Object<'gc>>,
} },
Avm2 {
loader_info: Avm2Object<'gc>,
#[derive(Collect, Clone, Copy)] /// The context of the SWF being loaded.
#[collect(no_drop)] context: Option<Avm2Object<'gc>>,
pub struct Avm2LoaderData<'gc> {
/// The context of the SWF being loaded.
pub context: Option<Avm2Object<'gc>>,
/// The default domain this SWF will use. /// The default domain this SWF will use.
pub default_domain: Avm2Domain<'gc>, default_domain: Avm2Domain<'gc>,
},
} }
/// A struct that holds garbage-collected pointers for asynchronous code. /// A struct that holds garbage-collected pointers for asynchronous code.
@ -507,9 +503,8 @@ pub enum Loader<'gc> {
/// The target movie clip to load the movie into. /// The target movie clip to load the movie into.
target_clip: DisplayObject<'gc>, target_clip: DisplayObject<'gc>,
/// Event broadcaster (typically a `MovieClipLoader`) to fire events // Virtual-machine specific data (AVM1 or AVM2)
/// into. vm_data: MovieLoaderVMData<'gc>,
event_handler: Option<MovieLoaderEventHandler<'gc>>,
/// Indicates the completion status of this loader. /// Indicates the completion status of this loader.
/// ///
@ -527,9 +522,6 @@ pub enum Loader<'gc> {
/// completed and we expect the Player to periodically tick preload /// completed and we expect the Player to periodically tick preload
/// until loading completes. /// until loading completes.
movie: Option<Arc<SwfMovie>>, movie: Option<Arc<SwfMovie>>,
/// AVM2 specific data for this SWF.
avm2_data: Option<Avm2LoaderData<'gc>>,
}, },
/// Loader that is loading form data into an AVM1 object scope. /// Loader that is loading form data into an AVM1 object scope.
@ -1318,26 +1310,28 @@ impl<'gc> Loader<'gc> {
let me = me.unwrap(); let me = me.unwrap();
let (clip, event_handler) = match me { let (clip, vm_data) = match me {
Loader::Movie { Loader::Movie {
target_clip, target_clip,
event_handler, vm_data,
.. ..
} => (*target_clip, *event_handler), } => (*target_clip, *vm_data),
_ => unreachable!(), _ => unreachable!(),
}; };
match event_handler { match vm_data {
Some(MovieLoaderEventHandler::Avm1Broadcast(broadcaster)) => { MovieLoaderVMData::Avm1 { broadcaster } => {
Avm1::run_stack_frame_for_method( if let Some(broadcaster) = broadcaster {
clip, Avm1::run_stack_frame_for_method(
broadcaster, clip,
uc, broadcaster,
"broadcastMessage".into(), uc,
&["onLoadStart".into(), clip.object()], "broadcastMessage".into(),
); &["onLoadStart".into(), clip.object()],
);
}
} }
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) => { MovieLoaderVMData::Avm2 { loader_info, .. } => {
let mut activation = Avm2Activation::from_nothing(uc.reborrow()); let mut activation = Avm2Activation::from_nothing(uc.reborrow());
// Update the LoadersTream - we still have a fake SwfMovie, but we now have the real target clip. // Update the LoadersTream - we still have a fake SwfMovie, but we now have the real target clip.
@ -1356,7 +1350,6 @@ impl<'gc> Loader<'gc> {
let open_evt = Avm2EventObject::bare_default_event(&mut activation.context, "open"); let open_evt = Avm2EventObject::bare_default_event(&mut activation.context, "open");
Avm2::dispatch_event(uc, open_evt, loader_info); Avm2::dispatch_event(uc, open_evt, loader_info);
} }
None => {}
} }
Ok(()) Ok(())
@ -1379,22 +1372,25 @@ impl<'gc> Loader<'gc> {
} }
} }
player.lock().unwrap().update(|uc| { player.lock().unwrap().update(|uc| {
let (clip, event_handler, avm2_data) = match uc.load_manager.get_loader(handle) { let (clip, vm_data) = match uc.load_manager.get_loader(handle) {
Some(Loader::Movie { Some(Loader::Movie {
target_clip, target_clip,
event_handler, vm_data,
avm2_data,
.. ..
}) => (*target_clip, *event_handler, *avm2_data), }) => (*target_clip, *vm_data),
None => return Err(Error::Cancelled), None => return Err(Error::Cancelled),
_ => unreachable!(), _ => unreachable!(),
}; };
let mut activation = Avm2Activation::from_nothing(uc.reborrow()); let mut activation = Avm2Activation::from_nothing(uc.reborrow());
let domain = if let Some(avm2_data) = avm2_data { let domain = if let MovieLoaderVMData::Avm2 {
let domain = avm2_data context,
.context default_domain,
..
} = vm_data
{
let domain = context
.and_then(|o| { .and_then(|o| {
o.get_public_property("applicationDomain", &mut activation) o.get_public_property("applicationDomain", &mut activation)
.ok() .ok()
@ -1402,7 +1398,7 @@ impl<'gc> Loader<'gc> {
.and_then(|v| v.coerce_to_object(&mut activation).ok()) .and_then(|v| v.coerce_to_object(&mut activation).ok())
.and_then(|o| o.as_application_domain()) .and_then(|o| o.as_application_domain())
.unwrap_or_else(|| { .unwrap_or_else(|| {
let parent_domain = avm2_data.default_domain; let parent_domain = default_domain;
Avm2Domain::movie_domain(&mut activation, parent_domain) Avm2Domain::movie_domain(&mut activation, parent_domain)
}); });
Some(domain) Some(domain)
@ -1430,7 +1426,7 @@ impl<'gc> Loader<'gc> {
_ => unreachable!(), _ => unreachable!(),
}; };
if let Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) = event_handler { if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
// Update the LoaderStream - we now have a real SWF movie and a real target clip // Update the LoaderStream - we now have a real SWF movie and a real target clip
loader_info loader_info
.as_loader_info_object() .as_loader_info_object()
@ -1458,9 +1454,7 @@ impl<'gc> Loader<'gc> {
if let Some(mc) = clip.as_movie_clip() { if let Some(mc) = clip.as_movie_clip() {
let loader_info = let loader_info =
if let Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) = if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
event_handler
{
Some(*loader_info.as_loader_info_object().unwrap()) Some(*loader_info.as_loader_info_object().unwrap())
} else { } else {
None None
@ -1570,31 +1564,33 @@ impl<'gc> Loader<'gc> {
let me = me.unwrap(); let me = me.unwrap();
let (clip, event_handler) = match me { let (clip, vm_data) = match me {
Loader::Movie { Loader::Movie {
target_clip, target_clip,
event_handler, vm_data,
.. ..
} => (*target_clip, *event_handler), } => (*target_clip, *vm_data),
_ => unreachable!(), _ => unreachable!(),
}; };
match event_handler { match vm_data {
Some(MovieLoaderEventHandler::Avm1Broadcast(broadcaster)) => { MovieLoaderVMData::Avm1 { broadcaster } => {
Avm1::run_stack_frame_for_method( if let Some(broadcaster) = broadcaster {
clip, Avm1::run_stack_frame_for_method(
broadcaster, clip,
uc, broadcaster,
"broadcastMessage".into(), uc,
&[ "broadcastMessage".into(),
"onLoadProgress".into(), &[
clip.object(), "onLoadProgress".into(),
cur_len.into(), clip.object(),
total_len.into(), cur_len.into(),
], total_len.into(),
); ],
);
}
} }
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) => { MovieLoaderVMData::Avm2 { loader_info, .. } => {
let mut activation = Avm2Activation::from_nothing(uc.reborrow()); let mut activation = Avm2Activation::from_nothing(uc.reborrow());
let progress_evt = activation let progress_evt = activation
@ -1615,7 +1611,6 @@ impl<'gc> Loader<'gc> {
Avm2::dispatch_event(uc, progress_evt, loader_info); Avm2::dispatch_event(uc, progress_evt, loader_info);
} }
None => {}
} }
Ok(()) Ok(())
@ -1627,23 +1622,22 @@ impl<'gc> Loader<'gc> {
uc: &mut UpdateContext<'_, 'gc>, uc: &mut UpdateContext<'_, 'gc>,
dobj: Option<DisplayObject<'gc>>, dobj: Option<DisplayObject<'gc>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let (target_clip, event_handler, movie) = match uc.load_manager.get_loader_mut(handle) { let (target_clip, vm_data, movie) = match uc.load_manager.get_loader_mut(handle) {
Some(Loader::Movie { Some(Loader::Movie {
target_clip, target_clip,
movie, movie,
event_handler, vm_data,
.. ..
}) => (*target_clip, *event_handler, movie.clone()), }) => (*target_clip, *vm_data, movie.clone()),
None => return Err(Error::Cancelled), None => return Err(Error::Cancelled),
_ => unreachable!(), _ => unreachable!(),
}; };
let loader_info = let loader_info = if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
if let Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) = event_handler { Some(*loader_info.as_loader_info_object().unwrap())
Some(*loader_info.as_loader_info_object().unwrap()) } else {
} else { None
None };
};
if let Some(loader_info) = loader_info { if let Some(loader_info) = loader_info {
// Store the real movie into the `LoaderStream`, so that // Store the real movie into the `LoaderStream`, so that
@ -1673,7 +1667,7 @@ impl<'gc> Loader<'gc> {
} }
} }
if let Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) = event_handler { if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
let domain = uc let domain = uc
.library .library
.library_for_movie(movie.unwrap()) .library_for_movie(movie.unwrap())
@ -1707,23 +1701,25 @@ impl<'gc> Loader<'gc> {
} }
} }
match event_handler { match vm_data {
Some(MovieLoaderEventHandler::Avm1Broadcast(broadcaster)) => { MovieLoaderVMData::Avm1 { broadcaster } => {
Avm1::run_stack_frame_for_method( if let Some(broadcaster) = broadcaster {
target_clip, Avm1::run_stack_frame_for_method(
broadcaster, target_clip,
uc, broadcaster,
"broadcastMessage".into(), uc,
// TODO: Pass an actual httpStatus argument instead of 0. "broadcastMessage".into(),
&["onLoadComplete".into(), target_clip.object(), 0.into()], // TODO: Pass an actual httpStatus argument instead of 0.
); &["onLoadComplete".into(), target_clip.object(), 0.into()],
);
}
} }
// This is fired after we process the movie's first frame, // This is fired after we process the movie's first frame,
// in `MovieClip.on_exit_frame` // in `MovieClip.on_exit_frame`
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) => { MovieLoaderVMData::Avm2 { loader_info, .. } => {
let loader_info_obj = loader_info.as_loader_info_object().unwrap(); let loader_info_obj = loader_info.as_loader_info_object().unwrap();
loader_info_obj.set_loader_stream( loader_info_obj.set_loader_stream(
LoaderStream::Swf(target_clip.as_movie_clip().unwrap().movie(), target_clip), LoaderStream::Swf(target_clip.as_movie_clip().unwrap().movie(), dobj.unwrap()),
uc.gc_context, uc.gc_context,
); );
@ -1733,7 +1729,6 @@ impl<'gc> Loader<'gc> {
} }
} }
} }
None => {}
} }
if let Loader::Movie { loader_status, .. } = uc.load_manager.get_loader_mut(handle).unwrap() if let Loader::Movie { loader_status, .. } = uc.load_manager.get_loader_mut(handle).unwrap()
@ -1758,31 +1753,33 @@ impl<'gc> Loader<'gc> {
//error types we can actually inspect. //error types we can actually inspect.
//This also can get errors from decoding an invalid SWF file, //This also can get errors from decoding an invalid SWF file,
//too. We should distinguish those to player code. //too. We should distinguish those to player code.
let (clip, event_handler) = match uc.load_manager.get_loader_mut(handle) { let (clip, vm_data) = match uc.load_manager.get_loader_mut(handle) {
Some(Loader::Movie { Some(Loader::Movie {
target_clip, target_clip,
event_handler, vm_data,
.. ..
}) => (*target_clip, *event_handler), }) => (*target_clip, *vm_data),
None => return Err(Error::Cancelled), None => return Err(Error::Cancelled),
_ => unreachable!(), _ => unreachable!(),
}; };
match event_handler { match vm_data {
Some(MovieLoaderEventHandler::Avm1Broadcast(broadcaster)) => { MovieLoaderVMData::Avm1 { broadcaster } => {
Avm1::run_stack_frame_for_method( if let Some(broadcaster) = broadcaster {
clip, Avm1::run_stack_frame_for_method(
broadcaster, clip,
uc, broadcaster,
"broadcastMessage".into(), uc,
&[ "broadcastMessage".into(),
"onLoadError".into(), &[
clip.object(), "onLoadError".into(),
"LoadNeverCompleted".into(), clip.object(),
], "LoadNeverCompleted".into(),
); ],
);
}
} }
Some(MovieLoaderEventHandler::Avm2LoaderInfo(loader_info)) => { MovieLoaderVMData::Avm2 { loader_info, .. } => {
let mut activation = Avm2Activation::from_nothing(uc.reborrow()); let mut activation = Avm2Activation::from_nothing(uc.reborrow());
// FIXME - Match the exact error message generated by Flash // FIXME - Match the exact error message generated by Flash
@ -1802,7 +1799,6 @@ impl<'gc> Loader<'gc> {
Avm2::dispatch_event(uc, io_error_evt, loader_info); Avm2::dispatch_event(uc, io_error_evt, loader_info);
} }
None => {}
} }
if let Loader::Movie { loader_status, .. } = uc.load_manager.get_loader_mut(handle).unwrap() if let Loader::Movie { loader_status, .. } = uc.load_manager.get_loader_mut(handle).unwrap()
@ -1819,13 +1815,13 @@ impl<'gc> Loader<'gc> {
/// ///
/// Used to fire listener events on clips and terminate completed loaders. /// Used to fire listener events on clips and terminate completed loaders.
fn movie_clip_loaded(&mut self, queue: &mut ActionQueue<'gc>) -> bool { fn movie_clip_loaded(&mut self, queue: &mut ActionQueue<'gc>) -> bool {
let (clip, event_handler, loader_status) = match self { let (clip, vm_data, loader_status) = match self {
Loader::Movie { Loader::Movie {
target_clip, target_clip,
event_handler, vm_data,
loader_status, loader_status,
.. ..
} => (*target_clip, *event_handler, *loader_status), } => (*target_clip, *vm_data, *loader_status),
_ => return false, _ => return false,
}; };
@ -1835,7 +1831,10 @@ impl<'gc> Loader<'gc> {
LoaderStatus::Failed => true, LoaderStatus::Failed => true,
LoaderStatus::Succeeded => { LoaderStatus::Succeeded => {
// AVM2 is handled separately // AVM2 is handled separately
if let Some(MovieLoaderEventHandler::Avm1Broadcast(broadcaster)) = event_handler { if let MovieLoaderVMData::Avm1 {
broadcaster: Some(broadcaster),
} = vm_data
{
queue.queue_action( queue.queue_action(
clip, clip,
ActionType::Method { ActionType::Method {