core: Add flag for display objects which were placed by AS3.

In AS3. objects placed by scripts *cannot* be removed by `RemoveObject` tags. This enforces that in Ruffle.
This commit is contained in:
David Wendt 2020-10-23 22:19:30 -04:00 committed by Mike Welsh
parent 8c9f8520b3
commit f3ae65df93
2 changed files with 51 additions and 13 deletions

View File

@ -358,6 +358,18 @@ impl<'gc> DisplayObjectBase<'gc> {
} }
} }
fn placed_by_script(&self) -> bool {
self.flags.contains(DisplayObjectFlags::PlacedByScript)
}
fn set_placed_by_script(&mut self, value: bool) {
if value {
self.flags.insert(DisplayObjectFlags::PlacedByScript);
} else {
self.flags.remove(DisplayObjectFlags::PlacedByScript);
}
}
fn swf_version(&self) -> u8 { fn swf_version(&self) -> u8 {
self.parent self.parent
.map(|p| p.swf_version()) .map(|p| p.swf_version())
@ -736,6 +748,16 @@ pub trait TDisplayObject<'gc>:
false false
} }
/// Whether this display object has been created by ActionScript 3.
/// When this flag is set, changes from SWF `RemoveObject` tags are
/// ignored.
fn placed_by_script(&self) -> bool;
/// Sets whether this display object has been created by ActionScript 3.
/// When this flag is set, changes from SWF `RemoveObject` tags are
/// ignored.
fn set_placed_by_script(&self, context: MutationContext<'gc, '_>, value: bool);
/// Executes and propagates the given clip event. /// Executes and propagates the given clip event.
/// Events execute inside-out; the deepest child will react first, followed by its parent, and /// Events execute inside-out; the deepest child will react first, followed by its parent, and
/// so forth. /// so forth.
@ -1123,6 +1145,12 @@ macro_rules! impl_display_object_sansbounds {
.$field .$field
.set_transformed_by_script(value) .set_transformed_by_script(value)
} }
fn placed_by_script(&self) -> bool {
self.0.read().$field.placed_by_script()
}
fn set_placed_by_script(&self, context: gc_arena::MutationContext<'gc, '_>, value: bool) {
self.0.write(context).$field.set_placed_by_script(value)
}
fn instantiate( fn instantiate(
&self, &self,
gc_context: gc_arena::MutationContext<'gc, '_>, gc_context: gc_arena::MutationContext<'gc, '_>,
@ -1255,6 +1283,10 @@ enum DisplayObjectFlags {
/// Whether this object has been transformed by ActionScript. /// Whether this object has been transformed by ActionScript.
/// When this flag is set, changes from SWF `PlaceObject` tags are ignored. /// When this flag is set, changes from SWF `PlaceObject` tags are ignored.
TransformedByScript, TransformedByScript,
/// Whether this object has been placed on the timeline by ActionScript 3.
/// When this flag is set, changes from SWF `RemoveObject` tags are ignored.
PlacedByScript,
} }
pub struct ChildIter<'gc> { pub struct ChildIter<'gc> {

View File

@ -1198,7 +1198,9 @@ impl<'gc> MovieClip<'gc> {
.children .children
.insert(depth, child); .insert(depth, child);
if let Some(prev_child) = prev_child { if let Some(prev_child) = prev_child {
self.remove_child_from_exec_list(context, prev_child); if !prev_child.placed_by_script() {
self.remove_child_from_exec_list(context, prev_child);
}
} }
self.0 self.0
.write(context.gc_context) .write(context.gc_context)
@ -1275,8 +1277,10 @@ impl<'gc> MovieClip<'gc> {
}) })
.collect(); .collect();
for (depth, child) in children { for (depth, child) in children {
self.0.write(context.gc_context).children.remove(&depth); if !child.placed_by_script() {
self.remove_child_from_exec_list(context, child); self.0.write(context.gc_context).children.remove(&depth);
self.remove_child_from_exec_list(context, child);
}
} }
true true
} else { } else {
@ -1704,9 +1708,11 @@ impl<'gc> MovieClip<'gc> {
// Don't do this for rewinds, because they conceptually // Don't do this for rewinds, because they conceptually
// start from an empty display list, and we also want to examine // start from an empty display list, and we also want to examine
// the old children to decide if they persist (place_frame <= goto_frame). // the old children to decide if they persist (place_frame <= goto_frame).
let child = self.0.write(context.gc_context).children.remove(&depth); if let Some(child) = self.0.read().children.get(&depth).cloned() {
if let Some(child) = child { if !child.placed_by_script() {
self.remove_child_from_exec_list(context, child); self.0.write(context.gc_context).children.remove(&depth);
self.remove_child_from_exec_list(context, child);
}
} }
} }
Ok(()) Ok(())
@ -2944,14 +2950,14 @@ impl<'gc, 'a> MovieClip<'gc> {
} else { } else {
reader.read_remove_object_2() reader.read_remove_object_2()
}?; }?;
let child = self
.0 if let Some(child) = self.0.read().children.get(&remove_object.depth.into()).cloned() {
.write(context.gc_context) if !child.placed_by_script() {
.children self.0.write(context.gc_context).children.remove(&remove_object.depth.into());
.remove(&remove_object.depth.into()); self.remove_child_from_exec_list(context, child);
if let Some(child) = child { }
self.remove_child_from_exec_list(context, child);
} }
Ok(()) Ok(())
} }