diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 29d637e2a..3df339bc0 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -643,15 +643,6 @@ pub fn load_player_globals<'gc>( ); class(activation, flash::text::font::create_class(mc), script)?; - // package `flash.crypto` - - // package `flash.external` - class( - activation, - flash::external::externalinterface::create_class(mc), - script, - )?; - // Inside this call, the macro `avm2_system_classes_playerglobal` // triggers classloading. Therefore, we run `load_playerglobal` // relative late, so that it can access classes defined before diff --git a/core/src/avm2/globals/flash/external.rs b/core/src/avm2/globals/flash/external.rs index 8bccf102a..67bc46ce7 100644 --- a/core/src/avm2/globals/flash/external.rs +++ b/core/src/avm2/globals/flash/external.rs @@ -1,3 +1,3 @@ //! `flash.external` namespace -pub mod externalinterface; +pub mod external_interface; diff --git a/core/src/avm2/globals/flash/external/ExternalInterface.as b/core/src/avm2/globals/flash/external/ExternalInterface.as new file mode 100644 index 000000000..ffae6a3f5 --- /dev/null +++ b/core/src/avm2/globals/flash/external/ExternalInterface.as @@ -0,0 +1,11 @@ +package flash.external +{ + public final class ExternalInterface + { + public static native function get available(): Boolean; + + public static native function addCallback(functionName: String, closure: Function) : void; + + public static native function call(functionName: String, ... arguments) : *; + } +} diff --git a/core/src/avm2/globals/flash/external/external_interface.rs b/core/src/avm2/globals/flash/external/external_interface.rs new file mode 100644 index 000000000..208895962 --- /dev/null +++ b/core/src/avm2/globals/flash/external/external_interface.rs @@ -0,0 +1,58 @@ +use crate::avm2::{Activation, Error, Object, Value}; +use crate::external::{Callback, Value as ExternalValue}; + +pub fn call<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + if args.is_empty() { + return Ok(Value::Null); + } + + let name = args.get(0).unwrap().coerce_to_string(activation)?; + if let Some(method) = activation + .context + .external_interface + .get_method_for(&name.to_utf8_lossy()) + { + let mut external_args = Vec::with_capacity(args.len() - 1); + for arg in &args[1..] { + external_args.push(ExternalValue::from_avm2(arg.to_owned())); + } + Ok(method + .call(&mut activation.context, &external_args) + .into_avm2(activation)) + } else { + Ok(Value::Null) + } +} + +pub fn get_available<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + Ok(activation.context.external_interface.available().into()) +} + +pub fn add_callback<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + if args.len() < 2 { + return Ok(Value::Undefined); + } + + let name = args.get(0).unwrap().coerce_to_string(activation)?; + let method = args.get(1).unwrap(); + + if let Value::Object(method) = method { + activation + .context + .external_interface + .add_callback(name.to_string(), Callback::Avm2 { method: *method }); + } + Ok(Value::Undefined) +} diff --git a/core/src/avm2/globals/flash/external/externalinterface.rs b/core/src/avm2/globals/flash/external/externalinterface.rs deleted file mode 100644 index a8cca495b..000000000 --- a/core/src/avm2/globals/flash/external/externalinterface.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! `flash.external.ExternalInterface` builtin/prototype - -use crate::avm2::class::{Class, ClassAttributes}; -use crate::avm2::method::{Method, NativeMethodImpl}; -use crate::avm2::Multiname; -use crate::avm2::{Activation, Error, Namespace, Object, QName, Value}; -use crate::external::{Callback, Value as ExternalValue}; -use gc_arena::{GcCell, MutationContext}; - -fn instance_init<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Option>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(this) = this { - activation.super_init(this, &[])?; - } - - Ok(Value::Undefined) -} - -fn class_init<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - _this: Option>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - Ok(Value::Undefined) -} - -pub fn call<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - _this: Option>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if args.is_empty() { - return Ok(Value::Null); - } - - let name = args.get(0).unwrap().coerce_to_string(activation)?; - if let Some(method) = activation - .context - .external_interface - .get_method_for(&name.to_utf8_lossy()) - { - let mut external_args = Vec::with_capacity(args.len() - 1); - for arg in &args[1..] { - external_args.push(ExternalValue::from_avm2(arg.to_owned())); - } - Ok(method - .call(&mut activation.context, &external_args) - .into_avm2(activation)) - } else { - Ok(Value::Null) - } -} - -pub fn available<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - _this: Option>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - Ok(activation.context.external_interface.available().into()) -} - -pub fn add_callback<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - _this: Option>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if args.len() < 2 { - return Ok(Value::Undefined); - } - - let name = args.get(0).unwrap().coerce_to_string(activation)?; - let method = args.get(1).unwrap(); - - if let Value::Object(method) = method { - activation - .context - .external_interface - .add_callback(name.to_string(), Callback::Avm2 { method: *method }); - } - Ok(Value::Undefined) -} - -/// Construct `ExternalInterface`'s class. -pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { - let class = Class::new( - QName::new(Namespace::package("flash.external"), "ExternalInterface"), - Some(Multiname::public("Object")), - Method::from_builtin( - instance_init, - "", - mc, - ), - Method::from_builtin(class_init, "", mc), - mc, - ); - - let mut write = class.write(mc); - write.set_attributes(ClassAttributes::FINAL | ClassAttributes::SEALED); - - const PUBLIC_CLASS_METHODS: &[(&str, NativeMethodImpl)] = - &[("call", call), ("addCallback", add_callback)]; - - write.define_public_builtin_class_methods(mc, PUBLIC_CLASS_METHODS); - - const PUBLIC_INSTANCE_PROPERTIES: &[( - &str, - Option, - Option, - )] = &[("available", Some(available), None)]; - - write.define_public_builtin_class_properties(mc, PUBLIC_INSTANCE_PROPERTIES); - - class -} diff --git a/core/src/avm2/globals/globals.as b/core/src/avm2/globals/globals.as index eaf332d69..726ab8564 100644 --- a/core/src/avm2/globals/globals.as +++ b/core/src/avm2/globals/globals.as @@ -163,6 +163,8 @@ include "flash/media/StageVideoAvailabilityReason.as" include "flash/media/VideoCodec.as" include "flash/media/VideoStatus.as" +include "flash/external/ExternalInterface.as" + include "flash/net.as" include "flash/net/FileFilter.as" include "flash/net/FileReference.as"