From d3265bfd60a03655788e86e8afcab292827594dc Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Wed, 18 Nov 2020 14:10:17 -0800 Subject: [PATCH] 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. --- core/src/avm1/activation.rs | 20 ++++++++++++------ core/src/avm1/globals/color.rs | 4 ++-- core/src/avm1/globals/movie_clip.rs | 12 ++++++++--- .../swfs/avm1/movieclip_hittest/output.txt | 4 ++++ .../swfs/avm1/movieclip_hittest/test.fla | Bin 7934 -> 7962 bytes .../swfs/avm1/movieclip_hittest/test.swf | Bin 1706 -> 1715 bytes 6 files changed, 29 insertions(+), 11 deletions(-) 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 a4ee111b897686e8326a8c2f4689c1029e83f2d3..19ba4b278b234f0ee6be75c56d250fb37a966804 100644 GIT binary patch delta 2931 zcmY*bcQhLc7muw7MGz~si^d*pmD+o+K2b3f8Z{$Tv#HutjHo?I)TovcyDiTq+ESyu z5zrFk62twf6hTN%ZFY*9JtT zr{%NTt3Z z(Gf1WlM_g# zy6eOv2i*mg7w;l1I_)o6eyV1{7m5l0R#k;DmZe+pB^j(N9JnHE|f{f6>Nc zNFS$>B+lF0ssP(pC{evUN))8;>+&*NrINAIO46u|7{smTd*(Nz@PjW0M4&fSgJWOL z@_^MZ&*zoNVlEF+1J9DtRCmDo$VKr3EH2Ak{Va18IH9FKqbM+xE44A4?m6rbG~CTz zF-FyY;PO@`KHiiM%bV5x7^;&?X6Ek@Emy02riilsy5dcL)xz$2RMD`k=#+l@xIdVE zE|BEtflhM1indElBHWhOQ^g8!t+R^I1YoO?Xp9N<@1&FZ9x7=cY1P3haAhH<}SNHqP05`H?(9fswiM zZetnbOY70iYf$!SG*lPGbjUH5b~5_8@K8Zc;IOOp#OAC^;_=VWHvm#0XA?{ zWRTMuva3CT5!tDu?&AjaQ#j2C+Mbuc5flCra{)3DECYje77tawBxh_t+6s40v#DWv zdpd-qsQ#!eI;sK|&$Qe%JLbJV?g?U7THkfZ?=iwg2ogTcP0|HV4LtNsG^wg$(o-|~ z4E%MlbGz-{y*6pKNosB?jmwk1R$TAZrzsqB@dL|?jam>^I&C^bbRtPUL}^`9B8omJ;U=(WD-+C9u-W(Ik7(%S-QWsFUrFR(TA4F{ z--qv5+w|8 z7SdN+X(i9{vgNv1w%s=f!$4Ng0G9^>vTcFC4nM^ncDYXqSM6jeCNZyb)UL$cmy701 zbY%Csts4cD2<@b4(X~q_C@uD1cY&lMWN|-IFot8CvyNlVryAva=>&X@ueqo-7Fz$_ zXF<q{Y=+H$i+^TB%GZtz~;%nMZqetL)@*g`{=&>v81<@+<+`dN$#J z^wdv*GmkV!4xBamZelB5xR3ZpR^}?L5{*^_lRxj9i0Q7byer{@+#BUi$4Enx)j2F~ zaG1pkw!vdE2D3xjyq^^gR`;W^T#Vn2TSX{PSYYe|!ryc6>0E%&u^f8Ta2!VG!i7g${*4{Edfi}+r`Md*T>WZeL*-r@Dcri8O%Bf{fQ5f~!qmbWwt5M0?vw^% zCqg>m^==opVVB3;uO(W;Tg`xuhwE6H4n1rxSWOTU+ zsB_dFlQ{xkzeH1JZdzmV-sRejCr-^FvoY#+OM$4zQuXS5M`_+YRwGs zIYJ)}I;lImqo^akAKBrpnoQHjQb|lC?82F&<&4*RD)KrB^5OO)bRn_ob0{k!`zB zxAs?~hj%-0<4J35F9?df7%i+#Ua3onESAr^`r)`_m#}p2GQ+<53Cn=#K+OEN=a3#S z*of(FhsUBejc%TfM4*1Zz!cCd!aY)EFaMb8b6Vu8LCvwc3CuPusRH#(`A)&^-7pQM zYe2#n*hHWIvgwa5A~p-8?f^~VSAv$1x&=(8Y$-mW#n7FY@}iixcd@he(qzv5a{Il8 z6-iDPB50ZY(T!DFmxzhr0ayuwC~;xKa+I8r%&=b|86qFIiVy|6@#`>5kBIz=@m{{E ziSwhS;ErdM9dlBWQzBLLsw?f`M|2)3W00YN9cC|CT(8Wx_ek7V{Ym&|cG5J>3SGWyd$=I#pUms&C^2k*}uINmtO1d>Qtf=&= zK79M&?$e5+gP#xct-h@}twQ(qeFk$9tS;_Pihh~t^Pbl2D|oC3$uC&R%CzdJ!$p2- zv)J;u1N>zA1}QPy8NVy}LM<51$}jH6smbr@=M10jcjgepCVgd8mYsP!Ca~@X6X9dw zRsT~kY(@}Y_srvwRrgh$$vZb0^sEvhk3=>pitWp1Le8~(9yaWS*~mwH;g_Txm!CN6 zaN4soklvt0A}D=7kGB3i#5mdTZ5&Xi^-2faYfP&pifzwuou^<{$`rm9~R2vT-G8Sew?bCX*Pzj@-74|BsjVw_jP{Gdx*7D>C>6f zP<)16XQme}vQp_I{C$7ItW^bv%j9b3iU7ej(B$2h;_9hoLQ`0$?Ecx^h6czdfFs-5fO8wc5Q4N_`NTtIguCw}-8adLFNi6}=oNGgcB;?Gvd`v+25UG+$R| zl?!t!p+<3RXYjq}Y-|}wG*(z3>`0_K1rSw%bm=_ME&07x!vjr3^!`^&<6FU6O@C6X z{9G}n(7XG{P0rMOJgZ6KA9H+5!smpHrD*Okk<8j(VFCYtif=byjjT);=ogN~y6~)^ z%La4+E&vcfcMib01_0OqXe2_!3x)RbLju&<0X}FXGT`4Bzyt`n;}L`e>~_99=bj7A zIcSNYAXeu8d28{hLMnp)J$e9u=O6CBDiHsdkRB~7D_&Swn}&ji3rh<9y^j5P{cmvp z%lpUjfbb>fw0!)$@J$*syf8$c#trWQF=P%A1OPC|5KNX32%jx@k@de_OL#IwkWobF H@63My;oW6m delta 2899 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 J{{Ritp@gJsR2u*Q literal 1706 zcmV;b237e(S5psy5C8ypoUK<|Y!p=(KD%Xi+sdW26euikY)hfi-MQ>_mQv`pY{aA# z$_9fW*6HkQcW`!QGBZoNzPKV@qA|gcG}Kfjf)|Vr@?az}N_l`_A{9eo)JGK(eIQLV zQ4^AS&e_>+FWc^_H0eLH|M|Y}znuS^?2ZD*AOLI50kFXadh6=|0M9klIRNO?6UoRx zZw!v6WyOe~PrI8_rm00JYGh=@JL2=I`Y^?C97oYC#j+lR@EBReO?)3FV_A7?TD}uPcw+lJE zl9Y(h-E@eK1q1#7%k7IYOf>B8jj_=H>x%`L5Z6Nrolh;O^{PT9Eh?s^miQNHbEpNi zeq9=t(0r6o4Km%K9@fYC8J6DH%`jok7mG3e5EG=?K#&d=R7*(A<|^va2{94V)il`! zjn@qkcWQTYp;fp|q`whqahrmuj94%l4Ry2O(uFOe7Ss}p3*z?0VjLZeG91g4E@%<8 zpr-zxHZ3UuXE&zUr~f?C31N0ZwTv#4D<~mQqAcQQ2AWca+!zTVl2r9HZ+7vTCQAa3 z1*uVwky3?cM)(t=Cy8&5PO1njnG$7Pf?W{w*k@*D_Ea6O0pOKy9TcD_7*{nYi%An6 z&ln~|i5gNuglZBBk}k-i7d|1GDM^9RXa~BKOsJ?PG*cqv1v7(gD^SE zMZ?^|`urV`VcB+&UjWtuz}Tp2nt0Nr;nV{U?DK~^AnWtB<27_Fu`1?Suu`c8Ih1Oc zu^0qB%xh4pgzRH{n4AySU#z0YGDr!gN?f?rC39ecC~KChP=<7_WICY`O2@~Pt{#r$ zds?W_3NR!MFISdrnMt-=HylkL-5CuMi zo>mSJy&DiK%_WrFB3}Sa4@Z+uJ&w;4Pxh(M7Hg6f)WJdw)Hsr)ceaS7Pca>6ulUsN zuQ*g?qg1)xVN=Jg*LX99n)<-UWqUp*nUd|+TzF@PXI^l=g33yU@OmjSDM-KYx=5TV zMWhge0%LYi4cf4^wT;yS*R$x0u!g&G1v*qKgA-0ZiE@Mu-Jg1)6 zhly=)kGX)$wgi4HvGyXSVJXc6X|?&fNh6qw?!Pu!WvIc{D6$8K+)keI)}YRc6fIt!TVkuzI_NxKic ze)8q_s@^!5(dyKh}Sy81=w{Kro!r-mke5$>hH`*(9I zf1Z%0j@|&re%$N0$^3Br%+`rF&Vu)va#e2O!&v?EhmN03-)fph$`Pbo1&&OCOP8+j0LX;rD6Bcy686fA{L155SufRW7IOuB*-F+P|4j?zwstj3>bT`(UO9{~TX| zG9tNF0k9WWT;oR8QShQYebwo-BiP2%C%+?zE6#dk+w2JBe!aUcXMKyj0(y|!36l1f zBewI8*>E~2ANk97QU`8-V*BD8Q04f&*BW=7o~&)9?bj})F3AV&J?Fsb+n=vHb?=(G zw|3Jl+pYtX`Mg|(44lA