avm1: Add allow_empty param to Activation:resolve_target_display_object
In some cases, the empty string path "" should resolve to the starting clip. In other cases, it should be considered invalid. Add a parameter to control this behavior, and set this to false for MovieClip::hit_test to prevent `clip.hitTest("")` from returning true.
This commit is contained in:
parent
4c01022a38
commit
d3265bfd60
|
@ -681,7 +681,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let target = self.context.avm1.pop();
|
||||
let source = self.context.avm1.pop();
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let source_clip = self.resolve_target_display_object(start_clip, source)?;
|
||||
let source_clip = self.resolve_target_display_object(start_clip, source, true)?;
|
||||
|
||||
if let Some(movie_clip) = source_clip.and_then(|o| o.as_movie_clip()) {
|
||||
let _ = globals::movie_clip::duplicate_movie_clip_with_bias(
|
||||
|
@ -1159,7 +1159,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let prop_index = self.context.avm1.pop().into_number_v1() as usize;
|
||||
let path = self.context.avm1.pop();
|
||||
let ret = if let Some(target) = self.target_clip() {
|
||||
if let Some(clip) = self.resolve_target_display_object(target, path)? {
|
||||
if let Some(clip) = self.resolve_target_display_object(target, path, true)? {
|
||||
let display_properties = self.context.avm1.display_properties;
|
||||
let props = display_properties.write(self.context.gc_context);
|
||||
if let Some(property) = props.get_by_index(prop_index) {
|
||||
|
@ -1264,7 +1264,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
target.as_display_object()
|
||||
} else {
|
||||
let start = self.target_clip_or_root();
|
||||
self.resolve_target_display_object(start, target.clone())?
|
||||
self.resolve_target_display_object(start, target.clone(), true)?
|
||||
}
|
||||
} else {
|
||||
Some(self.target_clip_or_root())
|
||||
|
@ -1794,7 +1794,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
fn action_remove_sprite(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||
let target = self.context.avm1.pop();
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let target_clip = self.resolve_target_display_object(start_clip, target)?;
|
||||
let target_clip = self.resolve_target_display_object(start_clip, target, true)?;
|
||||
|
||||
if let Some(target_clip) = target_clip.and_then(|o| o.as_movie_clip()) {
|
||||
let _ = globals::movie_clip::remove_movie_clip(target_clip, self, &[]);
|
||||
|
@ -1826,7 +1826,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let prop_index = self.context.avm1.pop().coerce_to_u32(self)? as usize;
|
||||
let path = self.context.avm1.pop();
|
||||
if let Some(target) = self.target_clip() {
|
||||
if let Some(clip) = self.resolve_target_display_object(target, path)? {
|
||||
if let Some(clip) = self.resolve_target_display_object(target, path, true)? {
|
||||
let display_properties = self.context.avm1.display_properties;
|
||||
let props = display_properties.read();
|
||||
if let Some(property) = props.get_by_index(prop_index) {
|
||||
|
@ -1957,7 +1957,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
fn action_start_drag(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||
let target = self.context.avm1.pop();
|
||||
let start_clip = self.target_clip_or_root();
|
||||
let display_object = self.resolve_target_display_object(start_clip, target)?;
|
||||
let display_object = self.resolve_target_display_object(start_clip, target, true)?;
|
||||
if let Some(display_object) = display_object {
|
||||
let lock_center = self.context.avm1.pop();
|
||||
let constrain = self.context.avm1.pop().as_bool(self.current_swf_version());
|
||||
|
@ -2406,10 +2406,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
///
|
||||
/// A target path always resolves via the display list. It can look
|
||||
/// at the prototype chain, but not the scope chain.
|
||||
///
|
||||
/// `allow_empty` will allow the empty string to resolve to the start movie clip.
|
||||
pub fn resolve_target_display_object(
|
||||
&mut self,
|
||||
start: DisplayObject<'gc>,
|
||||
target: Value<'gc>,
|
||||
allow_empty: bool,
|
||||
) -> Result<Option<DisplayObject<'gc>>, Error<'gc>> {
|
||||
// If the value you got was a display object, we can just toss it straight back.
|
||||
if let Value::Object(o) = target {
|
||||
|
@ -2422,6 +2425,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
// This means that values like `undefined` will resolve to clips with an instance name of
|
||||
// `"undefined"`, for example.
|
||||
let path = target.coerce_to_string(self)?;
|
||||
|
||||
if !allow_empty && path.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let root = start.root();
|
||||
let start = start.object().coerce_to_object(self);
|
||||
Ok(self
|
||||
|
|
|
@ -82,9 +82,9 @@ fn target<'gc>(
|
|||
// depending on which timeline its called from!
|
||||
let target = this.get("target", activation)?;
|
||||
// Undefined or empty target is no-op.
|
||||
if target != Value::Undefined && !matches!(&target, &Value::String(ref s) if s.is_empty()) {
|
||||
if target != Value::Undefined {
|
||||
let start_clip = activation.target_clip_or_root();
|
||||
activation.resolve_target_display_object(start_clip, target)
|
||||
activation.resolve_target_display_object(start_clip, target, false)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
@ -138,8 +138,11 @@ pub fn hit_test<'gc>(
|
|||
return Ok(ret.into());
|
||||
}
|
||||
} else if args.len() == 1 {
|
||||
let other = activation
|
||||
.resolve_target_display_object(movie_clip.into(), args.get(0).unwrap().clone())?;
|
||||
let other = activation.resolve_target_display_object(
|
||||
movie_clip.into(),
|
||||
args.get(0).unwrap().clone(),
|
||||
false,
|
||||
)?;
|
||||
if let Some(other) = other {
|
||||
return Ok(other
|
||||
.world_bounds()
|
||||
|
@ -921,7 +924,9 @@ fn swap_depths<'gc>(
|
|||
let mut depth = None;
|
||||
if let Value::Number(n) = arg {
|
||||
depth = Some(crate::ecma_conversions::f64_to_wrapping_i32(n).wrapping_add(AVM_DEPTH_BIAS));
|
||||
} else if let Some(target) = activation.resolve_target_display_object(movie_clip.into(), arg)? {
|
||||
} else if let Some(target) =
|
||||
activation.resolve_target_display_object(movie_clip.into(), arg, false)?
|
||||
{
|
||||
if let Some(target_parent) = target.parent() {
|
||||
if DisplayObject::ptr_eq(target_parent, parent.into()) {
|
||||
depth = Some(target.depth())
|
||||
|
@ -996,6 +1001,7 @@ fn get_bounds<'gc>(
|
|||
activation.resolve_target_display_object(
|
||||
movie_clip.into(),
|
||||
AvmString::new(activation.context.gc_context, path.to_string()).into(),
|
||||
false,
|
||||
)?
|
||||
}
|
||||
None => Some(movie_clip.into()),
|
||||
|
|
|
@ -78,3 +78,7 @@ true
|
|||
// circle.hitTest('/')
|
||||
true
|
||||
|
||||
// String path, ''
|
||||
// circle.hitTest('')
|
||||
false
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue