From a50f5cd07274a3510cd2c10de826e3c9965a911e Mon Sep 17 00:00:00 2001 From: Kornelius Rohrschneider Date: Tue, 8 Aug 2023 19:42:16 +0200 Subject: [PATCH] avm1: Remove delay to enter the unloaded state for non-root MovieClips avm1_unload_movie has been adapted to only enter the unloaded state with one frame delay if the MovieClip is a root MovieClip. The unloaded state is now immediately entered for non-root MovieClips. This fixes the regressions #12254 and #12265 which got introduced because of this delay. However, in Flash Player, even non-root MovieClips enter the unloaded state one frame after the unloadMovie command has been read. Ruffle is probably replacing a MovieClip differently to Flash, therefore introducing these regressions when trying to emulate that delay. Documentation explaining this all has been added to avm1_unload_movie. Therefore, the test movieclip_library_state_values has been added. It tests the default state and the unloaded state of a (non-root) child MovieClip that's loaded from the library. It is marked as known_failure because Ruffle currently doesn't implement the delay before entering the unloaded state for non-root MovieClips. --- core/src/display_object/movie_clip.rs | 73 +++++---- .../movieclip_library_state_values/output.txt | 78 ++++++++++ .../movieclip_library_state_values/test.as | 144 ++++++++++++++++++ .../movieclip_library_state_values/test.fla | Bin 0 -> 80384 bytes .../movieclip_library_state_values/test.swf | Bin 0 -> 2060 bytes .../movieclip_library_state_values/test.toml | 2 + 6 files changed, 267 insertions(+), 30 deletions(-) create mode 100644 tests/tests/swfs/avm1/movieclip_library_state_values/output.txt create mode 100644 tests/tests/swfs/avm1/movieclip_library_state_values/test.as create mode 100644 tests/tests/swfs/avm1/movieclip_library_state_values/test.fla create mode 100644 tests/tests/swfs/avm1/movieclip_library_state_values/test.swf create mode 100644 tests/tests/swfs/avm1/movieclip_library_state_values/test.toml diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index d6a1fbebe..b11801988 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -2420,40 +2420,53 @@ impl<'gc> MovieClip<'gc> { // after one frame if the target is a MovieClip? Test the behaviour and adapt the code // if necessary. pub fn avm1_unload_movie(&self, context: &mut UpdateContext<'_, 'gc>) { - let unloader = Loader::MovieUnloader { - self_handle: None, - target_clip: DisplayObject::MovieClip(*self), - }; - let handle = context.load_manager.add_loader(unloader); + // TODO: In Flash player, the MovieClip properties change to the unloaded state + // one frame after the unloadMovie command has been read, even if the MovieClip + // is not a root MovieClip (see the movieclip_library_state_values test). + // However, if avm1_unload and transform_to_unloaded_state are called with a one + // frame delay when the MovieClip is not a root MovieClip, regressions appear. + // Ruffle is probably replacing a MovieClip differently to Flash, therefore + // introducing these regressions when trying to emulate that delay. - let player = context - .player - .clone() - .upgrade() - .expect("Could not upgrade weak reference to player"); - let future = Box::pin(async move { - player - .lock() - .unwrap() - .update(|uc| -> Result<(), loader::Error> { - let clip = match uc.load_manager.get_loader(handle) { - Some(Loader::MovieUnloader { target_clip, .. }) => *target_clip, - None => return Err(loader::Error::Cancelled), - _ => unreachable!(), - }; - if let Some(mc) = clip.as_movie_clip() { - mc.avm1_unload(uc); - mc.transform_to_unloaded_state(uc); - } + if self.is_root() { + let unloader = Loader::MovieUnloader { + self_handle: None, + target_clip: DisplayObject::MovieClip(*self), + }; + let handle = context.load_manager.add_loader(unloader); - uc.load_manager.remove_loader(handle); + let player = context + .player + .clone() + .upgrade() + .expect("Could not upgrade weak reference to player"); + let future = Box::pin(async move { + player + .lock() + .unwrap() + .update(|uc| -> Result<(), loader::Error> { + let clip = match uc.load_manager.get_loader(handle) { + Some(Loader::MovieUnloader { target_clip, .. }) => *target_clip, + None => return Err(loader::Error::Cancelled), + _ => unreachable!(), + }; + if let Some(mc) = clip.as_movie_clip() { + mc.avm1_unload(uc); + mc.transform_to_unloaded_state(uc); + } - Ok(()) - })?; - Ok(()) - }); + uc.load_manager.remove_loader(handle); - context.navigator.spawn_future(future); + Ok(()) + })?; + Ok(()) + }); + + context.navigator.spawn_future(future); + } else { + self.avm1_unload(context); + self.transform_to_unloaded_state(context); + } } /// This makes the MovieClip enter the unloaded state in which some attributes have diff --git a/tests/tests/swfs/avm1/movieclip_library_state_values/output.txt b/tests/tests/swfs/avm1/movieclip_library_state_values/output.txt new file mode 100644 index 000000000..76ea859d1 --- /dev/null +++ b/tests/tests/swfs/avm1/movieclip_library_state_values/output.txt @@ -0,0 +1,78 @@ +_accProps = undefined +_alpha = 100 +_currentframe = 1 +_droptarget = +_focusrect = null +_framesloaded = 1 +_height = 0 +_highquality = 1 +_lockroot = false +_name = childClip +_parent = _level0 +_quality = HIGH +_rotation = 0 +_soundbuftime = 5 +_target = /childClip +_totalframes = 1 +_visible = true +_width = 0 +_x = 0 +_xmouse = 21 +_xscale = 100 +_y = 0 +_ymouse = 0 +_yscale = 100 +blendMode = normal +cacheAsBitmap = false +enabled = true +filters = +filters.length = 0 +focusEnabled = undefined +forceSmoothing = undefined +hitArea = undefined +menu = undefined +opaqueBackground = undefined +scale9Grid = undefined +scrollRect = undefined +tabChildren = undefined +tabEnabled = undefined +tabIndex = undefined +trackAsMenu = undefined +transform.colorTransform = (redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=0, greenOffset=0, blueOffset=0, alphaOffset=0) +transform.concatenatedColorTransform = (redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=0, greenOffset=0, blueOffset=0, alphaOffset=0) +transform.matrix = (a=1, b=0, c=0, d=1, tx=0, ty=0) +transform.concatenatedMatrix = (a=1, b=0, c=0, d=1, tx=0, ty=0) +transform.pixelBounds = (x=0, y=0, w=0, h=0) +useHandCursor = true +_url = movieclip_library_state_values/test.swf +getBounds(this).xMin = 6710886.4 +getBounds(this).xMax = 6710886.4 +getBounds(this).yMin = 6710886.4 +getBounds(this).yMax = 6710886.4 +getBounds(mc).xMin = 6710886.35 +getBounds(mc).xMax = 6710886.35 +getBounds(mc).yMin = 6710886.35 +getBounds(mc).yMax = 6710886.35 +getBytesLoaded() = 4 +getBytesTotal() = 4 +getDepth() = 0 +getInstanceAtDepth(0) = undefined +getNextHighestDepth() = 0 +getRect(this).xMin = 6710886.4 +getRect(this).xMax = 6710886.4 +getRect(this).yMin = 6710886.4 +getRect(this).yMax = 6710886.4 +getRect(mc).xMin = 6710886.35 +getRect(mc).xMax = 6710886.35 +getRect(mc).yMin = 6710886.35 +getRect(mc).yMax = 6710886.35 +getSWFVersion() = 10 +getTextSnapshot().getCount() = 0 + +Both targets have the same props. + +Change: Prop _currentframe is "1" on the first, but "0" on the second target. +Change: Prop _framesloaded is "1" on the first, but "0" on the second target. +Change: Prop _totalframes is "1" on the first, but "0" on the second target. +Change: Prop getBytesLoaded() is "4" on the first, but "0" on the second target. +Change: Prop getBytesTotal() is "4" on the first, but "0" on the second target. diff --git a/tests/tests/swfs/avm1/movieclip_library_state_values/test.as b/tests/tests/swfs/avm1/movieclip_library_state_values/test.as new file mode 100644 index 000000000..f9c135ba5 --- /dev/null +++ b/tests/tests/swfs/avm1/movieclip_library_state_values/test.as @@ -0,0 +1,144 @@ +// SWF Version 10 + +/* + * This test tests the default state and the unloaded state of a child MovieClip that's loaded + * from the library. + * A state of a MovieClip consists of the values of all properties and the results of some getter + * functions of the MovieClip. + * + * The default state is the state the MovieClip is in after it's loaded with attachMovie. + * The unloaded state is the state the MovieClip enters on the next frame after the MovieClip + * has been unloaded. + */ + +var childClip = this.attachMovie("childClip", "childClip", this.getNextHighestDepth()); + +var mcPropsArray1: Array; +var mcPropsArray2: Array; +var mcPropsArray3: Array; +var frameCount = 0; +this.onEnterFrame = function() { + frameCount++; + + if (frameCount == 1) { + mcPropsArray1 = getMcPropsArray(childClip); + printMcProps(mcPropsArray1); + + unloadMovie(childClip); + + mcPropsArray2 = getMcPropsArray(childClip); + trace(""); + compareMcProps(mcPropsArray1, mcPropsArray2, true); + } + + if (frameCount == 2) { + mcPropsArray3 = getMcPropsArray(childClip); + trace(""); + compareMcProps(mcPropsArray2, mcPropsArray3, true); + this.onEnterFrame = null; + } +}; + + + +/* + * This traces all elements of an McProps-Array returned by getMcPropsArray with their respective names. + */ +function printMcProps(mcPropsArray:Array) { + for (var propIterator = 0; propIterator < mcPropsArray.length; propIterator++) { + var propName = mcPropsArray[propIterator][0]; + var propValue = mcPropsArray[propIterator][1]; + trace(propName + " = " + propValue); + } +} + + +/* + * This compares two McProps-Arrays returned by getMcPropsArray. + * If traceDifferences is true, all differences will be traced. + * It returns the diverging values with their respective names in a two-dimensional array. + */ +function compareMcProps(mcPropsArray1:Array, mcPropsArray2:Array, traceDifferences:Boolean) { + if (mcPropsArray1 == undefined || mcPropsArray2 == undefined) { + trace("Error: An mcPropsArray is undefined."); + return; + } + + var differenceArray = []; + for (var propIterator = 0; propIterator < mcPropsArray1.length; propIterator++) { + if (mcPropsArray1[propIterator][1].toString() != mcPropsArray2[propIterator][1].toString()) { + var value1 = mcPropsArray1[propIterator][1]; + var value2 = mcPropsArray2[propIterator][1]; + var propName = mcPropsArray1[propIterator][0]; + differenceArray.push([propName, value1, value2]) + if (traceDifferences) { + trace("Change: Prop " + propName + " is \"" + value1 + "\" on the first, but \"" + value2 + + "\" on the second target."); + } + } + } + if (traceDifferences && differenceArray.length == 0) { + trace("Both targets have the same props."); + } + + return differenceArray; +} + + +/* + * This returns all properties and the results of some getter functions of a MovieClip with their respective + * names in a two-dimensional array. + */ +function getMcPropsArray(mc:MovieClip) { + var mcProps = []; + var simplePropNames = ["_accProps", "_alpha", "_currentframe", "_droptarget", "_focusrect", "_framesloaded", + "_height", "_highquality", "_lockroot", "_name", "_parent", "_quality", "_rotation", "_soundbuftime", + "_target", "_totalframes", "_visible", "_width", "_x", "_xmouse", "_xscale", "_y", "_ymouse", "_yscale", + "blendMode", "cacheAsBitmap", "enabled", "filters", "filters.length", "focusEnabled", "forceSmoothing", + "hitArea", "menu", "opaqueBackground", "scale9Grid", "scrollRect", "tabChildren", "tabEnabled", "tabIndex", + "trackAsMenu", "transform.colorTransform", "transform.concatenatedColorTransform", "transform.matrix", + "transform.concatenatedMatrix", "transform.pixelBounds", "useHandCursor"]; + for (var simplePropIterator = 0; simplePropIterator < simplePropNames.length; simplePropIterator++) { + var simplePropName = simplePropNames[simplePropIterator]; + var simplePropValue = eval("mc." + simplePropName); + mcProps.push([simplePropName, simplePropValue]); + } + + var url = unescape(mc._url); + if (url.indexOf("file:///") == 0) { + var urlSplit = url.split("/"); + mcProps.push(["_url", urlSplit[urlSplit.length - 2] + "/" + urlSplit[urlSplit.length - 1]]); + } else { + mcProps.push(["_url", url]); + } + + var getBoundsThis = mc.getBounds(this); + mcProps.push(["getBounds(this).xMin", getBoundsThis.xMin]); + mcProps.push(["getBounds(this).xMax", getBoundsThis.xMax]); + mcProps.push(["getBounds(this).yMin", getBoundsThis.yMin]); + mcProps.push(["getBounds(this).yMax", getBoundsThis.yMax]); + var getBoundsMc = mc.getBounds(mc); + mcProps.push(["getBounds(mc).xMin", getBoundsMc.xMin]); + mcProps.push(["getBounds(mc).xMax", getBoundsMc.xMax]); + mcProps.push(["getBounds(mc).yMin", getBoundsMc.yMin]); + mcProps.push(["getBounds(mc).yMax", getBoundsMc.yMax]); + mcProps.push(["getBytesLoaded()", mc.getBytesLoaded()]); + mcProps.push(["getBytesTotal()", mc.getBytesTotal()]); + mcProps.push(["getDepth()", mc.getDepth()]); + mcProps.push(["getInstanceAtDepth(0)", mc.getInstanceAtDepth(0)]); + mcProps.push(["getNextHighestDepth()", mc.getNextHighestDepth()]); + var getRectThis = mc.getBounds(this); + mcProps.push(["getRect(this).xMin", getRectThis.xMin]); + mcProps.push(["getRect(this).xMax", getRectThis.xMax]); + mcProps.push(["getRect(this).yMin", getRectThis.yMin]); + mcProps.push(["getRect(this).yMax", getRectThis.yMax]); + var getRectMc = mc.getBounds(mc); + mcProps.push(["getRect(mc).xMin", getRectMc.xMin]); + mcProps.push(["getRect(mc).xMax", getRectMc.xMax]); + mcProps.push(["getRect(mc).yMin", getRectMc.yMin]); + mcProps.push(["getRect(mc).yMax", getRectMc.yMax]); + mcProps.push(["getSWFVersion()", mc.getSWFVersion()]); + mcProps.push(["getTextSnapshot().getCount()", mc.getTextSnapshot().getCount()]); + + return mcProps; +} diff --git a/tests/tests/swfs/avm1/movieclip_library_state_values/test.fla b/tests/tests/swfs/avm1/movieclip_library_state_values/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..2ae6e9e47164e1580700b40c0e8419192e08f592 GIT binary patch literal 80384 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 literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm1/movieclip_library_state_values/test.swf b/tests/tests/swfs/avm1/movieclip_library_state_values/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..1fd2a0ff1af13dca362675c9569333ba95692378 GIT binary patch literal 2060 zcmV+n2=n(tS5pd-6#xKuoTXS>ciYAh9#SN=wi?HA(kN}27L{8^N+RAQD;7=BrbyjV zQ*tTQ>788xOA$2)z}SUEHV=JlpZwJ7oVKUu~9#n#eN=(gK&2X1Yi zp@wtHGP={rcBz}qXL4ET2SLTE$Ea|;6UVX26^YVzEy~+k^>|%-?l~>SIfkHCDLami z*s~h`lnc4S+G_66YHs5szgc{^QC!b`mMau%{|` z3u^1^pjJjf#x#v$ZvD|0m5qnFM}@WZot=FC(dNcRt%B?IjoNy?8XR=gwC}Cv=zfRU zBJ^h5z+1$$@9oGl&rIxxNnUer=XHXML9Wa*&zZ67IUQ96mwKE@rnZ*)R7si?zeRHb zO*2!tV%qm+e47JZG3|SE{vVpIb)gLkGvw)8x3r-TI@I+&ON|{v&oYb2XdJsVucm~d z7h8_kp`uJ(*D`e~9cH_$yzS^OFX%a2ZOMsI$_{}OQ_6;eudG2e{s#ZtA~+Od`1dKk zpWL|YXu>fo(^$!wuKkc<2B`J4|zB4jBlO0d96&i^=sNYB~6v?Ni&R`W|;Y5FWL;WGUmC zU1n`Nm@|(^F(?wuZ2U3m6pzr2C_CeVD7xxcIGFpY5zx3oe_7#mX8R!M=>c-qm5Uvl zo?6V1%c`t^$85mzIhNI6y1*s1*yf&R8sNC}mCv@(&6y|jhmQ2sHbn&|JYW!=9d{fg zO!D8t?&W>oEz=UrbHd1L5PPC*`CmRaYGtS$6D{Wy@Q`)IK6VA;L*%$Da4GN8 zNl3k1N0}ZM1;>hrdRI}s2r8gdDWil&1QCx>G9!Wjas1Wp3%s22a*XVPGo0Ybj&17V zM?iH|6>A~J70WA`z6z~|bDau-H@)thn+&2eJU_B4jO^Ax-Jc+iWAE5_S$5Tp!VvWc z5L}%5eF*^mn@is4{{HpSs6GbCPaz%+1h3%o7Nj8j^B4RiEncP|5lh4g9PZ!ycN~u3 zG69KrB6b)>9wIccIM5_vJ{C&nlr%wNdzW!Y%vM7 zNR6^Xj%K}$SaB9TbW&&e|&K$#`x zNuseR*(EaCCo&pICX$kOl2Ys^)OtRwgj|#Hdc@x-56`5F4?$%yk3>YXP@JX|r?-^# zeEDCCS2{5!M|3%b$&%Yc!b_JrkOlG`@(#H}-X(X*d*mK@pL{^ROFkrvfQe`G9Y7 zTWKWc=%a|%&p$>2|Mo_1%jAjDK0O@3D_bRGk!%OMvQk$YSBSj;nR<=vL~*+fl}jx> ze8OQrqXpGvJA0E6F@@N}G|1EI4uEOjSM|hb({S=+dt^U7&;*A(2(sH#n=j>w$DQ@hfHV>l;}7Mp^v!T8q0wizunTQ)a)vL7=9x_`|f~(Q8|eMv8YjjcOVx zo<18G6wjCq42s95W#78C^;(ha&!)v{MY3(_d=y*T$mI^JVI1