avm1: Improve display object property setters for weird values

Setting a property such as _x to undefined or null should have no
effect. This was working for v7+ SWFs because it would coerce to
NaN and we toss out NaNs. But on v6 and below, these coerce to 0
and would end up setting the property to 0.

Explicitly check for undefined/null and bail out. Fixes #380.

Also adjust the _visible setter, since this actually coerces to a
number (because of its legacy from SWFv4). For example,
_visible = "" should have no effect.
This commit is contained in:
Mike Welsh 2020-02-14 14:44:51 -08:00
parent 3a1a73ae11
commit 9ad069e11a
1 changed files with 32 additions and 21 deletions

View File

@ -412,8 +412,7 @@ fn set_x<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)?;
if !val.is_nan() {
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_x(context.gc_context, val);
}
Ok(())
@ -433,8 +432,7 @@ fn set_y<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)?;
if !val.is_nan() {
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_y(context.gc_context, val);
}
Ok(())
@ -455,9 +453,8 @@ fn set_x_scale<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)? / 100.0;
if !val.is_nan() {
this.set_scale_x(context.gc_context, val);
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_scale_x(context.gc_context, val / 100.0);
}
Ok(())
}
@ -477,9 +474,8 @@ fn set_y_scale<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)? / 100.0;
if !val.is_nan() {
this.set_scale_y(context.gc_context, val);
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_scale_y(context.gc_context, val / 100.0);
}
Ok(())
}
@ -523,9 +519,8 @@ fn set_alpha<'gc>(
this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)? / 100.0;
if !val.is_nan() {
this.set_alpha(context.gc_context, val);
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_alpha(context.gc_context, val / 100.0);
}
Ok(())
}
@ -545,8 +540,11 @@ fn set_visible<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_bool(avm.current_swf_version());
this.set_visible(context.gc_context, val);
// Because this property dates to the era of Flash 4, this is actually coerced to an integer.
// `_visible = "false";` coerces to NaN and has no effect.
if let Some(n) = property_coerce_to_number(avm, context, val)? {
this.set_visible(context.gc_context, n != 0.0);
}
Ok(())
}
@ -564,8 +562,7 @@ fn set_width<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)?;
if !val.is_nan() {
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_width(context.gc_context, val);
}
Ok(())
@ -585,8 +582,7 @@ fn set_height<'gc>(
mut this: DisplayObject<'gc>,
val: Value<'gc>,
) -> Result<(), Error> {
let val = val.as_number(avm, context)?;
if !val.is_nan() {
if let Some(val) = property_coerce_to_number(avm, context, val)? {
this.set_height(context.gc_context, val);
}
Ok(())
@ -606,8 +602,7 @@ fn set_rotation<'gc>(
mut this: DisplayObject<'gc>,
degrees: Value<'gc>,
) -> Result<(), Error> {
let mut degrees = degrees.as_number(avm, context)?;
if !degrees.is_nan() {
if let Some(mut degrees) = property_coerce_to_number(avm, context, degrees)? {
// Normalize into the range of [-180, 180].
degrees %= 360.0;
if degrees < -180.0 {
@ -770,3 +765,19 @@ fn y_mouse<'gc>(
let local = this.global_to_local(*context.mouse_position);
Ok(local.1.to_pixels().into())
}
fn property_coerce_to_number<'gc>(
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
value: Value<'gc>,
) -> Result<Option<f64>, Error> {
if value != Value::Undefined && value != Value::Null {
let n = value.as_number(avm, context)?;
if n.is_finite() {
return Ok(Some(n));
}
}
// Invalid value; do not set.
Ok(None)
}