avm1: Implement AsBroadcaster
This commit is contained in:
parent
32be19da6d
commit
890dc0ea13
|
@ -11,6 +11,7 @@ use rand::Rng;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
|
||||||
mod array;
|
mod array;
|
||||||
|
mod as_broadcaster;
|
||||||
pub(crate) mod boolean;
|
pub(crate) mod boolean;
|
||||||
pub(crate) mod button;
|
pub(crate) mod button;
|
||||||
mod color;
|
mod color;
|
||||||
|
@ -478,6 +479,17 @@ pub fn create_globals<'gc>(
|
||||||
);
|
);
|
||||||
globals.define_value(gc_context, "System", system.into(), DontEnum.into());
|
globals.define_value(gc_context, "System", system.into(), DontEnum.into());
|
||||||
|
|
||||||
|
globals.define_value(
|
||||||
|
gc_context,
|
||||||
|
"AsBroadcaster",
|
||||||
|
Value::Object(as_broadcaster::create(
|
||||||
|
gc_context,
|
||||||
|
Some(object_proto),
|
||||||
|
Some(function_proto),
|
||||||
|
)),
|
||||||
|
DontEnum.into(),
|
||||||
|
);
|
||||||
|
|
||||||
globals.define_value(
|
globals.define_value(
|
||||||
gc_context,
|
gc_context,
|
||||||
"Math",
|
"Math",
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
//! ActionScript Broadcaster (AsBroadcaster)
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
|
use crate::avm1::error::Error;
|
||||||
|
use crate::avm1::property::Attribute::*;
|
||||||
|
use crate::avm1::object::TObject;
|
||||||
|
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
|
||||||
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
|
pub fn add_listener<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
let new_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
|
|
||||||
|
if let Value::Object(listeners) = listeners {
|
||||||
|
let length = listeners.length();
|
||||||
|
listeners.set_length(context.gc_context, length + 1);
|
||||||
|
listeners.set_array_element(length, new_listener, context.gc_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_listener<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
let old_listener = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
|
|
||||||
|
if let Value::Object(listeners) = listeners {
|
||||||
|
let length = listeners.length();
|
||||||
|
let mut position = None;
|
||||||
|
|
||||||
|
for i in 0..length {
|
||||||
|
let other_listener = listeners.array_element(i);
|
||||||
|
if old_listener == other_listener {
|
||||||
|
position = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(position) = position {
|
||||||
|
if length > 0 {
|
||||||
|
let new_length = length - 1;
|
||||||
|
for i in position..new_length {
|
||||||
|
listeners.set_array_element(
|
||||||
|
i,
|
||||||
|
listeners.array_element(i + 1),
|
||||||
|
context.gc_context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.delete_array_element(new_length, context.gc_context);
|
||||||
|
listeners.delete(activation, context.gc_context, &new_length.to_string());
|
||||||
|
|
||||||
|
listeners.set_length(context.gc_context, new_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcast_message<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
this: Object<'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
let event_name_value = args.get(0).cloned().unwrap_or(Value::Undefined);
|
||||||
|
let event_name = event_name_value.coerce_to_string(activation, context)?;
|
||||||
|
let call_args = &args[1..];
|
||||||
|
|
||||||
|
let listeners = this.get("_listeners", activation, context)?;
|
||||||
|
|
||||||
|
if let Value::Object(listeners) = listeners {
|
||||||
|
for i in 0..listeners.length() {
|
||||||
|
let listener = listeners.array_element(i);
|
||||||
|
|
||||||
|
if let Value::Object(listener) = listener {
|
||||||
|
listener.call_method(&event_name, call_args, activation, context)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn initialize<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
_this: Object<'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
if let Some(val) = args.get(0) {
|
||||||
|
let broadcaster = val.coerce_to_object(activation, context);
|
||||||
|
|
||||||
|
let listeners =
|
||||||
|
ScriptObject::array(context.gc_context, Some(activation.avm.prototypes().array));
|
||||||
|
|
||||||
|
broadcaster.define_value(
|
||||||
|
context.gc_context,
|
||||||
|
"_listeners",
|
||||||
|
Value::Object(listeners.into()),
|
||||||
|
DontEnum.into()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(mut broadcaster_script_obj) = broadcaster.as_script_object() {
|
||||||
|
broadcaster_script_obj.force_set_function(
|
||||||
|
"addListener",
|
||||||
|
add_listener,
|
||||||
|
context.gc_context,
|
||||||
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
Some(activation.avm.prototypes().function),
|
||||||
|
);
|
||||||
|
|
||||||
|
broadcaster_script_obj.force_set_function(
|
||||||
|
"removeListener",
|
||||||
|
remove_listener,
|
||||||
|
context.gc_context,
|
||||||
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
Some(activation.avm.prototypes().function),
|
||||||
|
);
|
||||||
|
|
||||||
|
broadcaster_script_obj.force_set_function(
|
||||||
|
"broadcastMessage",
|
||||||
|
broadcast_message,
|
||||||
|
context.gc_context,
|
||||||
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
Some(activation.avm.prototypes().function),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create<'gc>(
|
||||||
|
gc_context: MutationContext<'gc, '_>,
|
||||||
|
proto: Option<Object<'gc>>,
|
||||||
|
fn_proto: Option<Object<'gc>>,
|
||||||
|
) -> Object<'gc> {
|
||||||
|
let mut as_broadcaster = ScriptObject::object(gc_context, proto);
|
||||||
|
|
||||||
|
as_broadcaster.force_set_function(
|
||||||
|
"initialize",
|
||||||
|
initialize,
|
||||||
|
gc_context,
|
||||||
|
DontDelete | ReadOnly | DontEnum,
|
||||||
|
fn_proto,
|
||||||
|
);
|
||||||
|
|
||||||
|
as_broadcaster.into()
|
||||||
|
}
|
|
@ -59,6 +59,7 @@ macro_rules! swf_tests_approx {
|
||||||
swf_tests! {
|
swf_tests! {
|
||||||
(add_property, "avm1/add_property", 1),
|
(add_property, "avm1/add_property", 1),
|
||||||
(as_transformed_flag, "avm1/as_transformed_flag", 3),
|
(as_transformed_flag, "avm1/as_transformed_flag", 3),
|
||||||
|
(as_broadcaster, "avm1/as_broadcaster", 1),
|
||||||
(attach_movie, "avm1/attach_movie", 1),
|
(attach_movie, "avm1/attach_movie", 1),
|
||||||
(function_base_clip, "avm1/function_base_clip", 2),
|
(function_base_clip, "avm1/function_base_clip", 2),
|
||||||
(call, "avm1/call", 2),
|
(call, "avm1/call", 2),
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
true
|
||||||
|
true
|
||||||
|
Listener1 got the event!
|
||||||
|
Listener2 got the event!
|
||||||
|
Listener2 got: hello! and 123.123
|
||||||
|
Listener2 got: hello! and 123.123
|
||||||
|
[object Object],[object Object]
|
||||||
|
true
|
||||||
|
Listener2 got: Only one and 521
|
||||||
|
[object Object]
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue