avm1: Introduce `NativeObject`
The existing `Object` enum representation is problematic for inherited native objects, since "regular" `ScriptObject`s cannot be turned into native objects, but rather a completely new native object needs to be created. `TObject::create_bare_object` is an attempt to aid this situation, but it works only for `ActionExtends` inheritance, and not when the user manually wires up `prototype`/`__proto__` (#701). In Flash, it seems like derived constructors initially have a "regular" `this` object. But once the `super()` constructor is invoked, the same `this` object becomes a native object. To allow this in Ruffle, introduce a new `NativeObject` enum, and store it as a member in `ScriptObject`. For a start, move `TextFormatObject` from the `Object` enum to `NativeObject`. The plan is to gradually move all `Object` enum variants to `NativeObject`, except for `ScriptObject`.
This commit is contained in:
parent
c17da6e91a
commit
af006a3053
|
@ -1,13 +1,13 @@
|
||||||
use crate::avm1::activation::Activation;
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::object::text_format_object::TextFormatObject;
|
use crate::avm1::object::NativeObject;
|
||||||
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
||||||
use crate::avm1::{globals, Object, ScriptObject, TObject, Value};
|
use crate::avm1::{globals, Object, ScriptObject, TObject, Value};
|
||||||
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject, TextSelection};
|
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject, TextSelection};
|
||||||
use crate::font::round_down_to_pixel;
|
use crate::font::round_down_to_pixel;
|
||||||
use crate::html::TextFormat;
|
use crate::html::TextFormat;
|
||||||
use crate::string::{AvmString, WStr};
|
use crate::string::{AvmString, WStr};
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
macro_rules! tf_method {
|
macro_rules! tf_method {
|
||||||
($fn:expr) => {
|
($fn:expr) => {
|
||||||
|
@ -121,13 +121,26 @@ pub fn set_password<'gc>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_text_format<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
text_format: TextFormat,
|
||||||
|
) -> ScriptObject<'gc> {
|
||||||
|
let proto = activation.context.avm1.prototypes().text_format;
|
||||||
|
let object = ScriptObject::new(activation.context.gc_context, Some(proto));
|
||||||
|
object.set_native(
|
||||||
|
activation.context.gc_context,
|
||||||
|
NativeObject::TextFormat(GcCell::allocate(activation.context.gc_context, text_format)),
|
||||||
|
);
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
fn get_new_text_format<'gc>(
|
fn get_new_text_format<'gc>(
|
||||||
text_field: EditText<'gc>,
|
text_field: EditText<'gc>,
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = text_field.new_text_format();
|
let text_format = text_field.new_text_format();
|
||||||
Ok(TextFormatObject::new(activation, tf).into())
|
Ok(new_text_format(activation, text_format).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_new_text_format<'gc>(
|
fn set_new_text_format<'gc>(
|
||||||
|
@ -135,11 +148,9 @@ fn set_new_text_format<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = args.get(0).unwrap_or(&Value::Undefined);
|
if let [Value::Object(text_format), ..] = args {
|
||||||
|
if let NativeObject::TextFormat(text_format) = text_format.native() {
|
||||||
if let Value::Object(tf) = tf {
|
text_field.set_new_text_format(text_format.read().clone(), &mut activation.context);
|
||||||
if let Some(tf) = tf.as_text_format_object() {
|
|
||||||
text_field.set_new_text_format(tf.text_format().clone(), &mut activation.context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,20 +162,26 @@ fn get_text_format<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let (from, to) = match (args.get(0), args.get(1)) {
|
let (begin_index, end_index) = match args {
|
||||||
(Some(f), Some(t)) => (
|
[begin_index, end_index, ..] => {
|
||||||
f.coerce_to_f64(activation)? as usize,
|
let begin_index = begin_index.coerce_to_u32(activation)? as usize;
|
||||||
t.coerce_to_f64(activation)? as usize,
|
let end_index = end_index.coerce_to_u32(activation)? as usize;
|
||||||
),
|
(begin_index, end_index)
|
||||||
(Some(f), None) => {
|
}
|
||||||
let v = f.coerce_to_f64(activation)? as usize;
|
[begin_index] => {
|
||||||
(v, v.saturating_add(1))
|
let begin_index = begin_index.coerce_to_u32(activation)? as usize;
|
||||||
|
let end_index = begin_index + 1;
|
||||||
|
(begin_index, end_index)
|
||||||
|
}
|
||||||
|
[] => {
|
||||||
|
let begin_index = 0;
|
||||||
|
let end_index = text_field.text_length();
|
||||||
|
(begin_index, end_index)
|
||||||
}
|
}
|
||||||
_ => (0, text_field.text_length()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tf = text_field.text_format(from, to);
|
let text_format = text_field.text_format(begin_index, end_index);
|
||||||
Ok(TextFormatObject::new(activation, tf).into())
|
Ok(new_text_format(activation, text_format).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_text_format<'gc>(
|
fn set_text_format<'gc>(
|
||||||
|
@ -172,23 +189,33 @@ fn set_text_format<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let tf = args.last().unwrap_or(&Value::Undefined);
|
let (begin_index, end_index, text_format) = match args {
|
||||||
|
[begin_index, end_index, text_format, ..] => {
|
||||||
if let Value::Object(tf) = tf {
|
let begin_index = begin_index.coerce_to_u32(activation)? as usize;
|
||||||
if let Some(tf) = tf.as_text_format_object() {
|
let end_index = end_index.coerce_to_u32(activation)? as usize;
|
||||||
let (from, to) = match (args.get(0), args.get(1)) {
|
(begin_index, end_index, text_format)
|
||||||
(Some(f), Some(t)) if args.len() > 2 => (
|
|
||||||
f.coerce_to_f64(activation)? as usize,
|
|
||||||
t.coerce_to_f64(activation)? as usize,
|
|
||||||
),
|
|
||||||
(Some(f), _) if args.len() > 1 => {
|
|
||||||
let v = f.coerce_to_f64(activation)? as usize;
|
|
||||||
(v, v.saturating_add(1))
|
|
||||||
}
|
}
|
||||||
_ => (0, text_field.text_length()),
|
[begin_index, text_format, ..] => {
|
||||||
|
let begin_index = begin_index.coerce_to_u32(activation)? as usize;
|
||||||
|
let end_index = begin_index + 1;
|
||||||
|
(begin_index, end_index, text_format)
|
||||||
|
}
|
||||||
|
[text_format] => {
|
||||||
|
let begin_index = 0;
|
||||||
|
let end_index = text_field.text_length();
|
||||||
|
(begin_index, end_index, text_format)
|
||||||
|
}
|
||||||
|
_ => return Ok(Value::Undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
text_field.set_text_format(from, to, tf.text_format().clone(), &mut activation.context);
|
if let Value::Object(text_format) = text_format {
|
||||||
|
if let NativeObject::TextFormat(text_format) = text_format.native() {
|
||||||
|
text_field.set_text_format(
|
||||||
|
begin_index,
|
||||||
|
end_index,
|
||||||
|
text_format.read().clone(),
|
||||||
|
&mut activation.context,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,12 +343,17 @@ pub fn set_text_color<'gc>(
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
let rgb = value.coerce_to_u32(activation)?;
|
let rgb = value.coerce_to_u32(activation)?;
|
||||||
let tf = TextFormat {
|
let text_format = TextFormat {
|
||||||
color: Some(swf::Color::from_rgb(rgb, 0)),
|
color: Some(swf::Color::from_rgb(rgb, 0)),
|
||||||
..TextFormat::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
this.set_text_format(0, this.text_length(), tf.clone(), &mut activation.context);
|
this.set_text_format(
|
||||||
this.set_new_text_format(tf, &mut activation.context);
|
0,
|
||||||
|
this.text_length(),
|
||||||
|
text_format.clone(),
|
||||||
|
&mut activation.context,
|
||||||
|
);
|
||||||
|
this.set_new_text_format(text_format, &mut activation.context);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,7 +596,7 @@ pub fn get_type<'gc>(
|
||||||
true => "input",
|
true => "input",
|
||||||
false => "dynamic",
|
false => "dynamic",
|
||||||
};
|
};
|
||||||
Ok(AvmString::from(tf_type).into())
|
Ok(tf_type.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_type<'gc>(
|
pub fn set_type<'gc>(
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
//! `TextFormat` impl
|
//! `TextFormat` impl
|
||||||
|
|
||||||
use crate::avm1::object::text_format_object::TextFormatObject;
|
use crate::avm1::object::NativeObject;
|
||||||
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
||||||
use crate::avm1::{Activation, ArrayObject, AvmString, Error, Object, TObject, Value};
|
use crate::avm1::{
|
||||||
|
Activation, ArrayObject, AvmString, Error, Object, ScriptObject, TObject, Value,
|
||||||
|
};
|
||||||
use crate::avm_warn;
|
use crate::avm_warn;
|
||||||
use crate::ecma_conversions::round_to_even;
|
use crate::ecma_conversions::round_to_even;
|
||||||
use crate::html::TextFormat;
|
use crate::html::TextFormat;
|
||||||
use crate::string::WStr;
|
use crate::string::WStr;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
macro_rules! getter {
|
macro_rules! getter {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|activation, this, _args| {
|
|activation, this, _args| {
|
||||||
if let Some(text_format) = this.as_text_format_object() {
|
if let NativeObject::TextFormat(text_format) = this.native() {
|
||||||
return Ok($name(activation, &text_format.text_format()));
|
return Ok($name(activation, &text_format.read()));
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
@ -23,11 +25,11 @@ macro_rules! getter {
|
||||||
macro_rules! setter {
|
macro_rules! setter {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|activation, this, args| {
|
|activation, this, args| {
|
||||||
if let Some(text_format) = this.as_text_format_object() {
|
if let NativeObject::TextFormat(text_format) = this.native() {
|
||||||
let value = args.get(0).unwrap_or(&Value::Undefined);
|
let value = args.get(0).unwrap_or(&Value::Undefined);
|
||||||
$name(
|
$name(
|
||||||
activation,
|
activation,
|
||||||
&mut text_format.text_format_mut(activation.context.gc_context),
|
&mut text_format.write(activation.context.gc_context),
|
||||||
value,
|
value,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -470,8 +472,7 @@ pub fn constructor<'gc>(
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(this) = this.as_text_format_object() {
|
let mut text_format = Default::default();
|
||||||
let mut text_format = this.text_format_mut(activation.context.gc_context);
|
|
||||||
set_font(
|
set_font(
|
||||||
activation,
|
activation,
|
||||||
&mut text_format,
|
&mut text_format,
|
||||||
|
@ -537,8 +538,10 @@ pub fn constructor<'gc>(
|
||||||
&mut text_format,
|
&mut text_format,
|
||||||
args.get(12).unwrap_or(&Value::Undefined),
|
args.get(12).unwrap_or(&Value::Undefined),
|
||||||
)?;
|
)?;
|
||||||
}
|
this.set_native(
|
||||||
|
activation.context.gc_context,
|
||||||
|
NativeObject::TextFormat(GcCell::allocate(activation.context.gc_context, text_format)),
|
||||||
|
);
|
||||||
Ok(this.into())
|
Ok(this.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,8 +551,7 @@ pub fn create_proto<'gc>(
|
||||||
proto: Object<'gc>,
|
proto: Object<'gc>,
|
||||||
fn_proto: Object<'gc>,
|
fn_proto: Object<'gc>,
|
||||||
) -> Object<'gc> {
|
) -> Object<'gc> {
|
||||||
let text_format = TextFormatObject::empty_object(gc_context, Some(proto));
|
let object = ScriptObject::new(gc_context, Some(proto));
|
||||||
let object = text_format.as_script_object().unwrap();
|
|
||||||
define_properties_on(PROTO_DECLS, gc_context, object, fn_proto);
|
define_properties_on(PROTO_DECLS, gc_context, object, fn_proto);
|
||||||
text_format.into()
|
object.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
//! Object trait to expose objects to AVM
|
//! Object trait to expose objects to AVM
|
||||||
|
|
||||||
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject};
|
use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject};
|
||||||
use crate::avm1::object::shared_object::SharedObject;
|
|
||||||
use crate::avm1::object::super_object::SuperObject;
|
|
||||||
use crate::avm1::object::value_object::ValueObject;
|
|
||||||
use crate::avm1::property::Attribute;
|
|
||||||
|
|
||||||
use crate::avm1::activation::Activation;
|
|
||||||
use crate::avm1::object::array_object::ArrayObject;
|
use crate::avm1::object::array_object::ArrayObject;
|
||||||
use crate::avm1::object::bevel_filter::BevelFilterObject;
|
use crate::avm1::object::bevel_filter::BevelFilterObject;
|
||||||
use crate::avm1::object::bitmap_data::BitmapDataObject;
|
use crate::avm1::object::bitmap_data::BitmapDataObject;
|
||||||
|
@ -21,14 +16,18 @@ use crate::avm1::object::drop_shadow_filter::DropShadowFilterObject;
|
||||||
use crate::avm1::object::glow_filter::GlowFilterObject;
|
use crate::avm1::object::glow_filter::GlowFilterObject;
|
||||||
use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject;
|
use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject;
|
||||||
use crate::avm1::object::gradient_glow_filter::GradientGlowFilterObject;
|
use crate::avm1::object::gradient_glow_filter::GradientGlowFilterObject;
|
||||||
use crate::avm1::object::text_format_object::TextFormatObject;
|
use crate::avm1::object::shared_object::SharedObject;
|
||||||
|
use crate::avm1::object::super_object::SuperObject;
|
||||||
use crate::avm1::object::transform_object::TransformObject;
|
use crate::avm1::object::transform_object::TransformObject;
|
||||||
|
use crate::avm1::object::value_object::ValueObject;
|
||||||
use crate::avm1::object::xml_node_object::XmlNodeObject;
|
use crate::avm1::object::xml_node_object::XmlNodeObject;
|
||||||
use crate::avm1::object::xml_object::XmlObject;
|
use crate::avm1::object::xml_object::XmlObject;
|
||||||
|
use crate::avm1::property::Attribute;
|
||||||
use crate::avm1::{AvmString, ScriptObject, SoundObject, StageObject, Value};
|
use crate::avm1::{AvmString, ScriptObject, SoundObject, StageObject, Value};
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
|
use crate::html::TextFormat;
|
||||||
use crate::xml::XmlNode;
|
use crate::xml::XmlNode;
|
||||||
use gc_arena::{Collect, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use ruffle_macros::enum_trait_object;
|
use ruffle_macros::enum_trait_object;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
@ -51,12 +50,18 @@ pub mod shared_object;
|
||||||
pub mod sound_object;
|
pub mod sound_object;
|
||||||
pub mod stage_object;
|
pub mod stage_object;
|
||||||
pub mod super_object;
|
pub mod super_object;
|
||||||
pub mod text_format_object;
|
|
||||||
pub mod transform_object;
|
pub mod transform_object;
|
||||||
pub mod value_object;
|
pub mod value_object;
|
||||||
pub mod xml_node_object;
|
pub mod xml_node_object;
|
||||||
pub mod xml_object;
|
pub mod xml_object;
|
||||||
|
|
||||||
|
#[derive(Clone, Collect)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub enum NativeObject<'gc> {
|
||||||
|
None,
|
||||||
|
TextFormat(GcCell<'gc, TextFormat>),
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents an object that can be directly interacted with by the AVM
|
/// Represents an object that can be directly interacted with by the AVM
|
||||||
/// runtime.
|
/// runtime.
|
||||||
#[enum_trait_object(
|
#[enum_trait_object(
|
||||||
|
@ -87,7 +92,6 @@ pub mod xml_object;
|
||||||
GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
|
GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
|
||||||
DateObject(DateObject<'gc>),
|
DateObject(DateObject<'gc>),
|
||||||
BitmapData(BitmapDataObject<'gc>),
|
BitmapData(BitmapDataObject<'gc>),
|
||||||
TextFormatObject(TextFormatObject<'gc>),
|
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
||||||
|
@ -493,6 +497,12 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn native(&self) -> NativeObject<'gc> {
|
||||||
|
NativeObject::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_native(&self, _gc_context: MutationContext<'gc, '_>, _native: NativeObject<'gc>) {}
|
||||||
|
|
||||||
/// Get the underlying script object, if it exists.
|
/// Get the underlying script object, if it exists.
|
||||||
fn as_script_object(&self) -> Option<ScriptObject<'gc>>;
|
fn as_script_object(&self) -> Option<ScriptObject<'gc>>;
|
||||||
|
|
||||||
|
@ -611,11 +621,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the underlying `TextFormatObject`, if it exists
|
|
||||||
fn as_text_format_object(&self) -> Option<TextFormatObject<'gc>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_ptr(&self) -> *const ObjectPtr;
|
fn as_ptr(&self) -> *const ObjectPtr;
|
||||||
|
|
||||||
/// Check if this object is in the prototype chain of the specified test object.
|
/// Check if this object is in the prototype chain of the specified test object.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::avm1::activation::Activation;
|
use crate::avm1::activation::Activation;
|
||||||
use crate::avm1::error::Error;
|
use crate::avm1::error::Error;
|
||||||
use crate::avm1::function::{ExecutionName, ExecutionReason};
|
use crate::avm1::function::{ExecutionName, ExecutionReason};
|
||||||
|
use crate::avm1::object::NativeObject;
|
||||||
use crate::avm1::property::{Attribute, Property};
|
use crate::avm1::property::{Attribute, Property};
|
||||||
use crate::avm1::property_map::{Entry, PropertyMap};
|
use crate::avm1::property_map::{Entry, PropertyMap};
|
||||||
use crate::avm1::{Object, ObjectPtr, TObject, Value};
|
use crate::avm1::{Object, ObjectPtr, TObject, Value};
|
||||||
|
@ -52,6 +53,7 @@ pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
||||||
#[derive(Collect)]
|
#[derive(Collect)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct ScriptObjectData<'gc> {
|
pub struct ScriptObjectData<'gc> {
|
||||||
|
native: NativeObject<'gc>,
|
||||||
properties: PropertyMap<'gc, Property<'gc>>,
|
properties: PropertyMap<'gc, Property<'gc>>,
|
||||||
interfaces: Vec<Object<'gc>>,
|
interfaces: Vec<Object<'gc>>,
|
||||||
watchers: PropertyMap<'gc, Watcher<'gc>>,
|
watchers: PropertyMap<'gc, Watcher<'gc>>,
|
||||||
|
@ -71,6 +73,7 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
let object = Self(GcCell::allocate(
|
let object = Self(GcCell::allocate(
|
||||||
gc_context,
|
gc_context,
|
||||||
ScriptObjectData {
|
ScriptObjectData {
|
||||||
|
native: NativeObject::None,
|
||||||
properties: PropertyMap::new(),
|
properties: PropertyMap::new(),
|
||||||
interfaces: vec![],
|
interfaces: vec![],
|
||||||
watchers: PropertyMap::new(),
|
watchers: PropertyMap::new(),
|
||||||
|
@ -481,6 +484,20 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
self.0.write(gc_context).interfaces = iface_list;
|
self.0.write(gc_context).interfaces = iface_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn native(&self) -> NativeObject<'gc> {
|
||||||
|
self.0.read().native.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_native(&self, gc_context: MutationContext<'gc, '_>, native: NativeObject<'gc>) {
|
||||||
|
// Native object should be introduced at most once.
|
||||||
|
debug_assert!(matches!(self.0.read().native, NativeObject::None));
|
||||||
|
|
||||||
|
// Native object must not be `None`.
|
||||||
|
debug_assert!(!matches!(native, NativeObject::None));
|
||||||
|
|
||||||
|
self.0.write(gc_context).native = native;
|
||||||
|
}
|
||||||
|
|
||||||
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
|
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
|
||||||
Some(*self)
|
Some(*self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
use crate::avm1::{Activation, Object, ScriptObject, TObject};
|
|
||||||
use crate::html::TextFormat;
|
|
||||||
use crate::impl_custom_object;
|
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
|
||||||
use std::cell::{Ref, RefMut};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Collect)]
|
|
||||||
#[collect(no_drop)]
|
|
||||||
pub struct TextFormatObject<'gc>(GcCell<'gc, TextFormatData<'gc>>);
|
|
||||||
|
|
||||||
#[derive(Collect)]
|
|
||||||
#[collect(no_drop)]
|
|
||||||
pub struct TextFormatData<'gc> {
|
|
||||||
/// The underlying script object.
|
|
||||||
base: ScriptObject<'gc>,
|
|
||||||
|
|
||||||
text_format: TextFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TextFormatObject<'_> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let this = self.0.read();
|
|
||||||
f.debug_struct("TextFormatObject")
|
|
||||||
.field("text_format", &this.text_format)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'gc> TextFormatObject<'gc> {
|
|
||||||
pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Option<Object<'gc>>) -> Self {
|
|
||||||
Self(GcCell::allocate(
|
|
||||||
gc_context,
|
|
||||||
TextFormatData {
|
|
||||||
base: ScriptObject::new(gc_context, proto),
|
|
||||||
text_format: TextFormat::default(),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(activation: &mut Activation<'_, 'gc, '_>, text_format: TextFormat) -> Self {
|
|
||||||
Self(GcCell::allocate(
|
|
||||||
activation.context.gc_context,
|
|
||||||
TextFormatData {
|
|
||||||
base: ScriptObject::new(
|
|
||||||
activation.context.gc_context,
|
|
||||||
Some(activation.context.avm1.prototypes.text_format),
|
|
||||||
),
|
|
||||||
text_format,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text_format(&self) -> Ref<TextFormat> {
|
|
||||||
Ref::map(self.0.read(), |o| &o.text_format)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text_format_mut(&self, gc_context: MutationContext<'gc, '_>) -> RefMut<TextFormat> {
|
|
||||||
RefMut::map(self.0.write(gc_context), |o| &mut o.text_format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'gc> TObject<'gc> for TextFormatObject<'gc> {
|
|
||||||
impl_custom_object!(base {
|
|
||||||
bare_object(as_text_format_object -> TextFormatObject::empty_object);
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in New Issue