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.
This commit is contained in:
parent
f582ea572c
commit
a50f5cd072
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
num_frames = 3
|
||||
known_failure = true
|
Loading…
Reference in New Issue