From bd27a6bbd61bd8ebfe2955c59ff6b19a24539238 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 11 Sep 2024 21:04:49 -0500 Subject: [PATCH] avm2: Call set_expose_content for swf loaded through Loader This exposes non-null 'content' and 'url' fields to the SWF at the proper time --- .../avm2/globals/flash/display/loader_info.rs | 19 ++++++++------- core/src/loader.rs | 4 ++++ .../avm2/loader_loadbytes_events/Loadable.as | 20 ++++++++++++---- .../swfs/avm2/loader_loadbytes_events/Test.as | 14 +++++------ .../avm2/loader_loadbytes_events/loadable.fla | Bin 3919 -> 3883 bytes .../avm2/loader_loadbytes_events/loadable.swf | Bin 738 -> 953 bytes .../avm2/loader_loadbytes_events/output.txt | 22 ++++++++++-------- .../avm2/loader_loadbytes_events/test.swf | Bin 2678 -> 2983 bytes 8 files changed, 47 insertions(+), 32 deletions(-) mode change 100644 => 100755 tests/tests/swfs/avm2/loader_loadbytes_events/loadable.fla diff --git a/core/src/avm2/globals/flash/display/loader_info.rs b/core/src/avm2/globals/flash/display/loader_info.rs index 2d0c0c644..1c3294cec 100644 --- a/core/src/avm2/globals/flash/display/loader_info.rs +++ b/core/src/avm2/globals/flash/display/loader_info.rs @@ -352,15 +352,16 @@ pub fn get_url<'gc>( this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(loader_stream) = this - .as_loader_info_object() - .and_then(|o| o.as_loader_stream()) - { - let root = match &*loader_stream { - LoaderStream::NotYetLoaded(_, _, false) => return Ok(Value::Null), - LoaderStream::NotYetLoaded(root, _, true) | LoaderStream::Swf(root, _) => root, - }; - return Ok(AvmString::new_utf8(activation.context.gc_context, root.url()).into()); + if let Some(loader_info) = this.as_loader_info_object() { + if !loader_info.expose_content() { + return Ok(Value::Null); + } + if let Some(loader_stream) = loader_info.as_loader_stream() { + let root = match &*loader_stream { + LoaderStream::NotYetLoaded(root, _, _) | LoaderStream::Swf(root, _) => root, + }; + return Ok(AvmString::new_utf8(activation.context.gc_context, root.url()).into()); + } } Ok(Value::Undefined) diff --git a/core/src/loader.rs b/core/src/loader.rs index 1117b9309..d96f8bdc4 100644 --- a/core/src/loader.rs +++ b/core/src/loader.rs @@ -2521,6 +2521,10 @@ impl<'gc> Loader<'gc> { "addChild at the correct time" ); + if let Some(loader_info) = loader_info.as_loader_info_object() { + loader_info.set_expose_content(); + } + // Note that we do *not* use the 'addChild' method here: // Per the flash docs, our implementation always throws // an 'unsupported' error. Also, the AVM2 side of our movie diff --git a/tests/tests/swfs/avm2/loader_loadbytes_events/Loadable.as b/tests/tests/swfs/avm2/loader_loadbytes_events/Loadable.as index c4ab2dc03..3c84ee07b 100644 --- a/tests/tests/swfs/avm2/loader_loadbytes_events/Loadable.as +++ b/tests/tests/swfs/avm2/loader_loadbytes_events/Loadable.as @@ -1,16 +1,26 @@ package { import flash.display.Stage; import flash.display.Sprite; + import flash.display.MovieClip; + import flash.events.Event; - public class Loadable { - public function Loadable(stage: Stage) { - trace("Hello from loaded SWF!"); - + public class Loadable extends MovieClip { + public function Loadable() { + trace("Hello from loaded SWF:"); + trace("Loaded swf loaderInfo.url: " + this.urlPrefix(this.loaderInfo.url) + " content: " + this.loaderInfo.content); + this.addEventListener(Event.ADDED_TO_STAGE, this.onAddedToStage); var circle:Sprite = new Sprite(); circle.graphics.beginFill(0xFFCC00); circle.graphics.drawCircle(50, 50, 50); - stage.addChild(circle); + this.addChild(circle); + } + private function onAddedToStage(event: *) { + trace("Added to stage: this.loaderInfo.url = " + this.urlPrefix(this.loaderInfo.url) + " this.loaderInfo.content = " + this.loaderInfo.content); + } + + private function urlPrefix(url: String): String { + return url ? url.substr(0, 8) : url; } } } \ No newline at end of file diff --git a/tests/tests/swfs/avm2/loader_loadbytes_events/Test.as b/tests/tests/swfs/avm2/loader_loadbytes_events/Test.as index 23f353df9..249d37e9f 100644 --- a/tests/tests/swfs/avm2/loader_loadbytes_events/Test.as +++ b/tests/tests/swfs/avm2/loader_loadbytes_events/Test.as @@ -11,6 +11,10 @@ super(); addFrameScript(0,this.frame1); } + + function urlPrefix(url: String): String { + return url ? url.substring(0, 8) : url; + } function frame1() { import flash.display.Loader; @@ -28,23 +32,17 @@ trace("loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded); trace("loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal); trace("loader.contentLoaderInfo.bytes = " + loader.contentLoaderInfo.bytes); - trace("loader.contentLoaderInfo.url = " + loader.contentLoaderInfo.url); + trace("loader.contentLoaderInfo.url = " + this.urlPrefix(loader.contentLoaderInfo.url)); var bytes = ByteArray(new loadableSwf); function dump(event:Event) { - var url = loader.contentLoaderInfo.url; - if (url) { - // This truncates the path to 'file:///' to make the output - // reproducible across deifferent machines - url = url.substr(0, 8); - } trace("Event " + event + ": " + "loader.numChildren = " + loader.numChildren + ", loader.content = " + loader.content + ", loader.contentLoaderInfo.bytesLoaded = " + loader.contentLoaderInfo.bytesLoaded + ", loader.contentLoaderInfo.bytesTotal = " + loader.contentLoaderInfo.bytesTotal - + ", loader.contentLoaderInfo.url = " + url); + + ", loader.contentLoaderInfo.url = " + self.urlPrefix(loader.contentLoaderInfo.url)); trace("bytes.position = " + bytes.position); } diff --git a/tests/tests/swfs/avm2/loader_loadbytes_events/loadable.fla b/tests/tests/swfs/avm2/loader_loadbytes_events/loadable.fla old mode 100644 new mode 100755 index 09a7ae5125eba999eb17375ca17661192c53dda8..a72921c1c0d9bd1a29ae54ada21af23dc9e3124a GIT binary patch delta 2924 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 z6951J0000000000004=TYz{yHypydCB^(T2E7lwU000~S000vJ000000000000000 W>r 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 U)coVIhLsfh#5zKM0h)YIXQ`!a4gdfE diff --git a/tests/tests/swfs/avm2/loader_loadbytes_events/output.txt b/tests/tests/swfs/avm2/loader_loadbytes_events/output.txt index 286ed68cb..5db6543ba 100644 --- a/tests/tests/swfs/avm2/loader_loadbytes_events/output.txt +++ b/tests/tests/swfs/avm2/loader_loadbytes_events/output.txt @@ -4,24 +4,26 @@ loader.contentLoaderInfo.bytesLoaded = 0 loader.contentLoaderInfo.bytesTotal = 0 loader.contentLoaderInfo.bytes = null loader.contentLoaderInfo.url = null -Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=738]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 738, loader.contentLoaderInfo.url = null +Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=953]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 0, loader.contentLoaderInfo.bytesTotal = 953, loader.contentLoaderInfo.url = null bytes.position = 0 -Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=738 bytesTotal=738]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 738, loader.contentLoaderInfo.bytesTotal = 738, loader.contentLoaderInfo.url = null +Event [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=953 bytesTotal=953]: loader.numChildren = 0, loader.content = null, loader.contentLoaderInfo.bytesLoaded = 953, loader.contentLoaderInfo.bytesTotal = 953, loader.contentLoaderInfo.url = null bytes.position = 0 +Hello from loaded SWF: +Loaded swf loaderInfo.url: null content: null +Added to stage: this.loaderInfo.url = file:/// this.loaderInfo.content = [object Loadable] Framescript frame 1 -Hello from loaded SWF! loader.contentLoaderInfo === loader.content.loaderInfo : true loader.contentLoaderInfo.content === loader.content : true -Event [Event type="init" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LoadableMainTimeline], loader.contentLoaderInfo.bytesLoaded = 738, loader.contentLoaderInfo.bytesTotal = 738, loader.contentLoaderInfo.url = file:/// +Event [Event type="init" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object Loadable], loader.contentLoaderInfo.bytesLoaded = 953, loader.contentLoaderInfo.bytesTotal = 953, loader.contentLoaderInfo.url = file:/// bytes.position = 0 -Event [Event type="complete" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object LoadableMainTimeline], loader.contentLoaderInfo.bytesLoaded = 738, loader.contentLoaderInfo.bytesTotal = 738, loader.contentLoaderInfo.url = file:/// +Event [Event type="complete" bubbles=false cancelable=false eventPhase=2]: loader.numChildren = 1, loader.content = [object Loadable], loader.contentLoaderInfo.bytesLoaded = 953, loader.contentLoaderInfo.bytesTotal = 953, loader.contentLoaderInfo.url = file:/// bytes.position = 0 -Stage children before addChild attempt: 3 +Stage children before addChild attempt: 2 loader.numChildren before addChild attempt: 1 -loader.content before addChild attempt: [object LoadableMainTimeline] -Stage children after addChild attempt: 4 +loader.content before addChild attempt: [object Loadable] +Added to stage: this.loaderInfo.url = file:/// this.loaderInfo.content = [object Loadable] +Stage children after addChild attempt: 3 loader.numChildren after addChild attempt: 0 -loader.content after addChild attempt: [object LoadableMainTimeline] +loader.content after addChild attempt: [object Loadable] Framescript frame 2 Framescript frame 1 -Hello from loaded SWF! diff --git a/tests/tests/swfs/avm2/loader_loadbytes_events/test.swf b/tests/tests/swfs/avm2/loader_loadbytes_events/test.swf index 756c174898d0c1d77554cfc63316ee50b56fac1e..c68c6ad250e30aadf6c81fabc1e7dad54641f9b1 100644 GIT binary patch literal 2983 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 dh=-x2A1#TdFE9xDzcnM6fr~iZ)+c zW7n>B2hGlV`xaLCT%Fy8ebs9!Oor}!a4b=RxDZ$zw2xNj12B|$4dVdrRoP^jc~}5I z?-RQ$0}TU)l-oB|PaNF&+QFRxC4EEYec)jKLF(Xd#I)MHeXH>CmhDAd9`ibL+}~cm zn%{k>_)21l?zVETDLj`t^4kQ>TidQ)zC83jIdJTL^%d2guk{bUobs7A`$04H>r+>E zoxHUE=%Y=KtUvtSAMbqr>DNoX`kK9Q>64EK9N#+cd^z=v$A49z3q99xxTvZ8n(q6J zcfh-GZv4u?la5X1^fz1fx&B<-zvaW?S5x;~cPAJ1y?3F%<;32{({Ti=}>Bc!%ddZ>qZuiI?xc{M_7&-JhO1 zcHf6DulXQkIk5Yz|c9g=f9cq!`qG=DO-DN&-u9+sTU@+4H_Y8R+wwzj$2T=?*SpgGwYz2W(2>o`(}PmyGhSy*!8?Y* zm6+;y-0_0r3T$a+y+TvnxubmRV3A$$gr>LQu?>TBjxVmVY97scv2O6tN4tt|e3vH{ zf0&A;W~OSJ9cNFg;(IIpx&2||R0X?8Wq~gJgWw%}k!Zu!LwjC6b+&g~^3g~3wXS`3 zUb*U*_3zchJ__E??)v0Xy%B4A;S5>u#;&2)w|~3hqgjnl4OZ{osej>5P5b5)-~RS! z9Nx3;oiiK1`F-k)vrn6!Trt=9@!R1*>MOHOT~uvQQJG(+WS-l+=P#R_HDBs_H+H5y ze+!qcnZJ@vp`Lun(6ZiXuqLD&ozvL-)K@3n?{A)`eC^~s>8iau2JaKwJ%80dBsSh7 zt~LH{>BXH-?Mqy~qH*JWt6x+doV{S(71!?5r!IbX^`$Ml68?L#Wa0g=f4yn-v4o&3 zy4&SI$9*|KT1T(W;SbOywbfp_w;~v%8U5%md)T|MkD)690efGBG2YZP(m|%p>$f}Y z?Oxi_vvdYs zfniQzk))%yZJ1VXEU`N2ZmXLP_}e-Io^CrsTlr)?{=k}`wKL$qmv%70QOp_)I7&Nu zt=ue2$0+IS!NosMY_!!#%k=ZK#*T(@jT+sXrp38hemR_=Cjx?TiDhlrY%U9%KRpgXCx-?^I(Y>cmJJ zbiS^RO1H=B4A4GxnOZW&jL%g^Inh=jKN%fPpXeKHqmEw=-ga_|W6YeNifZ&Tb}tGN zk6M}^i9xH0$O@jU>+}abjK|N-g5VL+Fal`-ca_YYOh;W-RyHAt&O7QYQHw_{5#x^> z-r18@HPWpgs3NHx3u9kRs~y8FZ>L@8XsFR<<=d{dGYs9)$&{!CLE7sgf*d>dE=x6O zT+W1)lM+S4(6k99*t+3P z!zZSlbW=|K|F)s#$7zU>@e_MfU@{vUm%AlZ1gP!l~If zH3z3ka8g`G%}392Y9UU_DoJrQRfAJ?h-M;5GfC8tL>*Oz%qFTCC*ewxXd#JJBw?jm zk$DeEwvnWbB<&>G9v}HV!+kI?I>yJ01!KkpdP30#ZVd1C1b~YC9}r-Oive6f2_QiL zAx;T_2#_L51SA**fEY+1P(UDnQX!NnA&C$q9wP)mBElhtk_sU%0FZ)00l?xQDS&t! zfDmG2*lG^`luh{86Z zn_?xLoU}Dg3&JjmDGp<0h$zKziR2eIVHU``3AIp*LbSGcSAr{X)4Oc;`8X*6Y|eQt z(2A;)Fa*tLP0r0f7s@RN@4Jp8F3XOSLv{89&*JB zkgJk&%}1^UNVS4X#+Bj9I8dzyynZ>ttN0?OaGvLf@rq%og-A+{BdK0astQRNhh5bu z)@akXGSL*dm>Og+;wQ{mi;z{zPnxr8IUZ-3vLIWBT#Ls9E=G3!NMJp(mW%{0LHQLZ zzwBuY<&F&-nr2)=JF5Z2(onOh1M*Ldf8uNw4a=n)v>gIOO{QTO`Pmd2wr1C51H@vd zgk4i<=*3LaAge@amU2ckQ7W>R@peKKQ6a<