diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index b8485b78e..5b3cae996 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -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, 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, 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>, 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 diff --git a/core/src/avm1/globals/color.rs b/core/src/avm1/globals/color.rs index 5991b7309..43590baf3 100644 --- a/core/src/avm1/globals/color.rs +++ b/core/src/avm1/globals/color.rs @@ -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) } diff --git a/core/src/avm1/globals/movie_clip.rs b/core/src/avm1/globals/movie_clip.rs index 4c56c08ee..e9c2efb9a 100644 --- a/core/src/avm1/globals/movie_clip.rs +++ b/core/src/avm1/globals/movie_clip.rs @@ -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()), diff --git a/core/tests/swfs/avm1/movieclip_hittest/output.txt b/core/tests/swfs/avm1/movieclip_hittest/output.txt index 771e6edc0..e7dc6f9be 100644 --- a/core/tests/swfs/avm1/movieclip_hittest/output.txt +++ b/core/tests/swfs/avm1/movieclip_hittest/output.txt @@ -78,3 +78,7 @@ true // circle.hitTest('/') true +// String path, '' +// circle.hitTest('') +false + diff --git a/core/tests/swfs/avm1/movieclip_hittest/test.fla b/core/tests/swfs/avm1/movieclip_hittest/test.fla index a4ee111b8..19ba4b278 100644 Binary files a/core/tests/swfs/avm1/movieclip_hittest/test.fla and b/core/tests/swfs/avm1/movieclip_hittest/test.fla differ diff --git a/core/tests/swfs/avm1/movieclip_hittest/test.swf b/core/tests/swfs/avm1/movieclip_hittest/test.swf index 97e4712d7..8a8e49a4f 100644 Binary files a/core/tests/swfs/avm1/movieclip_hittest/test.swf and b/core/tests/swfs/avm1/movieclip_hittest/test.swf differ