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
|
// after one frame if the target is a MovieClip? Test the behaviour and adapt the code
|
||||||
// if necessary.
|
// if necessary.
|
||||||
pub fn avm1_unload_movie(&self, context: &mut UpdateContext<'_, 'gc>) {
|
pub fn avm1_unload_movie(&self, context: &mut UpdateContext<'_, 'gc>) {
|
||||||
let unloader = Loader::MovieUnloader {
|
// TODO: In Flash player, the MovieClip properties change to the unloaded state
|
||||||
self_handle: None,
|
// one frame after the unloadMovie command has been read, even if the MovieClip
|
||||||
target_clip: DisplayObject::MovieClip(*self),
|
// 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
|
||||||
let handle = context.load_manager.add_loader(unloader);
|
// 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
|
if self.is_root() {
|
||||||
.player
|
let unloader = Loader::MovieUnloader {
|
||||||
.clone()
|
self_handle: None,
|
||||||
.upgrade()
|
target_clip: DisplayObject::MovieClip(*self),
|
||||||
.expect("Could not upgrade weak reference to player");
|
};
|
||||||
let future = Box::pin(async move {
|
let handle = context.load_manager.add_loader(unloader);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
uc.load_manager.remove_loader(handle);
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
/// 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