Skip to content

core: don't store the SwfMovie in ChildContainer #20784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,8 @@ impl<'gc> UpdateContext<'gc> {

/// 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
/// This should only be called once, as it makes no attempt at proper clean-up
/// of previous stage contents. If you need to load a new root movie, you
/// should use `replace_root_movie`.
pub fn set_root_movie(&mut self, movie: SwfMovie) {
if !self.forced_frame_rate {
Expand Down
2 changes: 1 addition & 1 deletion core/src/display_object/avm1_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'gc> Avm1Button<'gc> {
Avm1ButtonData {
cell: RefLock::new(Avm1ButtonDataMut {
base: Default::default(),
container: ChildContainer::new(source_movie.movie.clone()),
container: ChildContainer::new(&source_movie.movie),
hit_area: BTreeMap::new(),
hit_bounds: Default::default(),
text_field_bindings: Vec::new(),
Expand Down
35 changes: 15 additions & 20 deletions core/src/display_object/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use std::collections::BTreeMap;
use std::fmt::Debug;
use std::ops::{Bound, RangeBounds};
use std::rc::Rc;
use std::sync::Arc;

/// Dispatch the `removedFromStage` event on a child and all of it's
/// grandchildren, recursively.
Expand Down Expand Up @@ -213,7 +212,7 @@ pub trait TDisplayObjectContainer<'gc>:
child.set_depth(depth);

if let Some(removed_child) = removed_child {
if !self.raw_container().movie().is_action_script_3() {
if !self.raw_container().is_action_script_3() {
removed_child.avm1_unload(context);
}
removed_child.set_parent(context, None);
Expand Down Expand Up @@ -281,7 +280,7 @@ pub trait TDisplayObjectContainer<'gc>:

child.set_place_frame(0);
child.set_parent(context, Some(this));
if !self.raw_container().movie().is_action_script_3() {
if !self.raw_container().is_action_script_3() {
child.set_avm1_removed(false);
}

Expand Down Expand Up @@ -318,7 +317,7 @@ pub trait TDisplayObjectContainer<'gc>:
));

// Check if this child should have delayed removal (AVM1 only)
if !self.raw_container().movie().is_action_script_3() {
if !self.raw_container().is_action_script_3() {
let should_delay_removal = {
let mut activation = Activation::from_nothing(
context,
Expand Down Expand Up @@ -366,7 +365,7 @@ pub trait TDisplayObjectContainer<'gc>:
ChildContainer::remove_child_from_render_list(this, child, context);

if removed_from_render_list {
if !self.raw_container().movie.is_action_script_3() {
if !self.raw_container().is_action_script_3() {
child.avm1_unload(context);
} else if !matches!(child.object2(), Avm2Value::Null) {
//TODO: This is an awful, *awful* hack to deal with the fact
Expand Down Expand Up @@ -445,7 +444,7 @@ pub trait TDisplayObjectContainer<'gc>:
let this: DisplayObjectContainer<'gc> = (*self).into();
ChildContainer::remove_child_from_render_list(this, removed, context);

if !self.raw_container().movie.is_action_script_3() {
if !self.raw_container().is_action_script_3() {
removed.avm1_unload(context);
} else if !matches!(removed.object2(), Avm2Value::Null) {
removed.set_parent(context, None);
Expand Down Expand Up @@ -490,17 +489,17 @@ pub trait TDisplayObjectContainer<'gc>:
/// According to the AS2 documentation, it should affect only automatic tab ordering.
/// However, that does not seem to be the case, as it also affects custom ordering.
fn is_tab_children(&self, context: &mut UpdateContext<'gc>) -> bool {
let this: DisplayObject<'_> = (*self).into();
if this.movie().is_action_script_3() {
self.raw_container().tab_children
let container = self.raw_container();
if container.is_action_script_3() {
container.tab_children
} else {
self.is_tab_children_avm1(context)
}
}

fn set_tab_children(&self, context: &mut UpdateContext<'gc>, value: bool) {
let this: DisplayObject<'_> = (*self).into();
if this.movie().is_action_script_3() {
if self.raw_container().is_action_script_3() {
self.raw_container_mut(context.gc()).tab_children = value;
} else {
this.set_avm1_property(istr!(context, "tabChildren"), value.into(), context);
Expand Down Expand Up @@ -653,21 +652,21 @@ pub struct ChildContainer<'gc> {

mouse_children: bool,

/// The movie this ChildContainer belongs to.
movie: Arc<SwfMovie>,
/// Specifies whether this container uses AVM1 or AVM2 semantics.
is_action_script_3: bool,

/// Specifies whether children are present in the tab ordering.
tab_children: bool,
}

impl<'gc> ChildContainer<'gc> {
pub fn new(movie: Arc<SwfMovie>) -> Self {
pub fn new(movie: &SwfMovie) -> Self {
Self {
render_list: Rc::new(Vec::new()),
depth_list: BTreeMap::new(),
has_pending_removals: false,
mouse_children: true,
movie,
is_action_script_3: movie.is_action_script_3(),
tab_children: true,
}
}
Expand Down Expand Up @@ -928,12 +927,8 @@ impl<'gc> ChildContainer<'gc> {
self.mouse_children = mouse_children;
}

pub fn movie(&self) -> Arc<SwfMovie> {
self.movie.clone()
}

pub fn set_movie(&mut self, movie: Arc<SwfMovie>) {
self.movie = movie;
pub fn is_action_script_3(&self) -> bool {
self.is_action_script_3
}

/// Insert a child at a given render list position.
Expand Down
2 changes: 1 addition & 1 deletion core/src/display_object/loader_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl<'gc> LoaderDisplay<'gc> {
activation.gc(),
LoaderDisplayData {
base: RefLock::new(Default::default()),
container: RefLock::new(ChildContainer::new(movie.clone())),
container: RefLock::new(ChildContainer::new(&movie)),
avm2_object: Lock::new(None),
movie,
},
Expand Down
6 changes: 3 additions & 3 deletions core/src/display_object/movie_clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ impl<'gc> MovieClipData<'gc> {
tag_stream_pos: Cell::new(0),
current_frame: Cell::new(0),
audio_stream: Cell::new(None),
container: ChildContainer::new(movie),
container: ChildContainer::new(&movie),
object: None,
clip_event_handlers: Vec::new(),
clip_event_flags: Cell::new(ClipEventFlag::empty()),
Expand Down Expand Up @@ -346,11 +346,12 @@ impl<'gc> MovieClip<'gc> {
);

mc.base.base.reset_for_movie_load();
mc.container = ChildContainer::new(&movie);
mc.shared = Gc::new(
context.gc(),
MovieClipShared::with_data(
0,
movie.clone().into(),
movie.into(),
total_frames,
loader_info.map(|l| l.into()),
),
Expand All @@ -360,7 +361,6 @@ impl<'gc> MovieClip<'gc> {
mc.base.base.set_is_root(is_root);
mc.current_frame.set(0);
mc.audio_stream.set(None);
mc.container = ChildContainer::new(movie);
drop(mc);
}

Expand Down
9 changes: 3 additions & 6 deletions core/src/display_object/stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl<'gc> Stage<'gc> {
gc_context,
StageData {
base: Default::default(),
child: RefLock::new(ChildContainer::new(movie.clone())),
child: RefLock::new(ChildContainer::new(&movie)),
background_color: Cell::new(None),
letterbox: Cell::new(Letterbox::Fullscreen),
// This is updated when we set the root movie
Expand Down Expand Up @@ -229,12 +229,9 @@ impl<'gc> Stage<'gc> {
}

pub fn set_movie(self, gc_context: &Mutation<'gc>, movie: Arc<SwfMovie>) {
self.0.movie.replace(movie.clone());

// Stage is the only DO that has a fake movie set and then gets the real movie set.
unlock!(Gc::write(gc_context, self.0), StageData, child)
.borrow_mut()
.set_movie(movie);
*self.raw_container_mut(gc_context) = ChildContainer::new(&movie);
self.0.movie.replace(movie.clone());
}

pub fn set_loader_info(self, gc_context: &Mutation<'gc>, loader_info: Avm2Object<'gc>) {
Expand Down
Loading