avm2: Store `LoaderInfo` object on `MovieClip` and `Stage`
Previously, we would create a fresh `LoaderInfo` object each time the `loaderInfo` property was accessed. However, users can add event handlers to a `LoaderInfo`, so we need to create and store exactly one `LoaderInfo` object per movie (and stage). To verify that we're correctly handling the storage of `LoaderInfo`, I've implemented firing the "init" event. This required a new `on_frame_exit` hook, so that we can properly fire the "init" event after the "exitFrame" for the initial frame but before the "enterFrame" of the next frame.
This commit is contained in:
parent
df07f610e7
commit
49d1a985ca
|
@ -1148,7 +1148,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
if url.is_empty() {
|
||||
//Blank URL on movie loads = unload!
|
||||
if let Some(mut mc) = level.as_movie_clip() {
|
||||
mc.replace_with_movie(self.context.gc_context, None)
|
||||
mc.replace_with_movie(&mut self.context, None)
|
||||
}
|
||||
} else {
|
||||
let future = self.context.load_manager.load_movie_into_clip(
|
||||
|
@ -1264,7 +1264,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
if url.is_empty() {
|
||||
// Blank URL on movie loads = unload!
|
||||
if let Some(mut mc) = clip_target.as_movie_clip() {
|
||||
mc.replace_with_movie(self.context.gc_context, None)
|
||||
mc.replace_with_movie(&mut self.context, None)
|
||||
}
|
||||
} else {
|
||||
let request = self.locals_into_request(
|
||||
|
|
|
@ -1352,7 +1352,7 @@ fn unload_movie<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
target.unload(&mut activation.context);
|
||||
target.replace_with_movie(activation.context.gc_context, None);
|
||||
target.replace_with_movie(&mut activation.context, None);
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ fn unload_clip<'gc>(
|
|||
if let Some(target) = target {
|
||||
target.unload(&mut activation.context);
|
||||
if let Some(mut mc) = target.as_movie_clip() {
|
||||
mc.replace_with_movie(activation.context.gc_context, None);
|
||||
mc.replace_with_movie(&mut activation.context, None);
|
||||
}
|
||||
return Ok(true.into());
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::class::Class;
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
use crate::avm2::object::{stage_allocator, LoaderInfoObject, Object, TObject};
|
||||
use crate::avm2::object::{stage_allocator, Object, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::ArrayObject;
|
||||
use crate::avm2::Error;
|
||||
use crate::display_object::{DisplayObject, HitTestOptions, TDisplayObject};
|
||||
use crate::display_object::{HitTestOptions, TDisplayObject};
|
||||
use crate::types::{Degrees, Percent};
|
||||
use crate::vminterface::Instantiator;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
|
@ -574,26 +574,15 @@ pub fn hit_test_object<'gc>(
|
|||
|
||||
/// Implements `loaderInfo` getter
|
||||
pub fn loader_info<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(dobj) = this.and_then(|this| this.as_display_object()) {
|
||||
if let Some(root) = dobj.avm2_root(&mut activation.context) {
|
||||
let movie = dobj.movie();
|
||||
|
||||
if let Some(movie) = movie {
|
||||
let obj = LoaderInfoObject::from_movie(activation, movie, root)?;
|
||||
|
||||
return Ok(obj.into());
|
||||
}
|
||||
}
|
||||
|
||||
if DisplayObject::ptr_eq(dobj, activation.context.stage.into()) {
|
||||
return Ok(LoaderInfoObject::from_stage(activation)?.into());
|
||||
if let Some(loader_info) = dobj.loader_info() {
|
||||
return Ok(loader_info.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::avm2::vtable::{ClassBoundMethod, VTable};
|
|||
use crate::avm2::Error;
|
||||
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
||||
use crate::bitmap::bitmap_data::BitmapData;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::DisplayObject;
|
||||
use crate::html::TextFormat;
|
||||
use crate::string::AvmString;
|
||||
|
@ -1003,6 +1004,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
None
|
||||
}
|
||||
|
||||
fn loader_stream_init(&self, _context: &mut UpdateContext<'_, 'gc, '_>) {}
|
||||
|
||||
/// Unwrap this object's loader stream
|
||||
fn as_loader_stream(&self) -> Option<Ref<LoaderStream<'gc>>> {
|
||||
None
|
||||
|
|
|
@ -4,9 +4,11 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::object::script_object::ScriptObjectData;
|
||||
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Avm2;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::{Event, EventData};
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::DisplayObject;
|
||||
use crate::string::AvmString;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use std::cell::{Ref, RefMut};
|
||||
|
@ -24,6 +26,7 @@ pub fn loaderinfo_allocator<'gc>(
|
|||
LoaderInfoObjectData {
|
||||
base,
|
||||
loaded_stream: None,
|
||||
init_fired: false,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
|
@ -60,6 +63,9 @@ pub struct LoaderInfoObjectData<'gc> {
|
|||
|
||||
/// The loaded stream that this gets it's info from.
|
||||
loaded_stream: Option<LoaderStream<'gc>>,
|
||||
|
||||
/// Whether or not we've fired an 'init' event
|
||||
init_fired: bool,
|
||||
}
|
||||
|
||||
impl<'gc> LoaderInfoObject<'gc> {
|
||||
|
@ -78,6 +84,7 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
LoaderInfoObjectData {
|
||||
base,
|
||||
loaded_stream,
|
||||
init_fired: false,
|
||||
},
|
||||
))
|
||||
.into();
|
||||
|
@ -98,6 +105,9 @@ impl<'gc> LoaderInfoObject<'gc> {
|
|||
LoaderInfoObjectData {
|
||||
base,
|
||||
loaded_stream: Some(LoaderStream::Stage),
|
||||
// We never want to fire an "init" event for the special
|
||||
// Stagee loaderInfo
|
||||
init_fired: true,
|
||||
},
|
||||
))
|
||||
.into();
|
||||
|
@ -122,14 +132,23 @@ impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> {
|
|||
self.0.as_ptr() as *const ObjectPtr
|
||||
}
|
||||
|
||||
fn value_of(&self, mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
if let Some(class) = self.instance_of_class_definition() {
|
||||
Ok(
|
||||
AvmString::new_utf8(mc, format!("[object {}]", class.read().name().local_name()))
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
Ok("[object Object]".into())
|
||||
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||
Ok(Value::Object((*self).into()))
|
||||
}
|
||||
|
||||
fn loader_stream_init(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
if !self.0.read().init_fired {
|
||||
self.0.write(context.gc_context).init_fired = true;
|
||||
let mut init_evt = Event::new("init", EventData::Empty);
|
||||
init_evt.set_bubbles(false);
|
||||
init_evt.set_cancelable(false);
|
||||
|
||||
if let Err(e) = Avm2::dispatch_event(context, init_evt, (*self).into()) {
|
||||
log::error!(
|
||||
"Encountered AVM2 error when broadcasting `init` event: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1165,6 +1165,16 @@ pub trait TDisplayObject<'gc>:
|
|||
e
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_self(&self, _context: &mut RenderContext<'_, 'gc>) {}
|
||||
|
@ -1338,6 +1348,10 @@ pub trait TDisplayObject<'gc>:
|
|||
self.parent().and_then(|p| p.movie())
|
||||
}
|
||||
|
||||
fn loader_info(&self) -> Option<Avm2Object<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn instantiate(&self, gc_context: MutationContext<'gc, '_>) -> DisplayObject<'gc>;
|
||||
fn as_ptr(&self) -> *const DisplayObjectPtr;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
use crate::avm1::{
|
||||
Avm1, Object as Avm1Object, StageObject, TObject as Avm1TObject, Value as Avm1Value,
|
||||
};
|
||||
use crate::avm2::object::LoaderInfoObject;
|
||||
use crate::avm2::Activation as Avm2Activation;
|
||||
use crate::avm2::{
|
||||
Avm2, ClassObject as Avm2ClassObject, Error as Avm2Error, Namespace as Avm2Namespace,
|
||||
|
@ -205,15 +206,20 @@ impl<'gc> MovieClip<'gc> {
|
|||
}
|
||||
|
||||
/// Construct a movie clip that represents an entire movie.
|
||||
pub fn from_movie(gc_context: MutationContext<'gc, '_>, movie: Arc<SwfMovie>) -> Self {
|
||||
pub fn from_movie(context: &mut UpdateContext<'_, 'gc, '_>, movie: Arc<SwfMovie>) -> Self {
|
||||
let num_frames = movie.num_frames();
|
||||
let mc = MovieClip(GcCell::allocate(
|
||||
gc_context,
|
||||
context.gc_context,
|
||||
MovieClipData {
|
||||
base: Default::default(),
|
||||
static_data: Gc::allocate(
|
||||
gc_context,
|
||||
MovieClipStatic::with_data(0, movie.into(), num_frames, gc_context),
|
||||
context.gc_context,
|
||||
MovieClipStatic::with_data(
|
||||
0,
|
||||
movie.clone().into(),
|
||||
num_frames,
|
||||
context.gc_context,
|
||||
),
|
||||
),
|
||||
tag_stream_pos: 0,
|
||||
current_frame: 0,
|
||||
|
@ -237,23 +243,64 @@ impl<'gc> MovieClip<'gc> {
|
|||
drop_target: None,
|
||||
},
|
||||
));
|
||||
mc.set_is_root(gc_context, true);
|
||||
mc.set_is_root(context.gc_context, true);
|
||||
mc.set_loader_info(context, movie);
|
||||
mc
|
||||
}
|
||||
|
||||
/// Replace the current MovieClip with a completely new SwfMovie.
|
||||
/// Replace the current MovieClipData with a completely new SwfMovie.
|
||||
///
|
||||
/// Playback will start at position zero, any existing streamed audio will
|
||||
/// be terminated, and so on. Children and AVM data will be kept across the
|
||||
/// load boundary.
|
||||
/// be terminated, and so on. Children and AVM data will NOT be kept across
|
||||
/// the load boundary.
|
||||
///
|
||||
/// If no movie is provided, then the movie clip will be replaced with an
|
||||
/// empty movie of the same SWF version.
|
||||
pub fn replace_with_movie(
|
||||
&mut self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
movie: Option<Arc<SwfMovie>>,
|
||||
) {
|
||||
self.0
|
||||
.write(gc_context)
|
||||
.replace_with_movie(gc_context, movie)
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
let is_swf = movie.is_some();
|
||||
let movie = movie.unwrap_or_else(|| Arc::new(SwfMovie::empty(mc.movie().version())));
|
||||
let total_frames = movie.num_frames();
|
||||
mc.base.base.reset_for_movie_load();
|
||||
mc.static_data = Gc::allocate(
|
||||
context.gc_context,
|
||||
MovieClipStatic::with_data(0, movie.clone().into(), total_frames, context.gc_context),
|
||||
);
|
||||
mc.tag_stream_pos = 0;
|
||||
mc.flags = MovieClipFlags::PLAYING;
|
||||
mc.base.base.set_is_root(is_swf);
|
||||
mc.current_frame = 0;
|
||||
mc.audio_stream = None;
|
||||
mc.container = ChildContainer::new();
|
||||
drop(mc);
|
||||
|
||||
self.set_loader_info(context, movie);
|
||||
}
|
||||
|
||||
fn set_loader_info(&self, context: &mut UpdateContext<'_, 'gc, '_>, movie: Arc<SwfMovie>) {
|
||||
if movie.avm_type() == AvmType::Avm2 {
|
||||
let gc_context = context.gc_context;
|
||||
let mc = self.0.write(gc_context);
|
||||
if mc.base.base.is_root() {
|
||||
let mut activation = Avm2Activation::from_nothing(context.reborrow());
|
||||
match LoaderInfoObject::from_movie(&mut activation, movie, (*self).into()) {
|
||||
Ok(loader_info) => {
|
||||
*mc.static_data.loader_info.write(gc_context) = Some(loader_info);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Error contructing LoaderInfoObject for movie {:?}: {:?}",
|
||||
mc,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preload(self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
|
@ -1771,9 +1818,8 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
// we can't fire the events until we run our first frame, so we
|
||||
// have to actually check if we've just built the root and act
|
||||
// like it just got added to the timeline.
|
||||
let root = self.avm2_root(context);
|
||||
let self_dobj: DisplayObject<'gc> = (*self).into();
|
||||
if DisplayObject::option_ptr_eq(Some(self_dobj), root) {
|
||||
if self_dobj.is_root() {
|
||||
dispatch_added_event_only(self_dobj, context);
|
||||
dispatch_added_to_stage_event_only(self_dobj, context);
|
||||
}
|
||||
|
@ -1783,7 +1829,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
|
||||
fn run_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
// Run my load/enterFrame clip event.
|
||||
let is_load_frame = !self.0.read().initialized();
|
||||
let is_load_frame = !self.0.read().flags.contains(MovieClipFlags::INITIALIZED);
|
||||
if is_load_frame {
|
||||
self.event_dispatch(context, ClipEvent::Load);
|
||||
self.0.write(context.gc_context).set_initialized(true);
|
||||
|
@ -1866,6 +1912,23 @@ 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_strean_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() {
|
||||
loader_info.loader_stream_init(context);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -2017,6 +2080,10 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
self.set_removed(context.gc_context, true);
|
||||
}
|
||||
|
||||
fn loader_info(&self) -> Option<Avm2Object<'gc>> {
|
||||
*self.0.read().static_data.loader_info.read()
|
||||
}
|
||||
|
||||
fn allow_as_mask(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
@ -2225,36 +2292,6 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> MovieClipData<'gc> {
|
||||
/// Replace the current MovieClipData with a completely new SwfMovie.
|
||||
///
|
||||
/// Playback will start at position zero, any existing streamed audio will
|
||||
/// be terminated, and so on. Children and AVM data will NOT be kept across
|
||||
/// the load boundary.
|
||||
///
|
||||
/// If no movie is provided, then the movie clip will be replaced with an
|
||||
/// empty movie of the same SWF version.
|
||||
pub fn replace_with_movie(
|
||||
&mut self,
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
movie: Option<Arc<SwfMovie>>,
|
||||
) {
|
||||
let is_swf = movie.is_some();
|
||||
let movie = movie.unwrap_or_else(|| Arc::new(SwfMovie::empty(self.movie().version())));
|
||||
let total_frames = movie.num_frames();
|
||||
|
||||
self.base.base.reset_for_movie_load();
|
||||
self.static_data = Gc::allocate(
|
||||
gc_context,
|
||||
MovieClipStatic::with_data(0, movie.into(), total_frames, gc_context),
|
||||
);
|
||||
self.tag_stream_pos = 0;
|
||||
self.flags = MovieClipFlags::PLAYING;
|
||||
self.base.base.set_is_root(is_swf);
|
||||
self.current_frame = 0;
|
||||
self.audio_stream = None;
|
||||
self.container = ChildContainer::new();
|
||||
}
|
||||
|
||||
fn id(&self) -> CharacterId {
|
||||
self.static_data.id
|
||||
}
|
||||
|
@ -3283,6 +3320,10 @@ struct MovieClipStatic<'gc> {
|
|||
/// The last known symbol name under which this movie clip was exported.
|
||||
/// Used for looking up constructors registered with `Object.registerClass`.
|
||||
exported_name: GcCell<'gc, Option<AvmString<'gc>>>,
|
||||
/// Only set if this MovieClip is the root movie in an SWF
|
||||
/// (either the root SWF initially loaded by the player,
|
||||
/// or an SWF dynamically loaded by `Loader`)
|
||||
loader_info: GcCell<'gc, Option<Avm2Object<'gc>>>,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClipStatic<'gc> {
|
||||
|
@ -3305,6 +3346,7 @@ impl<'gc> MovieClipStatic<'gc> {
|
|||
audio_stream_info: None,
|
||||
audio_stream_handle: None,
|
||||
exported_name: GcCell::allocate(gc_context, None),
|
||||
loader_info: GcCell::allocate(gc_context, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Root stage impl
|
||||
|
||||
use crate::avm1::Object as Avm1Object;
|
||||
use crate::avm2::object::LoaderInfoObject;
|
||||
use crate::avm2::{
|
||||
Activation as Avm2Activation, Event as Avm2Event, EventData as Avm2EventData,
|
||||
Object as Avm2Object, ScriptObject as Avm2ScriptObject, StageObject as Avm2StageObject,
|
||||
|
@ -105,6 +106,9 @@ pub struct StageData<'gc> {
|
|||
|
||||
/// The AVM2 view of this stage object.
|
||||
avm2_object: Avm2Object<'gc>,
|
||||
|
||||
/// The AVM2 'LoaderInfo' object for this stage object
|
||||
loader_info: Avm2Object<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> Stage<'gc> {
|
||||
|
@ -138,6 +142,7 @@ impl<'gc> Stage<'gc> {
|
|||
window_mode: Default::default(),
|
||||
show_menu: true,
|
||||
avm2_object: Avm2ScriptObject::custom_object(gc_context, None, None),
|
||||
loader_info: Avm2ScriptObject::custom_object(gc_context, None, None),
|
||||
},
|
||||
));
|
||||
stage.set_is_root(gc_context, true);
|
||||
|
@ -671,7 +676,12 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
|
||||
match avm2_stage {
|
||||
Ok(avm2_stage) => {
|
||||
self.0.write(activation.context.gc_context).avm2_object = avm2_stage.into()
|
||||
let mut write = self.0.write(activation.context.gc_context);
|
||||
write.avm2_object = avm2_stage.into();
|
||||
match LoaderInfoObject::from_stage(&mut activation) {
|
||||
Ok(loader_info) => write.loader_info = loader_info,
|
||||
Err(e) => log::error!("Unable to set AVM2 Stage loaderInfo: {}", e),
|
||||
}
|
||||
}
|
||||
Err(e) => log::error!("Unable to construct AVM2 Stage: {}", e),
|
||||
}
|
||||
|
@ -729,6 +739,10 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
fn object2(&self) -> Avm2Value<'gc> {
|
||||
self.0.read().avm2_object.into()
|
||||
}
|
||||
|
||||
fn loader_info(&self) -> Option<Avm2Object<'gc>> {
|
||||
Some(self.0.read().loader_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TDisplayObjectContainer<'gc> for Stage<'gc> {
|
||||
|
|
|
@ -457,7 +457,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
if let Some(mut mc) = clip.as_movie_clip() {
|
||||
mc.unload(uc);
|
||||
mc.replace_with_movie(uc.gc_context, None);
|
||||
mc.replace_with_movie(uc, None);
|
||||
}
|
||||
|
||||
Loader::movie_loader_start(handle, uc)
|
||||
|
@ -499,7 +499,7 @@ impl<'gc> Loader<'gc> {
|
|||
.set_avm2_domain(domain);
|
||||
|
||||
if let Some(mut mc) = clip.as_movie_clip() {
|
||||
mc.replace_with_movie(uc.gc_context, Some(movie));
|
||||
mc.replace_with_movie(uc, Some(movie));
|
||||
mc.post_instantiation(uc, None, Instantiator::Movie, false);
|
||||
mc.preload(uc);
|
||||
}
|
||||
|
|
|
@ -294,8 +294,7 @@ impl Player {
|
|||
.set_avm2_domain(domain);
|
||||
context.ui.set_mouse_visible(true);
|
||||
|
||||
let root: DisplayObject =
|
||||
MovieClip::from_movie(context.gc_context, context.swf.clone()).into();
|
||||
let root: DisplayObject = MovieClip::from_movie(context, context.swf.clone()).into();
|
||||
|
||||
root.set_depth(context.gc_context, 0);
|
||||
let flashvars = if !context.swf.parameters().is_empty() {
|
||||
|
@ -1922,7 +1921,7 @@ impl PlayerBuilder {
|
|||
let mut player_lock = player.lock().unwrap();
|
||||
player_lock.mutate_with_update_context(|context| {
|
||||
// Instantiate an empty root before the main movie loads.
|
||||
let fake_root = MovieClip::from_movie(context.gc_context, fake_movie);
|
||||
let fake_root = MovieClip::from_movie(context, fake_movie);
|
||||
fake_root.post_instantiation(context, None, Instantiator::Movie, false);
|
||||
context.stage.replace_at_depth(context, fake_root.into(), 0);
|
||||
Avm2::load_player_globals(context).expect("Unable to load AVM2 globals");
|
||||
|
|
|
@ -332,6 +332,7 @@ swf_tests! {
|
|||
(as3_lazyinit, "avm2/lazyinit", 1),
|
||||
(as3_lessequals, "avm2/lessequals", 1),
|
||||
(as3_lessthan, "avm2/lessthan", 1),
|
||||
(as3_loaderinfo_events, "avm2/loaderinfo_events", 2),
|
||||
(as3_loaderinfo_properties, "avm2/loaderinfo_properties", 2),
|
||||
(as3_loaderinfo_quine, "avm2/loaderinfo_quine", 2),
|
||||
(as3_lshift, "avm2/lshift", 1),
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package {
|
||||
import flash.display.MovieClip;
|
||||
public class Main extends MovieClip {
|
||||
|
||||
public function Main() {
|
||||
loaderInfo.addEventListener("init", function() {
|
||||
trace("Called init!");
|
||||
});
|
||||
this.addEventListener("addedToStage", function() {
|
||||
trace("Called addedToStage");
|
||||
});
|
||||
this.addEventListener("added", function() {
|
||||
trace("Called added");
|
||||
});
|
||||
|
||||
var clip = this;
|
||||
this.addEventListener("enterFrame", function() {
|
||||
trace("Called enterFrame");
|
||||
});
|
||||
|
||||
this.addEventListener("exitFrame", function() {
|
||||
trace("Called exitFrame");
|
||||
});
|
||||
|
||||
stage.loaderInfo.addEventListener("init", function() {
|
||||
trace("ERROR: Stage loaderInfo should not fire 'init'");
|
||||
});
|
||||
trace("Called constructor");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
Called constructor
|
||||
Called added
|
||||
Called addedToStage
|
||||
Called exitFrame
|
||||
Called init!
|
||||
Called enterFrame
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue