avm2: Implement `EventDispatcher.willTrigger`

This commit is contained in:
David Wendt 2020-12-17 23:10:30 -05:00 committed by Mike Welsh
parent a071800117
commit 87ff679898
9 changed files with 161 additions and 0 deletions

View File

@ -8,6 +8,7 @@ use crate::avm2::object::{DispatchObject, Object, TObject};
use crate::avm2::traits::Trait; use crate::avm2::traits::Trait;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use crate::display_object::TDisplayObject;
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
/// Implements `flash.events.EventDispatcher`'s instance constructor. /// Implements `flash.events.EventDispatcher`'s instance constructor.
@ -165,6 +166,73 @@ pub fn has_event_listener<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
/// Retrieve the parent of a given `EventDispatcher`.
///
/// `EventDispatcher` does not provide a generic way for it's subclasses to
/// indicate ancestry. Instead, only specific event targets provide a hierarchy
/// to traverse. If no hierarchy is available, this returns `None`, as if the
/// target had no parent.
fn parent_of(target: Object<'_>) -> Option<Object<'_>> {
if let Some(dobj) = target.as_display_object() {
if let Some(dparent) = dobj.parent() {
if let Value::Object(parent) = dparent.object2() {
return Some(parent);
}
}
}
None
}
/// Implements `EventDispatcher.willTrigger`.
pub fn will_trigger<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(mut this) = this {
let dispatch_list = this
.get_property(
this,
&QName::new(
Namespace::ruffle_private("EventDispatcher"),
"dispatch_list",
),
activation,
)?
.coerce_to_object(activation)?;
let event_type = args
.get(0)
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_string(activation)?;
if dispatch_list
.as_dispatch_mut(activation.context.gc_context)
.ok_or_else(|| Error::from("Internal properties should have what I put in them"))?
.has_event_listener(event_type)
{
return Ok(true.into());
}
let target = this
.get_property(
this,
&QName::new(Namespace::ruffle_private("EventDispatcher"), "target"),
activation,
)?
.coerce_to_object(activation)
.ok()
.unwrap_or(this);
if let Some(parent) = parent_of(target) {
return will_trigger(activation, Some(parent), args);
}
}
Ok(false.into())
}
/// Implements `flash.events.EventDispatcher`'s class constructor. /// Implements `flash.events.EventDispatcher`'s class constructor.
pub fn class_init<'gc>( pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
@ -200,6 +268,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
QName::new(Namespace::public_namespace(), "hasEventListener"), QName::new(Namespace::public_namespace(), "hasEventListener"),
Method::from_builtin(has_event_listener), Method::from_builtin(has_event_listener),
)); ));
write.define_instance_trait(Trait::from_method(
QName::new(Namespace::public_namespace(), "willTrigger"),
Method::from_builtin(will_trigger),
));
write.define_instance_trait(Trait::from_slot( write.define_instance_trait(Trait::from_slot(
QName::new(Namespace::ruffle_private("EventDispatcher"), "target"), QName::new(Namespace::ruffle_private("EventDispatcher"), "target"),

View File

@ -460,6 +460,8 @@ swf_tests! {
(as3_function_call_arguments, "avm2/function_call_arguments", 1), (as3_function_call_arguments, "avm2/function_call_arguments", 1),
(as3_function_call_rest, "avm2/function_call_rest", 1), (as3_function_call_rest, "avm2/function_call_rest", 1),
(as3_eventdispatcher_haseventlistener, "avm2/eventdispatcher_haseventlistener", 1), (as3_eventdispatcher_haseventlistener, "avm2/eventdispatcher_haseventlistener", 1),
(as3_eventdispatcher_willtrigger, "avm2/eventdispatcher_willtrigger", 1),
(as3_movieclip_willtrigger, "avm2/movieclip_willtrigger", 3),
} }
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,57 @@
package {
public class Test {
}
}
import flash.events.EventDispatcher;
trace("//var evtd = new EventDispatcher();");
var evtd = new EventDispatcher();
trace("//var listener = function() { ... };");
var listener = function() { trace("//Listener called!"); };
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.addEventListener('test', listener, false, 0);");
evtd.addEventListener('test', listener, false, 0);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.removeEventListener('test', listener, false);");
evtd.removeEventListener('test', listener, false);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.removeEventListener('test', listener, false);");
evtd.removeEventListener('test', listener, false);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.addEventListener('test', listener, false, 0);");
evtd.addEventListener('test', listener, false, 0);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.addEventListener('test', listener, false, 0);");
evtd.addEventListener('test', listener, false, 0);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.removeEventListener('test', listener, false);");
evtd.removeEventListener('test', listener, false);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));
trace("//evtd.removeEventListener('test', listener, false);");
evtd.removeEventListener('test', listener, false);
trace("//evtd.willTrigger('test');");
trace(evtd.willTrigger('test'));

View File

@ -0,0 +1,25 @@
//var evtd = new EventDispatcher();
//var listener = function() { ... };
//evtd.willTrigger('test');
false
//evtd.addEventListener('test', listener, false, 0);
//evtd.willTrigger('test');
true
//evtd.removeEventListener('test', listener, false);
//evtd.willTrigger('test');
false
//evtd.removeEventListener('test', listener, false);
//evtd.willTrigger('test');
false
//evtd.addEventListener('test', listener, false, 0);
//evtd.willTrigger('test');
true
//evtd.addEventListener('test', listener, false, 0);
//evtd.willTrigger('test');
true
//evtd.removeEventListener('test', listener, false);
//evtd.willTrigger('test');
false
//evtd.removeEventListener('test', listener, false);
//evtd.willTrigger('test');
false

View File

@ -0,0 +1,5 @@
//(in child) this.willTrigger('test');
false
///(in parent) this.addEventListener('test', function () {});
//(in child) this.willTrigger('test');
true

Binary file not shown.

Binary file not shown.