avm2: Run most of Loader.loadBytes immediately
This requires moving `set_root_movie` into `UpdateContext`. Now, we preload the entire movieclip immediately - Flash Player does this regardless of the size of the SWF. The 'Loader::load_complete' is delayed to the end of the frame (which is when the root class is constructed for the loaded clip).
This commit is contained in:
parent
b5f28f6caa
commit
6c420fa5d5
|
@ -16,6 +16,7 @@ use crate::avm2_stub_method;
|
|||
use crate::backend::navigator::{NavigationMethod, Request};
|
||||
use crate::display_object::LoaderDisplay;
|
||||
use crate::display_object::MovieClip;
|
||||
use crate::loader::LoadManager;
|
||||
use crate::loader::MovieLoaderVMData;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use std::sync::Arc;
|
||||
|
@ -227,19 +228,24 @@ pub fn load_bytes<'gc>(
|
|||
.as_object()
|
||||
.unwrap();
|
||||
|
||||
let future = activation.context.load_manager.load_movie_into_clip_bytes(
|
||||
activation.context.player.clone(),
|
||||
let default_domain = activation
|
||||
.caller_domain()
|
||||
.expect("Missing caller domain in Loader.loadBytes");
|
||||
|
||||
if let Err(e) = LoadManager::load_movie_into_clip_bytes(
|
||||
&mut activation.context,
|
||||
content.into(),
|
||||
bytearray.bytes().to_vec(),
|
||||
MovieLoaderVMData::Avm2 {
|
||||
loader_info,
|
||||
context,
|
||||
default_domain: activation
|
||||
.caller_domain()
|
||||
.expect("Missing caller domain in Loader.loadBytes"),
|
||||
default_domain,
|
||||
},
|
||||
);
|
||||
activation.context.navigator.spawn_future(future);
|
||||
) {
|
||||
return Err(Error::RustError(
|
||||
format!("Error in Loader.loadBytes: {e:?}").into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ pub fn get_domain<'gc>(
|
|||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let movie = activation.context.swf;
|
||||
let movie = &activation.context.swf;
|
||||
|
||||
let domain = if let Ok(url) = url::Url::parse(movie.url()) {
|
||||
if url.scheme() == "file" {
|
||||
|
|
|
@ -9,7 +9,7 @@ pub fn get_domain<'gc>(
|
|||
_this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let movie = activation.context.swf;
|
||||
let movie = &activation.context.swf;
|
||||
|
||||
let domain = if let Ok(url) = url::Url::parse(movie.url()) {
|
||||
if url.scheme() == "file" {
|
||||
|
|
|
@ -236,33 +236,37 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
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() as i32 >= mc.total_bytes())
|
||||
.unwrap_or(true),
|
||||
_ => false,
|
||||
let (should_complete, from_url) = match self.0.read().loaded_stream {
|
||||
Some(LoaderStream::Swf(ref movie, root)) => (
|
||||
root.as_movie_clip()
|
||||
.map(|mc| mc.loaded_bytes() as i32 >= mc.total_bytes())
|
||||
.unwrap_or(true),
|
||||
movie.loader_url().is_some(),
|
||||
),
|
||||
_ => (false, false),
|
||||
};
|
||||
|
||||
if should_complete {
|
||||
let mut activation = Activation::from_nothing(context.reborrow());
|
||||
let http_status_evt = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.httpstatusevent
|
||||
.construct(
|
||||
&mut activation,
|
||||
&[
|
||||
"httpStatus".into(),
|
||||
false.into(),
|
||||
false.into(),
|
||||
status.into(),
|
||||
redirected.into(),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
if from_url {
|
||||
let http_status_evt = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.httpstatusevent
|
||||
.construct(
|
||||
&mut activation,
|
||||
&[
|
||||
"httpStatus".into(),
|
||||
false.into(),
|
||||
false.into(),
|
||||
status.into(),
|
||||
redirected.into(),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Avm2::dispatch_event(context, http_status_evt, (*self).into());
|
||||
Avm2::dispatch_event(context, http_status_evt, (*self).into());
|
||||
}
|
||||
|
||||
self.0.write(context.gc_context).complete_event_fired = true;
|
||||
let complete_evt = EventObject::bare_default_event(context, "complete");
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
//! Contexts and helper types passed between functions.
|
||||
|
||||
use crate::avm1::Activation;
|
||||
use crate::avm1::ActivationIdentifier;
|
||||
use crate::avm1::Attribute;
|
||||
use crate::avm1::Avm1;
|
||||
use crate::avm1::ScriptObject;
|
||||
use crate::avm1::SystemProperties;
|
||||
use crate::avm1::TObject;
|
||||
use crate::avm1::{Object as Avm1Object, Value as Avm1Value};
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm2::object::LoaderInfoObject;
|
||||
use crate::avm2::Activation as Avm2Activation;
|
||||
use crate::avm2::{Avm2, Object as Avm2Object, SoundChannelObject};
|
||||
use crate::backend::{
|
||||
audio::{AudioBackend, AudioManager, SoundHandle, SoundInstanceHandle},
|
||||
|
@ -24,10 +32,12 @@ use crate::player::Player;
|
|||
use crate::prelude::*;
|
||||
use crate::socket::Sockets;
|
||||
use crate::streams::StreamManager;
|
||||
use crate::string::AvmString;
|
||||
use crate::string::AvmStringInterner;
|
||||
use crate::stub::StubCollection;
|
||||
use crate::tag_utils::{SwfMovie, SwfSlice};
|
||||
use crate::timer::Timers;
|
||||
use crate::vminterface::Instantiator;
|
||||
use core::fmt;
|
||||
use gc_arena::{Collect, Mutation};
|
||||
use rand::rngs::SmallRng;
|
||||
|
@ -101,7 +111,7 @@ pub struct UpdateContext<'a, 'gc> {
|
|||
pub needs_render: &'a mut bool,
|
||||
|
||||
/// The root SWF file.
|
||||
pub swf: &'a Arc<SwfMovie>,
|
||||
pub swf: &'a mut Arc<SwfMovie>,
|
||||
|
||||
/// The audio backend, used by display objects and AVM to play audio.
|
||||
pub audio: &'a mut dyn AudioBackend,
|
||||
|
@ -238,6 +248,11 @@ pub struct UpdateContext<'a, 'gc> {
|
|||
|
||||
/// Dynamic root for allowing handles to GC objects to exist outside of the GC.
|
||||
pub dynamic_root: gc_arena::DynamicRootSet<'gc>,
|
||||
|
||||
/// These functions are run at the end of each frame execution.
|
||||
/// Currently, this is just used for handling `Loader.loadBytes`
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub post_frame_callbacks: &'a mut Vec<Box<dyn FnOnce(&mut UpdateContext<'_, '_>)>>,
|
||||
}
|
||||
|
||||
/// Convenience methods for controlling audio.
|
||||
|
@ -326,6 +341,112 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
|||
pub fn set_sound_transforms_dirty(&mut self) {
|
||||
self.audio_manager.set_sound_transforms_dirty()
|
||||
}
|
||||
|
||||
/// Change the root movie.
|
||||
///
|
||||
/// This should only be called once, as it makes no attempt at removing
|
||||
/// previous stage contents. If you need to load a new root movie, you
|
||||
/// should destroy and recreate the player instance.
|
||||
pub fn set_root_movie(&mut self, movie: SwfMovie) {
|
||||
if !self.forced_frame_rate {
|
||||
*self.frame_rate = movie.frame_rate().into();
|
||||
}
|
||||
|
||||
info!(
|
||||
"Loaded SWF version {}, resolution {}x{} @ {} FPS",
|
||||
movie.version(),
|
||||
movie.width(),
|
||||
movie.height(),
|
||||
self.frame_rate,
|
||||
);
|
||||
|
||||
*self.swf = Arc::new(movie);
|
||||
*self.instance_counter = 0;
|
||||
|
||||
if self.swf.is_action_script_3() {
|
||||
self.avm2.root_api_version =
|
||||
ApiVersion::from_swf_version(self.swf.version(), self.avm2.player_runtime)
|
||||
.unwrap_or_else(|| panic!("Unknown SWF version {}", self.swf.version()));
|
||||
}
|
||||
|
||||
self.stage.set_movie_size(
|
||||
self.gc_context,
|
||||
self.swf.width().to_pixels() as u32,
|
||||
self.swf.height().to_pixels() as u32,
|
||||
);
|
||||
self.stage.set_movie(self.gc_context, self.swf.clone());
|
||||
|
||||
let stage_domain = self.avm2.stage_domain();
|
||||
let mut activation = Avm2Activation::from_domain(self.reborrow(), stage_domain);
|
||||
|
||||
activation
|
||||
.context
|
||||
.library
|
||||
.library_for_movie_mut(activation.context.swf.clone())
|
||||
.set_avm2_domain(stage_domain);
|
||||
activation.context.ui.set_mouse_visible(true);
|
||||
|
||||
let swf = activation.context.swf.clone();
|
||||
let root: DisplayObject = MovieClip::player_root_movie(&mut activation, swf.clone()).into();
|
||||
|
||||
// The Stage `LoaderInfo` is permanently in the 'not yet loaded' state,
|
||||
// and has no associated `Loader` instance.
|
||||
// However, some properties are always accessible, and take their values
|
||||
// from the root SWF.
|
||||
let stage_loader_info =
|
||||
LoaderInfoObject::not_yet_loaded(&mut activation, swf, None, Some(root), true)
|
||||
.expect("Failed to construct Stage LoaderInfo");
|
||||
activation
|
||||
.context
|
||||
.stage
|
||||
.set_loader_info(activation.context.gc_context, stage_loader_info);
|
||||
|
||||
drop(activation);
|
||||
|
||||
root.set_depth(self.gc_context, 0);
|
||||
let flashvars = if !self.swf.parameters().is_empty() {
|
||||
let object = ScriptObject::new(self.gc_context, None);
|
||||
for (key, value) in self.swf.parameters().iter() {
|
||||
object.define_value(
|
||||
self.gc_context,
|
||||
AvmString::new_utf8(self.gc_context, key),
|
||||
AvmString::new_utf8(self.gc_context, value).into(),
|
||||
Attribute::empty(),
|
||||
);
|
||||
}
|
||||
Some(object.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
root.post_instantiation(self, flashvars, Instantiator::Movie, false);
|
||||
root.set_default_root_name(self);
|
||||
self.stage.replace_at_depth(self, root, 0);
|
||||
|
||||
// Set the version parameter on the root.
|
||||
let mut activation = Activation::from_stub(
|
||||
self.reborrow(),
|
||||
ActivationIdentifier::root("[Version Setter]"),
|
||||
);
|
||||
let object = root.object().coerce_to_object(&mut activation);
|
||||
let version_string = activation
|
||||
.context
|
||||
.system
|
||||
.get_version_string(activation.context.avm1);
|
||||
object.define_value(
|
||||
activation.context.gc_context,
|
||||
"$version",
|
||||
AvmString::new_utf8(activation.context.gc_context, version_string).into(),
|
||||
Attribute::empty(),
|
||||
);
|
||||
|
||||
let stage = activation.context.stage;
|
||||
stage.build_matrices(&mut activation.context);
|
||||
|
||||
drop(activation);
|
||||
|
||||
self.audio.set_frame_rate(*self.frame_rate);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
||||
|
@ -400,6 +521,7 @@ impl<'a, 'gc> UpdateContext<'a, 'gc> {
|
|||
net_connections: self.net_connections,
|
||||
local_connections: self.local_connections,
|
||||
dynamic_root: self.dynamic_root,
|
||||
post_frame_callbacks: self.post_frame_callbacks,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ use crate::streams::NetStream;
|
|||
use crate::string::AvmString;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use crate::vminterface::Instantiator;
|
||||
use crate::{avm2_stub_method, avm2_stub_method_context};
|
||||
use encoding_rs::UTF_8;
|
||||
use gc_arena::{Collect, GcCell};
|
||||
use generational_arena::{Arena, Index};
|
||||
|
@ -305,6 +306,7 @@ impl<'gc> LoadManager<'gc> {
|
|||
target_clip,
|
||||
vm_data,
|
||||
loader_status: LoaderStatus::Pending,
|
||||
from_bytes: false,
|
||||
movie: None,
|
||||
};
|
||||
let handle = self.add_loader(loader);
|
||||
|
@ -316,22 +318,21 @@ impl<'gc> LoadManager<'gc> {
|
|||
///
|
||||
/// Returns the loader's async process, which you will need to spawn.
|
||||
pub fn load_movie_into_clip_bytes(
|
||||
&mut self,
|
||||
player: Weak<Mutex<Player>>,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
target_clip: DisplayObject<'gc>,
|
||||
bytes: Vec<u8>,
|
||||
vm_data: MovieLoaderVMData<'gc>,
|
||||
) -> OwnedFuture<(), Error> {
|
||||
) -> Result<(), Error> {
|
||||
let loader = Loader::Movie {
|
||||
self_handle: None,
|
||||
target_clip,
|
||||
vm_data,
|
||||
loader_status: LoaderStatus::Pending,
|
||||
movie: None,
|
||||
from_bytes: true,
|
||||
};
|
||||
let handle = self.add_loader(loader);
|
||||
let loader = self.get_loader_mut(handle).unwrap();
|
||||
loader.movie_loader_bytes(player, bytes)
|
||||
let handle = context.load_manager.add_loader(loader);
|
||||
Loader::movie_loader_bytes(handle, context, bytes)
|
||||
}
|
||||
|
||||
/// Fires the `onLoad` listener event for every MovieClip that has been
|
||||
|
@ -621,6 +622,9 @@ pub enum Loader<'gc> {
|
|||
/// completed and we expect the Player to periodically tick preload
|
||||
/// until loading completes.
|
||||
movie: Option<Arc<SwfMovie>>,
|
||||
|
||||
/// Whether or not this was loaded as a result of a `Loader.loadBytes` call
|
||||
from_bytes: bool,
|
||||
},
|
||||
|
||||
/// Loader that is loading form data into an AVM1 object scope.
|
||||
|
@ -745,13 +749,21 @@ impl<'gc> Loader<'gc> {
|
|||
) -> Result<bool, Error> {
|
||||
let mc = match context.load_manager.get_loader_mut(handle) {
|
||||
Some(Self::Movie {
|
||||
target_clip, movie, ..
|
||||
target_clip,
|
||||
movie,
|
||||
from_bytes,
|
||||
..
|
||||
}) => {
|
||||
if movie.is_none() {
|
||||
//Non-SWF load or file not loaded yet
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Loader.loadBytes movies never participate in preloading
|
||||
if *from_bytes {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if target_clip.as_movie_clip().is_none() {
|
||||
// Non-movie-clip loads should not be handled in preload_tick
|
||||
tracing::error!("Cannot preload non-movie-clip loader");
|
||||
|
@ -829,7 +841,9 @@ impl<'gc> Loader<'gc> {
|
|||
let mut movie = SwfMovie::from_data(&response.body, spoofed_or_swf_url, None)?;
|
||||
on_metadata(movie.header());
|
||||
movie.append_parameters(parameters);
|
||||
player.lock().unwrap().set_root_movie(movie);
|
||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||
uc.set_root_movie(movie);
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -893,19 +907,23 @@ impl<'gc> Loader<'gc> {
|
|||
ContentType::sniff(&response.body).expect(ContentType::Swf)?;
|
||||
|
||||
let movie = SwfMovie::from_data(&response.body, response.url, loader_url)?;
|
||||
player.lock().unwrap().set_root_movie(movie);
|
||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||
uc.set_root_movie(movie);
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
Ok(response) => {
|
||||
Loader::movie_loader_data(
|
||||
handle,
|
||||
player,
|
||||
&response.body,
|
||||
response.url,
|
||||
response.status,
|
||||
response.redirected,
|
||||
loader_url,
|
||||
)?;
|
||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||
Loader::movie_loader_data(
|
||||
handle,
|
||||
uc,
|
||||
&response.body,
|
||||
response.url,
|
||||
response.status,
|
||||
response.redirected,
|
||||
loader_url,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
Err(response) => {
|
||||
tracing::error!(
|
||||
|
@ -939,57 +957,45 @@ impl<'gc> Loader<'gc> {
|
|||
})
|
||||
}
|
||||
|
||||
fn movie_loader_bytes(
|
||||
&mut self,
|
||||
player: Weak<Mutex<Player>>,
|
||||
pub fn movie_loader_bytes(
|
||||
handle: Handle,
|
||||
uc: &mut UpdateContext<'_, 'gc>,
|
||||
bytes: Vec<u8>,
|
||||
) -> OwnedFuture<(), Error> {
|
||||
let handle = match self {
|
||||
Loader::Movie { self_handle, .. } => self_handle.expect("Loader not self-introduced"),
|
||||
_ => return Box::pin(async { Err(Error::NotMovieLoader) }),
|
||||
) -> Result<(), Error> {
|
||||
let clip = match uc.load_manager.get_loader(handle) {
|
||||
Some(Loader::Movie { target_clip, .. }) => *target_clip,
|
||||
None => return Err(Error::Cancelled),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let player = player
|
||||
.upgrade()
|
||||
.expect("Could not upgrade weak reference to player");
|
||||
let replacing_root_movie = uc
|
||||
.stage
|
||||
.root_clip()
|
||||
.map(|root| DisplayObject::ptr_eq(clip, root))
|
||||
.unwrap_or(false);
|
||||
|
||||
Box::pin(async move {
|
||||
let mut replacing_root_movie = false;
|
||||
player.lock().unwrap().update(|uc| -> Result<(), Error> {
|
||||
let clip = match uc.load_manager.get_loader(handle) {
|
||||
Some(Loader::Movie { target_clip, .. }) => *target_clip,
|
||||
None => return Err(Error::Cancelled),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
replacing_root_movie = uc
|
||||
.stage
|
||||
.root_clip()
|
||||
.map(|root| DisplayObject::ptr_eq(clip, root))
|
||||
.unwrap_or(false);
|
||||
|
||||
if let Some(mc) = clip.as_movie_clip() {
|
||||
if !mc.movie().is_action_script_3() {
|
||||
mc.avm1_unload(uc);
|
||||
}
|
||||
mc.replace_with_movie(uc, None, false, None);
|
||||
}
|
||||
|
||||
// NOTE: We do NOT call `movie_loader_start` as `loadBytes` does
|
||||
// not emit `open`
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if replacing_root_movie {
|
||||
ContentType::sniff(&bytes).expect(ContentType::Swf)?;
|
||||
|
||||
let movie = SwfMovie::from_data(&bytes, "file:///".into(), None)?;
|
||||
player.lock().unwrap().set_root_movie(movie);
|
||||
return Ok(());
|
||||
if let Some(mc) = clip.as_movie_clip() {
|
||||
if !mc.movie().is_action_script_3() {
|
||||
mc.avm1_unload(uc);
|
||||
}
|
||||
mc.replace_with_movie(uc, None, false, None);
|
||||
}
|
||||
|
||||
Loader::movie_loader_data(handle, player, &bytes, "file:///".into(), 0, false, None)
|
||||
})
|
||||
if replacing_root_movie {
|
||||
ContentType::sniff(&bytes).expect(ContentType::Swf)?;
|
||||
|
||||
let movie = SwfMovie::from_data(&bytes, "file:///".into(), None)?;
|
||||
avm2_stub_method_context!(
|
||||
uc,
|
||||
"flash.display.Loader",
|
||||
"loadBytes",
|
||||
"replacing root movie"
|
||||
);
|
||||
uc.set_root_movie(movie);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Loader::movie_loader_data(handle, uc, &bytes, "file:///".into(), 0, false, None)
|
||||
}
|
||||
|
||||
fn form_loader(
|
||||
|
@ -1593,7 +1599,7 @@ impl<'gc> Loader<'gc> {
|
|||
/// Load data into a movie loader.
|
||||
fn movie_loader_data(
|
||||
handle: Handle,
|
||||
player: Arc<Mutex<Player>>,
|
||||
uc: &mut UpdateContext<'_, 'gc>,
|
||||
data: &[u8],
|
||||
url: String,
|
||||
status: u16,
|
||||
|
@ -1606,143 +1612,172 @@ impl<'gc> Loader<'gc> {
|
|||
if sniffed_type == ContentType::Unknown {
|
||||
if let Ok(data) = extract_swz(data) {
|
||||
return Self::movie_loader_data(
|
||||
handle, player, &data, url, status, redirected, loader_url,
|
||||
handle, uc, &data, url, status, redirected, loader_url,
|
||||
);
|
||||
}
|
||||
}
|
||||
player.lock().unwrap().update(|uc| {
|
||||
let (clip, vm_data) = match uc.load_manager.get_loader(handle) {
|
||||
Some(Loader::Movie {
|
||||
target_clip,
|
||||
vm_data,
|
||||
..
|
||||
}) => (*target_clip, *vm_data),
|
||||
None => return Err(Error::Cancelled),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
||||
|
||||
let domain = if let MovieLoaderVMData::Avm2 {
|
||||
context,
|
||||
default_domain,
|
||||
let (clip, vm_data, from_bytes) = match uc.load_manager.get_loader(handle) {
|
||||
Some(Loader::Movie {
|
||||
target_clip,
|
||||
vm_data,
|
||||
from_bytes,
|
||||
..
|
||||
} = vm_data
|
||||
{
|
||||
let domain = context
|
||||
.and_then(|o| {
|
||||
o.get_public_property("applicationDomain", &mut activation)
|
||||
.ok()
|
||||
})
|
||||
.and_then(|v| v.coerce_to_object(&mut activation).ok())
|
||||
.and_then(|o| o.as_application_domain())
|
||||
.unwrap_or_else(|| {
|
||||
let parent_domain = default_domain;
|
||||
Avm2Domain::movie_domain(&mut activation, parent_domain)
|
||||
});
|
||||
domain
|
||||
} else {
|
||||
// This is necessary when the MovieLoaderData is AVM1,
|
||||
// but loaded an AVM2 SWF (mixed AVM).
|
||||
activation.context.avm2.stage_domain()
|
||||
};
|
||||
}) => (*target_clip, *vm_data, *from_bytes),
|
||||
None => return Err(Error::Cancelled),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let movie = match sniffed_type {
|
||||
ContentType::Swf => Arc::new(SwfMovie::from_data(data, url.clone(), loader_url)?),
|
||||
ContentType::Gif | ContentType::Jpeg | ContentType::Png => {
|
||||
Arc::new(SwfMovie::from_loaded_image(url.clone(), length))
|
||||
}
|
||||
ContentType::Unknown => Arc::new(SwfMovie::error_movie(url.clone())),
|
||||
};
|
||||
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
||||
|
||||
match activation.context.load_manager.get_loader_mut(handle) {
|
||||
Some(Loader::Movie {
|
||||
movie: old,
|
||||
loader_status,
|
||||
..
|
||||
}) => {
|
||||
*loader_status = LoaderStatus::Parsing;
|
||||
*old = Some(movie.clone())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let domain = if let MovieLoaderVMData::Avm2 {
|
||||
context,
|
||||
default_domain,
|
||||
..
|
||||
} = vm_data
|
||||
{
|
||||
let domain = context
|
||||
.and_then(|o| {
|
||||
o.get_public_property("applicationDomain", &mut activation)
|
||||
.ok()
|
||||
})
|
||||
.and_then(|v| v.coerce_to_object(&mut activation).ok())
|
||||
.and_then(|o| o.as_application_domain())
|
||||
.unwrap_or_else(|| {
|
||||
let parent_domain = default_domain;
|
||||
Avm2Domain::movie_domain(&mut activation, parent_domain)
|
||||
});
|
||||
domain
|
||||
} else {
|
||||
// This is necessary when the MovieLoaderData is AVM1,
|
||||
// but loaded an AVM2 SWF (mixed AVM).
|
||||
activation.context.avm2.stage_domain()
|
||||
};
|
||||
|
||||
if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_content_type(sniffed_type, activation.context.gc_context);
|
||||
let fake_movie = Arc::new(SwfMovie::empty_fake_compressed_len(
|
||||
activation.context.swf.version(),
|
||||
length,
|
||||
));
|
||||
|
||||
// Expose 'bytesTotal' (via the fake movie) during the first 'progress' event,
|
||||
// but nothing else (in particular, the `parameters` and `url` properties are not set
|
||||
// to their real values)
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_loader_stream(
|
||||
LoaderStream::NotYetLoaded(fake_movie, Some(clip), false),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
// Flash always fires an initial 'progress' event with
|
||||
// bytesLoaded=0 and bytesTotal set to the proper value.
|
||||
// This only seems to happen for an AVM2 event handler
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, 0, length)?;
|
||||
|
||||
// Update the LoaderStream - we now have a real SWF movie and a real target clip
|
||||
// This is intentionally set *after* the first 'progress' event, to match Flash's behavior
|
||||
// (`LoaderInfo.parameters` is always empty during the first 'progress' event)
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_loader_stream(
|
||||
LoaderStream::NotYetLoaded(movie.clone(), Some(clip), false),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
let movie = match sniffed_type {
|
||||
ContentType::Swf => {
|
||||
Arc::new(SwfMovie::from_data(data, url.clone(), loader_url.clone())?)
|
||||
}
|
||||
ContentType::Gif | ContentType::Jpeg | ContentType::Png => {
|
||||
Arc::new(SwfMovie::from_loaded_image(url.clone(), length))
|
||||
}
|
||||
ContentType::Unknown => Arc::new(SwfMovie::error_movie(url.clone())),
|
||||
};
|
||||
|
||||
match sniffed_type {
|
||||
ContentType::Swf => {
|
||||
let library = activation
|
||||
.context
|
||||
.library
|
||||
.library_for_movie_mut(movie.clone());
|
||||
match activation.context.load_manager.get_loader_mut(handle) {
|
||||
Some(Loader::Movie {
|
||||
movie: old,
|
||||
loader_status,
|
||||
..
|
||||
}) => {
|
||||
*loader_status = LoaderStatus::Parsing;
|
||||
*old = Some(movie.clone())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
library.set_avm2_domain(domain);
|
||||
if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_content_type(sniffed_type, activation.context.gc_context);
|
||||
let fake_movie = Arc::new(SwfMovie::empty_fake_compressed_len(
|
||||
activation.context.swf.version(),
|
||||
length,
|
||||
));
|
||||
|
||||
if let Some(mc) = clip.as_movie_clip() {
|
||||
let loader_info =
|
||||
if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
|
||||
Some(*loader_info.as_loader_info_object().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Expose 'bytesTotal' (via the fake movie) during the first 'progress' event,
|
||||
// but nothing else (in particular, the `parameters` and `url` properties are not set
|
||||
// to their real values)
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_loader_stream(
|
||||
LoaderStream::NotYetLoaded(fake_movie, Some(clip), false),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
|
||||
// Store our downloaded `SwfMovie` into our target `MovieClip`,
|
||||
// and initialize it.
|
||||
// Flash always fires an initial 'progress' event with
|
||||
// bytesLoaded=0 and bytesTotal set to the proper value.
|
||||
// This only seems to happen for an AVM2 event handler
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, 0, length)?;
|
||||
|
||||
mc.replace_with_movie(
|
||||
&mut activation.context,
|
||||
Some(movie.clone()),
|
||||
true,
|
||||
loader_info,
|
||||
);
|
||||
// Update the LoaderStream - we now have a real SWF movie and a real target clip
|
||||
// This is intentionally set *after* the first 'progress' event, to match Flash's behavior
|
||||
// (`LoaderInfo.parameters` is always empty during the first 'progress' event)
|
||||
loader_info
|
||||
.as_loader_info_object()
|
||||
.unwrap()
|
||||
.set_loader_stream(
|
||||
LoaderStream::NotYetLoaded(movie.clone(), Some(clip), false),
|
||||
activation.context.gc_context,
|
||||
);
|
||||
}
|
||||
|
||||
if matches!(vm_data, MovieLoaderVMData::Avm2 { .. })
|
||||
&& !movie.is_action_script_3()
|
||||
{
|
||||
// When an AVM2 movie loads an AVM1 movie, we need to call `post_instantiation` here.
|
||||
mc.post_instantiation(uc, None, Instantiator::Movie, false);
|
||||
match sniffed_type {
|
||||
ContentType::Swf => {
|
||||
let library = activation
|
||||
.context
|
||||
.library
|
||||
.library_for_movie_mut(movie.clone());
|
||||
|
||||
mc.set_depth(uc.gc_context, LOADER_INSERTED_AVM1_DEPTH);
|
||||
}
|
||||
library.set_avm2_domain(domain);
|
||||
|
||||
if let Some(mc) = clip.as_movie_clip() {
|
||||
let loader_info = if let MovieLoaderVMData::Avm2 { loader_info, .. } = vm_data {
|
||||
Some(*loader_info.as_loader_info_object().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Store our downloaded `SwfMovie` into our target `MovieClip`,
|
||||
// and initialize it.
|
||||
|
||||
mc.replace_with_movie(
|
||||
&mut activation.context,
|
||||
Some(movie.clone()),
|
||||
true,
|
||||
loader_info,
|
||||
);
|
||||
|
||||
if matches!(vm_data, MovieLoaderVMData::Avm2 { .. })
|
||||
&& !movie.is_action_script_3()
|
||||
{
|
||||
// When an AVM2 movie loads an AVM1 movie, we need to call `post_instantiation` here.
|
||||
mc.post_instantiation(uc, None, Instantiator::Movie, false);
|
||||
|
||||
mc.set_depth(uc.gc_context, LOADER_INSERTED_AVM1_DEPTH);
|
||||
}
|
||||
|
||||
// NOTE: Certain tests specifically expect small files to preload immediately
|
||||
if from_bytes {
|
||||
mc.preload(uc, &mut ExecutionLimit::none());
|
||||
Loader::movie_loader_progress(
|
||||
handle,
|
||||
uc,
|
||||
mc.compressed_loaded_bytes() as usize,
|
||||
mc.compressed_total_bytes() as usize,
|
||||
)?;
|
||||
let cb: Box<dyn FnOnce(&mut UpdateContext<'_, '_>)> = Box::new(move |uc| {
|
||||
let target_clip = match uc.load_manager.get_loader(handle) {
|
||||
Some(Loader::Movie { target_clip, .. }) => *target_clip,
|
||||
None => return,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if let Err(e) = Loader::movie_loader_complete(
|
||||
handle,
|
||||
uc,
|
||||
Some(target_clip),
|
||||
0,
|
||||
false,
|
||||
) {
|
||||
tracing::error!("Error finishing loading of Loader.loadBytes movie {target_clip:?}: {e:?}");
|
||||
}
|
||||
});
|
||||
uc.post_frame_callbacks.push(cb);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Certain tests specifically expect small files to preload immediately
|
||||
if !from_bytes {
|
||||
Loader::preload_tick(
|
||||
handle,
|
||||
uc,
|
||||
|
@ -1750,103 +1785,96 @@ impl<'gc> Loader<'gc> {
|
|||
status,
|
||||
redirected,
|
||||
)?;
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
ContentType::Gif | ContentType::Jpeg | ContentType::Png => {
|
||||
let library = activation.context.library.library_for_movie_mut(movie);
|
||||
return Ok(());
|
||||
}
|
||||
ContentType::Gif | ContentType::Jpeg | ContentType::Png => {
|
||||
let library = activation.context.library.library_for_movie_mut(movie);
|
||||
|
||||
library.set_avm2_domain(domain);
|
||||
library.set_avm2_domain(domain);
|
||||
|
||||
// This will construct AVM2-side objects even under AVM1, but it doesn't matter,
|
||||
// since Bitmap and BitmapData never have AVM1-side objects.
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(data, None)?;
|
||||
// This will construct AVM2-side objects even under AVM1, but it doesn't matter,
|
||||
// since Bitmap and BitmapData never have AVM1-side objects.
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(data, None)?;
|
||||
|
||||
let transparency = true;
|
||||
let bitmap_data = BitmapData::new_with_pixels(
|
||||
bitmap.width(),
|
||||
bitmap.height(),
|
||||
transparency,
|
||||
bitmap.as_colors().map(Color::from).collect(),
|
||||
);
|
||||
let bitmapdata_wrapper = BitmapDataWrapper::new(GcCell::new(
|
||||
activation.context.gc_context,
|
||||
bitmap_data,
|
||||
));
|
||||
let bitmapdata_class = activation.context.avm2.classes().bitmapdata;
|
||||
let bitmapdata_avm2 = BitmapDataObject::from_bitmap_data_internal(
|
||||
&mut activation,
|
||||
bitmapdata_wrapper,
|
||||
bitmapdata_class,
|
||||
)
|
||||
let transparency = true;
|
||||
let bitmap_data = BitmapData::new_with_pixels(
|
||||
bitmap.width(),
|
||||
bitmap.height(),
|
||||
transparency,
|
||||
bitmap.as_colors().map(Color::from).collect(),
|
||||
);
|
||||
let bitmapdata_wrapper =
|
||||
BitmapDataWrapper::new(GcCell::new(activation.context.gc_context, bitmap_data));
|
||||
let bitmapdata_class = activation.context.avm2.classes().bitmapdata;
|
||||
let bitmapdata_avm2 = BitmapDataObject::from_bitmap_data_internal(
|
||||
&mut activation,
|
||||
bitmapdata_wrapper,
|
||||
bitmapdata_class,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bitmap_avm2 = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.bitmap
|
||||
.construct(&mut activation, &[bitmapdata_avm2.into()])
|
||||
.unwrap();
|
||||
let bitmap_obj = bitmap_avm2.as_display_object().unwrap();
|
||||
|
||||
let bitmap_avm2 = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.bitmap
|
||||
.construct(&mut activation, &[bitmapdata_avm2.into()])
|
||||
.unwrap();
|
||||
let bitmap_obj = bitmap_avm2.as_display_object().unwrap();
|
||||
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, length, length)?;
|
||||
Loader::movie_loader_complete(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
Some(bitmap_obj),
|
||||
status,
|
||||
redirected,
|
||||
)?;
|
||||
}
|
||||
ContentType::Unknown => {
|
||||
match vm_data {
|
||||
MovieLoaderVMData::Avm1 { .. } => {
|
||||
// If the file is no valid supported file, the MovieClip enters the error state
|
||||
if let Some(mut mc) = clip.as_movie_clip() {
|
||||
Loader::load_error_swf(
|
||||
&mut mc,
|
||||
&mut activation.context,
|
||||
url.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
// AVM1 fires the event with the current and total length as 0
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, 0, 0)?;
|
||||
Loader::movie_loader_complete(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
None,
|
||||
status,
|
||||
redirected,
|
||||
)?;
|
||||
}
|
||||
MovieLoaderVMData::Avm2 { .. } => {
|
||||
Loader::movie_loader_progress(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
length,
|
||||
length,
|
||||
)?;
|
||||
Loader::movie_loader_error(
|
||||
handle,
|
||||
uc,
|
||||
AvmString::new_utf8(
|
||||
uc.gc_context,
|
||||
&format!(
|
||||
"Error #2124: Loaded file is an unknown type. URL: {url}"
|
||||
),
|
||||
),
|
||||
status,
|
||||
redirected,
|
||||
url,
|
||||
)?;
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, length, length)?;
|
||||
Loader::movie_loader_complete(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
Some(bitmap_obj),
|
||||
status,
|
||||
redirected,
|
||||
)?;
|
||||
}
|
||||
ContentType::Unknown => {
|
||||
match vm_data {
|
||||
MovieLoaderVMData::Avm1 { .. } => {
|
||||
// If the file is no valid supported file, the MovieClip enters the error state
|
||||
if let Some(mut mc) = clip.as_movie_clip() {
|
||||
Loader::load_error_swf(&mut mc, &mut activation.context, url.clone());
|
||||
}
|
||||
|
||||
// AVM1 fires the event with the current and total length as 0
|
||||
Loader::movie_loader_progress(handle, &mut activation.context, 0, 0)?;
|
||||
Loader::movie_loader_complete(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
None,
|
||||
status,
|
||||
redirected,
|
||||
)?;
|
||||
}
|
||||
MovieLoaderVMData::Avm2 { .. } => {
|
||||
Loader::movie_loader_progress(
|
||||
handle,
|
||||
&mut activation.context,
|
||||
length,
|
||||
length,
|
||||
)?;
|
||||
Loader::movie_loader_error(
|
||||
handle,
|
||||
uc,
|
||||
AvmString::new_utf8(
|
||||
uc.gc_context,
|
||||
&format!("Error #2124: Loaded file is an unknown type. URL: {url}"),
|
||||
),
|
||||
status,
|
||||
redirected,
|
||||
url,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}) //TODO: content sniffing errors need to be reported somehow
|
||||
//TODO: content sniffing errors need to be reported somehow
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Report a movie loader progress event to script code.
|
||||
|
@ -2005,6 +2033,16 @@ impl<'gc> Loader<'gc> {
|
|||
.as_container()
|
||||
.unwrap();
|
||||
|
||||
// This isn't completely correct - the 'large_preload' test observes the child
|
||||
// being set after an 'enterFrame' call. However, our current logic should
|
||||
// hopefully be good enough.
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display.Loader",
|
||||
"load",
|
||||
"addChild at the correct time"
|
||||
);
|
||||
|
||||
// Note that we do *not* use the 'addChild' method here:
|
||||
// Per the flash docs, our implementation always throws
|
||||
// an 'unsupported' error. Also, the AVM2 side of our movie
|
||||
|
|
|
@ -5,11 +5,9 @@ use crate::avm1::Object;
|
|||
use crate::avm1::SystemProperties;
|
||||
use crate::avm1::VariableDumper;
|
||||
use crate::avm1::{Activation, ActivationIdentifier};
|
||||
use crate::avm1::{ScriptObject, TObject, Value};
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm1::{TObject, Value};
|
||||
use crate::avm2::{
|
||||
object::LoaderInfoObject, object::TObject as _, Activation as Avm2Activation, Avm2, CallStack,
|
||||
Object as Avm2Object,
|
||||
object::TObject as _, Activation as Avm2Activation, Avm2, CallStack, Object as Avm2Object,
|
||||
};
|
||||
use crate::backend::ui::FontDefinition;
|
||||
use crate::backend::{
|
||||
|
@ -28,7 +26,7 @@ use crate::context_menu::{
|
|||
};
|
||||
use crate::display_object::Avm2MousePick;
|
||||
use crate::display_object::{
|
||||
EditText, InteractiveObject, MovieClip, Stage, StageAlign, StageDisplayState, StageScaleMode,
|
||||
EditText, InteractiveObject, Stage, StageAlign, StageDisplayState, StageScaleMode,
|
||||
TInteractiveObject, WindowMode,
|
||||
};
|
||||
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode, MouseButton, PlayerEvent};
|
||||
|
@ -66,7 +64,7 @@ use std::rc::{Rc, Weak as RcWeak};
|
|||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::time::Duration;
|
||||
use tracing::{info, instrument};
|
||||
use tracing::instrument;
|
||||
use web_time::Instant;
|
||||
|
||||
/// The newest known Flash Player version, serves as a default to
|
||||
|
@ -178,6 +176,10 @@ struct GcRootData<'gc> {
|
|||
|
||||
/// Dynamic root for allowing handles to GC objects to exist outside of the GC.
|
||||
dynamic_root: DynamicRootSet<'gc>,
|
||||
|
||||
#[collect(require_static)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
post_frame_callbacks: Vec<Box<dyn FnOnce(&mut UpdateContext<'_, '_>) + 'static>>,
|
||||
}
|
||||
|
||||
impl<'gc> GcRootData<'gc> {
|
||||
|
@ -206,6 +208,7 @@ impl<'gc> GcRootData<'gc> {
|
|||
&mut Sockets<'gc>,
|
||||
&mut NetConnections<'gc>,
|
||||
&mut LocalConnections<'gc>,
|
||||
&mut Vec<Box<dyn FnOnce(&mut UpdateContext<'_, '_>) + 'static>>,
|
||||
DynamicRootSet<'gc>,
|
||||
) {
|
||||
(
|
||||
|
@ -228,6 +231,7 @@ impl<'gc> GcRootData<'gc> {
|
|||
&mut self.sockets,
|
||||
&mut self.net_connections,
|
||||
&mut self.local_connections,
|
||||
&mut self.post_frame_callbacks,
|
||||
self.dynamic_root,
|
||||
)
|
||||
}
|
||||
|
@ -375,117 +379,6 @@ impl Player {
|
|||
});
|
||||
}
|
||||
|
||||
/// Change the root movie.
|
||||
///
|
||||
/// This should only be called once, as it makes no attempt at removing
|
||||
/// previous stage contents. If you need to load a new root movie, you
|
||||
/// should destroy and recreate the player instance.
|
||||
pub fn set_root_movie(&mut self, movie: SwfMovie) {
|
||||
if !self.forced_frame_rate {
|
||||
self.frame_rate = movie.frame_rate().into();
|
||||
}
|
||||
|
||||
info!(
|
||||
"Loaded SWF version {}, resolution {}x{} @ {} FPS",
|
||||
movie.version(),
|
||||
movie.width(),
|
||||
movie.height(),
|
||||
self.frame_rate(),
|
||||
);
|
||||
|
||||
self.swf = Arc::new(movie);
|
||||
self.instance_counter = 0;
|
||||
|
||||
self.mutate_with_update_context(|context| {
|
||||
if context.swf.is_action_script_3() {
|
||||
context.avm2.root_api_version = ApiVersion::from_swf_version(
|
||||
context.swf.version(),
|
||||
context.avm2.player_runtime,
|
||||
)
|
||||
.unwrap_or_else(|| panic!("Unknown SWF version {}", context.swf.version()));
|
||||
}
|
||||
|
||||
context.stage.set_movie_size(
|
||||
context.gc_context,
|
||||
context.swf.width().to_pixels() as u32,
|
||||
context.swf.height().to_pixels() as u32,
|
||||
);
|
||||
context
|
||||
.stage
|
||||
.set_movie(context.gc_context, context.swf.clone());
|
||||
|
||||
let stage_domain = context.avm2.stage_domain();
|
||||
let mut activation = Avm2Activation::from_domain(context.reborrow(), stage_domain);
|
||||
|
||||
activation
|
||||
.context
|
||||
.library
|
||||
.library_for_movie_mut(activation.context.swf.clone())
|
||||
.set_avm2_domain(stage_domain);
|
||||
activation.context.ui.set_mouse_visible(true);
|
||||
|
||||
let swf = activation.context.swf.clone();
|
||||
let root: DisplayObject =
|
||||
MovieClip::player_root_movie(&mut activation, swf.clone()).into();
|
||||
|
||||
// The Stage `LoaderInfo` is permanently in the 'not yet loaded' state,
|
||||
// and has no associated `Loader` instance.
|
||||
// However, some properties are always accessible, and take their values
|
||||
// from the root SWF.
|
||||
let stage_loader_info =
|
||||
LoaderInfoObject::not_yet_loaded(&mut activation, swf, None, Some(root), true)
|
||||
.expect("Failed to construct Stage LoaderInfo");
|
||||
activation
|
||||
.context
|
||||
.stage
|
||||
.set_loader_info(activation.context.gc_context, stage_loader_info);
|
||||
|
||||
drop(activation);
|
||||
|
||||
root.set_depth(context.gc_context, 0);
|
||||
let flashvars = if !context.swf.parameters().is_empty() {
|
||||
let object = ScriptObject::new(context.gc_context, None);
|
||||
for (key, value) in context.swf.parameters().iter() {
|
||||
object.define_value(
|
||||
context.gc_context,
|
||||
AvmString::new_utf8(context.gc_context, key),
|
||||
AvmString::new_utf8(context.gc_context, value).into(),
|
||||
Attribute::empty(),
|
||||
);
|
||||
}
|
||||
Some(object.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
root.post_instantiation(context, flashvars, Instantiator::Movie, false);
|
||||
root.set_default_root_name(context);
|
||||
context.stage.replace_at_depth(context, root, 0);
|
||||
|
||||
// Set the version parameter on the root.
|
||||
let mut activation = Activation::from_stub(
|
||||
context.reborrow(),
|
||||
ActivationIdentifier::root("[Version Setter]"),
|
||||
);
|
||||
let object = root.object().coerce_to_object(&mut activation);
|
||||
let version_string = activation
|
||||
.context
|
||||
.system
|
||||
.get_version_string(activation.context.avm1);
|
||||
object.define_value(
|
||||
activation.context.gc_context,
|
||||
"$version",
|
||||
AvmString::new_utf8(activation.context.gc_context, version_string).into(),
|
||||
Attribute::empty(),
|
||||
);
|
||||
|
||||
let stage = activation.context.stage;
|
||||
stage.build_matrices(&mut activation.context);
|
||||
});
|
||||
|
||||
self.audio.set_frame_rate(self.frame_rate);
|
||||
}
|
||||
|
||||
/// Get rough estimate of the max # of times we can update the frame.
|
||||
///
|
||||
/// In some cases, we might want to update several times in a row.
|
||||
|
@ -1673,6 +1566,12 @@ impl Player {
|
|||
run_all_phases_avm2(context);
|
||||
Avm1::run_frame(context);
|
||||
AudioManager::update_sounds(context);
|
||||
|
||||
// Only run the current list of callbacks - any callbacks added during callback execution
|
||||
// will be run at the end of the *next* frame.
|
||||
for callback in std::mem::take(context.post_frame_callbacks) {
|
||||
callback(context);
|
||||
}
|
||||
});
|
||||
|
||||
self.needs_render = true;
|
||||
|
@ -1916,12 +1815,13 @@ impl Player {
|
|||
sockets,
|
||||
net_connections,
|
||||
local_connections,
|
||||
post_frame_callbacks,
|
||||
dynamic_root,
|
||||
) = root_data.update_context_params();
|
||||
|
||||
let mut update_context = UpdateContext {
|
||||
player_version: self.player_version,
|
||||
swf: &self.swf,
|
||||
swf: &mut self.swf,
|
||||
library,
|
||||
rng: &mut self.rng,
|
||||
renderer: self.renderer.deref_mut(),
|
||||
|
@ -1971,6 +1871,7 @@ impl Player {
|
|||
net_connections,
|
||||
local_connections,
|
||||
dynamic_root,
|
||||
post_frame_callbacks,
|
||||
};
|
||||
|
||||
let prev_frame_rate = *update_context.frame_rate;
|
||||
|
@ -2487,6 +2388,7 @@ impl PlayerBuilder {
|
|||
net_connections: NetConnections::default(),
|
||||
local_connections: LocalConnections::empty(),
|
||||
dynamic_root,
|
||||
post_frame_callbacks: Vec::new(),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -2645,7 +2547,9 @@ impl PlayerBuilder {
|
|||
if let Some(url) = self.spoofed_url.clone() {
|
||||
movie.set_url(url);
|
||||
}
|
||||
player_lock.set_root_movie(movie);
|
||||
player_lock.mutate_with_update_context(|context| {
|
||||
context.set_root_movie(movie);
|
||||
});
|
||||
}
|
||||
drop(player_lock);
|
||||
player
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package {
|
||||
import flash.display.Stage;
|
||||
import flash.display.Loader;
|
||||
import flash.display.Loader;
|
||||
import flash.net.URLRequest;
|
||||
import flash.errors.IllegalOperationError;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.HTTPStatusEvent;
|
||||
import flash.display.MovieClip;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public class Main extends MovieClip {
|
||||
|
||||
[Embed(source="large_bytearray/test.swf", mimeType="application/octet-stream")]
|
||||
private static var LOADABLE_SWF:Class;
|
||||
private var loader: Loader;
|
||||
|
||||
public function Main() {
|
||||
this.setupLoader();
|
||||
trace("Calling super() in Main()");
|
||||
super();
|
||||
trace("Called super() in Main()");
|
||||
|
||||
var self = this;
|
||||
|
||||
this.addEventListener(Event.ENTER_FRAME, function(e) {
|
||||
// FIXME - re-enable this when the timing of 'content' being
|
||||
// set in Ruffle matches Flash Player
|
||||
//trace("enterFrame in Test: this.loader.content = " + self.loader.content);
|
||||
});
|
||||
|
||||
this.addEventListener(Event.EXIT_FRAME, function(e) {
|
||||
trace("exitFrame in Test");
|
||||
});
|
||||
}
|
||||
|
||||
private function dumpParams(obj: Object) {
|
||||
var out = []
|
||||
for (var key in obj) {
|
||||
out.push(key + " = " + obj[key]);
|
||||
}
|
||||
out.sort();
|
||||
trace("Parameters: (len=" + out.length + ")");
|
||||
trace(out);
|
||||
}
|
||||
|
||||
private function dumpLoader(loader: Loader) {
|
||||
trace("loader.content = " + loader.content);
|
||||
trace("loader.contentLoaderInfo.content = " + loader.contentLoaderInfo.content);
|
||||
trace("loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded);
|
||||
trace("loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal);
|
||||
trace("loader.contentLoaderInfo.bytes?.length = " + (loader.contentLoaderInfo.bytes ? loader.contentLoaderInfo.bytes.length : null));
|
||||
trace("loader.contentLoaderInfo.url = " + loader.contentLoaderInfo.url);
|
||||
trace("loader.contentLoaderInfo.parameters = " + loader.contentLoaderInfo.parameters);
|
||||
}
|
||||
|
||||
function setupLoader() {
|
||||
this.loader = new Loader();
|
||||
this.addChild(loader);
|
||||
this.dumpLoader(loader);
|
||||
|
||||
|
||||
function dump(event:Event) {
|
||||
var url = loader.contentLoaderInfo.url;
|
||||
if (url) {
|
||||
// This truncates the path to 'file:///' to make the output
|
||||
// reproducible across deifferent machines
|
||||
url = url.substr(0, 8);
|
||||
}
|
||||
trace("Event " + event + ": "
|
||||
+ "loader.numChildren = " + loader.numChildren
|
||||
+ ", loader.content = " + loader.content
|
||||
+ ", loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded
|
||||
+ ", loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal
|
||||
+ ", loader.contentLoaderInfo.bytes.length = " + loader.contentLoaderInfo.bytes.length
|
||||
+ ", loader.contentLoaderInfo.url = " + url);
|
||||
}
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.OPEN, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, function(e) {
|
||||
// FIXME - the 'bytesLoaded' and 'bytesTotal' values printed here are wrong,
|
||||
// as they are not properly implemented in Ruffle. Once the implementation is fixed,
|
||||
// the output of this test will change.
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.INIT, function(e) {
|
||||
trace("loader.contentLoaderInfo === loader.content.loaderInfo : " + (loader.contentLoaderInfo === loader.content.loaderInfo).toString());
|
||||
trace("loader.contentLoaderInfo.content === loader.content : " + (loader.contentLoaderInfo.content == loader.content).toString());
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.loadBytes(ByteArray(new LOADABLE_SWF()));
|
||||
//loader.load(new URLRequest("./large_bytearray/test.swf"));
|
||||
trace("Directly after load:");
|
||||
this.dumpLoader(loader);
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package {
|
||||
|
||||
import flash.display.MovieClip;
|
||||
import flash.display.Loader;
|
||||
import flash.events.*;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
|
||||
public class LargeSWF extends MovieClip {
|
||||
|
||||
private var loader: Loader;
|
||||
|
||||
[Embed(source = "data1.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA1: Class;
|
||||
|
||||
[Embed(source = "data2.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA2: Class;
|
||||
|
||||
[Embed(source = "data3.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA3: Class;
|
||||
|
||||
[Embed(source = "data4.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA4: Class;
|
||||
|
||||
[Embed(source = "data5.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA5: Class;
|
||||
|
||||
[Embed(source= "../nested_load/test.swf", mimeType="application/octet-stream")]
|
||||
public static var NESTED_LOAD: Class;
|
||||
|
||||
public function LargeSWF() {
|
||||
trace("Calling super() in LargeSWF()");
|
||||
super();
|
||||
trace("Called super() in LargeSWF()");
|
||||
trace("Loading ../nested_load/test.swf from bytes");
|
||||
this.setupLoader();
|
||||
}
|
||||
|
||||
|
||||
private function dumpLoader(loader: Loader) {
|
||||
trace("LargeSWF loader.content = " + loader.content);
|
||||
trace("LargeSWF loader.contentLoaderInfo.content = " + loader.contentLoaderInfo.content);
|
||||
trace("LargeSWF loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded);
|
||||
trace("LargeSWF loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal);
|
||||
trace("LargeSWF loader.contentLoaderInfo.bytes?.length = " + (loader.contentLoaderInfo.bytes ? loader.contentLoaderInfo.bytes.length : null));
|
||||
trace("LargeSWF loader.contentLoaderInfo.url = " + loader.contentLoaderInfo.url);
|
||||
trace("LargeSWF loader.contentLoaderInfo.parameters = " + loader.contentLoaderInfo.parameters);
|
||||
}
|
||||
|
||||
private function setupLoader() {
|
||||
this.loader = new Loader();
|
||||
this.addChild(loader);
|
||||
this.dumpLoader(loader);
|
||||
|
||||
|
||||
function dump(event:Event) {
|
||||
var url = loader.contentLoaderInfo.url;
|
||||
if (url) {
|
||||
// This truncates the path to 'file:///' to make the output
|
||||
// reproducible across deifferent machines
|
||||
url = url.substr(0, 8);
|
||||
}
|
||||
trace("LargeSWF Event " + event + ": "
|
||||
+ "loader.numChildren = " + loader.numChildren
|
||||
+ ", loader.content = " + loader.content
|
||||
+ ", loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded
|
||||
+ ", loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal
|
||||
+ ", loader.contentLoaderInfo.bytes.length = " + loader.contentLoaderInfo.bytes.length
|
||||
+ ", loader.contentLoaderInfo.url = " + url);
|
||||
}
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.OPEN, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, function(e) {
|
||||
// FIXME - the 'bytesLoaded' and 'bytesTotal' values printed here are wrong,
|
||||
// as they are not properly implemented in Ruffle. Once the implementation is fixed,
|
||||
// the output of this test will change.
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.INIT, function(e) {
|
||||
trace("LargeSWF loader.contentLoaderInfo === loader.content.loaderInfo : " + (loader.contentLoaderInfo === loader.content.loaderInfo).toString());
|
||||
trace("LargeSWF loader.contentLoaderInfo.content === loader.content : " + (loader.contentLoaderInfo.content == loader.content).toString());
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.loadBytes(ByteArray(new NESTED_LOAD()));
|
||||
trace("LargeSWF: Directly after load:");
|
||||
this.dumpLoader(loader);
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,14 @@
|
|||
package {
|
||||
|
||||
import flash.display.MovieClip;
|
||||
|
||||
|
||||
public class Test extends MovieClip {
|
||||
|
||||
|
||||
public function Test() {
|
||||
trace("Constucted nested_load/test.swf");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,51 @@
|
|||
loader.content = null
|
||||
loader.contentLoaderInfo.content = null
|
||||
loader.contentLoaderInfo.bytesLoaded = 0
|
||||
loader.contentLoaderInfo.bytesTotal = 0
|
||||
loader.contentLoaderInfo.bytes?.length = null
|
||||
loader.contentLoaderInfo.url = null
|
||||
loader.contentLoaderInfo.parameters = [object Object]
|
||||
Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=503316]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 503316, loader.contentLoaderInfo.bytes.length = 0, loader.contentLoaderInfo.url = null
|
||||
Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=503316 bytesTotal=503316]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 503316, loader.contentLoaderInfo.bytesTotal = 503316, loader.contentLoaderInfo.bytes.length = 505316, loader.contentLoaderInfo.url = null
|
||||
Directly after load:
|
||||
loader.content = null
|
||||
loader.contentLoaderInfo.content = null
|
||||
loader.contentLoaderInfo.bytesLoaded = 503316
|
||||
loader.contentLoaderInfo.bytesTotal = 503316
|
||||
loader.contentLoaderInfo.bytes?.length = 505316
|
||||
loader.contentLoaderInfo.url = null
|
||||
loader.contentLoaderInfo.parameters = [object Object]
|
||||
Calling super() in Main()
|
||||
Called super() in Main()
|
||||
exitFrame in Test
|
||||
Calling super() in LargeSWF()
|
||||
Called super() in LargeSWF()
|
||||
Loading ../nested_load/test.swf from bytes
|
||||
LargeSWF loader.content = null
|
||||
LargeSWF loader.contentLoaderInfo.content = null
|
||||
LargeSWF loader.contentLoaderInfo.bytesLoaded = 0
|
||||
LargeSWF loader.contentLoaderInfo.bytesTotal = 0
|
||||
LargeSWF loader.contentLoaderInfo.bytes?.length = null
|
||||
LargeSWF loader.contentLoaderInfo.url = null
|
||||
LargeSWF loader.contentLoaderInfo.parameters = [object Object]
|
||||
LargeSWF Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=424]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 424, loader.contentLoaderInfo.bytes.length = 0, loader.contentLoaderInfo.url = null
|
||||
LargeSWF Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=424 bytesTotal=424]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 424, loader.contentLoaderInfo.bytesTotal = 424, loader.contentLoaderInfo.bytes.length = 541, loader.contentLoaderInfo.url = null
|
||||
LargeSWF: Directly after load:
|
||||
LargeSWF loader.content = null
|
||||
LargeSWF loader.contentLoaderInfo.content = null
|
||||
LargeSWF loader.contentLoaderInfo.bytesLoaded = 424
|
||||
LargeSWF loader.contentLoaderInfo.bytesTotal = 424
|
||||
LargeSWF loader.contentLoaderInfo.bytes?.length = 541
|
||||
LargeSWF loader.contentLoaderInfo.url = null
|
||||
LargeSWF loader.contentLoaderInfo.parameters = [object Object]
|
||||
exitFrame in Test
|
||||
loader.contentLoaderInfo === loader.content.loaderInfo : true
|
||||
loader.contentLoaderInfo.content === loader.content : true
|
||||
Event [Event type="init" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LargeSWF], loader.contentLoaderInfo.bytesLoaded = 503316, loader.contentLoaderInfo.bytesTotal = 503316, loader.contentLoaderInfo.bytes.length = 505316, loader.contentLoaderInfo.url = file:///
|
||||
Event [Event type="complete" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LargeSWF], loader.contentLoaderInfo.bytesLoaded = 503316, loader.contentLoaderInfo.bytesTotal = 503316, loader.contentLoaderInfo.bytes.length = 505316, loader.contentLoaderInfo.url = file:///
|
||||
Constucted nested_load/test.swf
|
||||
exitFrame in Test
|
||||
LargeSWF loader.contentLoaderInfo === loader.content.loaderInfo : true
|
||||
LargeSWF loader.contentLoaderInfo.content === loader.content : true
|
||||
LargeSWF Event [Event type="init" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object Test], loader.contentLoaderInfo.bytesLoaded = 424, loader.contentLoaderInfo.bytesTotal = 424, loader.contentLoaderInfo.bytes.length = 541, loader.contentLoaderInfo.url = file:///
|
||||
LargeSWF Event [Event type="complete" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object Test], loader.contentLoaderInfo.bytesLoaded = 424, loader.contentLoaderInfo.bytesTotal = 424, loader.contentLoaderInfo.bytes.length = 541, loader.contentLoaderInfo.url = file:///
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_ticks = 3
|
|
@ -0,0 +1,113 @@
|
|||
package {
|
||||
import flash.display.Stage;
|
||||
import flash.display.Loader;
|
||||
import flash.display.Loader;
|
||||
import flash.net.URLRequest;
|
||||
import flash.errors.IllegalOperationError;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.HTTPStatusEvent;
|
||||
import flash.display.MovieClip;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public class Test extends MovieClip {
|
||||
|
||||
private var loader: Loader;
|
||||
|
||||
public function Test() {
|
||||
this.setupLoader();
|
||||
trace("Calling super() in Test()");
|
||||
super();
|
||||
trace("Called super() in Test()");
|
||||
|
||||
var self = this;
|
||||
|
||||
this.addEventListener(Event.ENTER_FRAME, function(e) {
|
||||
// FIXME - re-enable this when the timing of 'content' being
|
||||
// set in Ruffle matches Flash Player
|
||||
//trace("enterFrame in Test: this.loader.content = " + self.loader.content);
|
||||
});
|
||||
|
||||
this.addEventListener(Event.EXIT_FRAME, function(e) {
|
||||
// FIXME - Flash Player has two 'exitFrame' events in a
|
||||
// row. Until we can figure out why, don't log them.
|
||||
//trace("exitFrame in Test");
|
||||
});
|
||||
}
|
||||
|
||||
private function dumpParams(obj: Object) {
|
||||
var out = []
|
||||
for (var key in obj) {
|
||||
out.push(key + " = " + obj[key]);
|
||||
}
|
||||
out.sort();
|
||||
trace("Parameters: (len=" + out.length + ")");
|
||||
trace(out);
|
||||
}
|
||||
|
||||
private function dumpLoader(loader: Loader) {
|
||||
trace("loader.content = " + loader.content);
|
||||
trace("loader.contentLoaderInfo.content = " + loader.contentLoaderInfo.content);
|
||||
trace("loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded);
|
||||
trace("loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal);
|
||||
trace("loader.contentLoaderInfo.bytes?.length = " + (loader.contentLoaderInfo.bytes ? loader.contentLoaderInfo.bytes.length : null));
|
||||
trace("loader.contentLoaderInfo.url = " + loader.contentLoaderInfo.url);
|
||||
trace("loader.contentLoaderInfo.parameters = " + loader.contentLoaderInfo.parameters);
|
||||
}
|
||||
|
||||
function setupLoader() {
|
||||
this.loader = new Loader();
|
||||
this.addChild(loader);
|
||||
this.dumpLoader(loader);
|
||||
|
||||
|
||||
function dump(event:Event) {
|
||||
var url = loader.contentLoaderInfo.url;
|
||||
if (url) {
|
||||
// This truncates the path to 'file:///' to make the output
|
||||
// reproducible across deifferent machines
|
||||
url = url.substr(0, 8);
|
||||
}
|
||||
trace("Event " + event + ": "
|
||||
+ "loader.numChildren = " + loader.numChildren
|
||||
+ ", loader.content = " + loader.content
|
||||
+ ", loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded
|
||||
+ ", loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal
|
||||
+ ", loader.contentLoaderInfo.bytes.length = " + loader.contentLoaderInfo.bytes.length
|
||||
+ ", loader.contentLoaderInfo.url = " + url);
|
||||
}
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.OPEN, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, function(e: ProgressEvent) {
|
||||
// Note - the exact number of bytes loaded during each event is different
|
||||
// between Flash and Ruffle, so we only print the start and end events
|
||||
if (e.bytesLoaded == 0 || e.bytesLoaded == e.bytesTotal) {
|
||||
dump(e);
|
||||
}
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.INIT, function(e) {
|
||||
trace("loader.contentLoaderInfo === loader.content.loaderInfo : " + (loader.contentLoaderInfo === loader.content.loaderInfo).toString());
|
||||
trace("loader.contentLoaderInfo.content === loader.content : " + (loader.contentLoaderInfo.content == loader.content).toString());
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e) {
|
||||
dump(e);
|
||||
});
|
||||
|
||||
loader.load(new URLRequest("./large_bytearray/test.swf"));
|
||||
trace("Directly after load:");
|
||||
this.dumpLoader(loader);
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package {
|
||||
|
||||
import flash.display.MovieClip;
|
||||
|
||||
|
||||
public class LargeSWF extends MovieClip {
|
||||
|
||||
[Embed(source = "data1.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA1: Class;
|
||||
|
||||
[Embed(source = "data2.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA2: Class;
|
||||
|
||||
[Embed(source = "data3.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA3: Class;
|
||||
|
||||
[Embed(source = "data4.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA4: Class;
|
||||
|
||||
[Embed(source = "data5.bin", mimeType="application/octet-stream")]
|
||||
public static var DATA5: Class;
|
||||
|
||||
public function LargeSWF() {
|
||||
trace("Calling super() in LargeSWF()");
|
||||
super();
|
||||
trace("Called super() in LargeSWF()");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,27 @@
|
|||
loader.content = null
|
||||
loader.contentLoaderInfo.content = null
|
||||
loader.contentLoaderInfo.bytesLoaded = 0
|
||||
loader.contentLoaderInfo.bytesTotal = 0
|
||||
loader.contentLoaderInfo.bytes?.length = null
|
||||
loader.contentLoaderInfo.url = null
|
||||
loader.contentLoaderInfo.parameters = [object Object]
|
||||
Directly after load:
|
||||
loader.content = null
|
||||
loader.contentLoaderInfo.content = null
|
||||
loader.contentLoaderInfo.bytesLoaded = 0
|
||||
loader.contentLoaderInfo.bytesTotal = 0
|
||||
loader.contentLoaderInfo.bytes?.length = 0
|
||||
loader.contentLoaderInfo.url = null
|
||||
loader.contentLoaderInfo.parameters = [object Object]
|
||||
Calling super() in Test()
|
||||
Called super() in Test()
|
||||
Event [Event type="open" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 0, loader.contentLoaderInfo.bytes.length = 0, loader.contentLoaderInfo.url = null
|
||||
Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=501655]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 501655, loader.contentLoaderInfo.bytes.length = 0, loader.contentLoaderInfo.url = null
|
||||
Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=501655 bytesTotal=501655]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 501655, loader.contentLoaderInfo.bytesTotal = 501655, loader.contentLoaderInfo.bytes.length = 502418, loader.contentLoaderInfo.url = null
|
||||
Calling super() in LargeSWF()
|
||||
Called super() in LargeSWF()
|
||||
loader.contentLoaderInfo === loader.content.loaderInfo : true
|
||||
loader.contentLoaderInfo.content === loader.content : true
|
||||
Event [Event type="init" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LargeSWF], loader.contentLoaderInfo.bytesLoaded = 501655, loader.contentLoaderInfo.bytesTotal = 501655, loader.contentLoaderInfo.bytes.length = 502418, loader.contentLoaderInfo.url = file:///
|
||||
Event [HTTPStatusEvent type="httpStatus" bubbles=false cancelable=false eventPhase=2 status=0 redirected=false responseURL=null]: loader.numChildren = 1, loader.content = [object LargeSWF], loader.contentLoaderInfo.bytesLoaded = 501655, loader.contentLoaderInfo.bytesTotal = 501655, loader.contentLoaderInfo.bytes.length = 502418, loader.contentLoaderInfo.url = file:///
|
||||
Event [Event type="complete" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LargeSWF], loader.contentLoaderInfo.bytesLoaded = 501655, loader.contentLoaderInfo.bytesTotal = 501655, loader.contentLoaderInfo.bytes.length = 502418, loader.contentLoaderInfo.url = file:///
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_ticks = 2
|
|
@ -1 +1 @@
|
|||
num_frames = 3
|
||||
num_frames = 4
|
||||
|
|
|
@ -408,7 +408,9 @@ impl Ruffle {
|
|||
self.on_metadata(movie.header());
|
||||
|
||||
let _ = self.with_core_mut(move |core| {
|
||||
core.set_root_movie(movie);
|
||||
core.update(|uc| {
|
||||
uc.set_root_movie(movie);
|
||||
});
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue