avm1: Store childNodes on the node itself
This commit is contained in:
parent
b427940431
commit
a8ea913bf6
|
@ -222,6 +222,8 @@ impl<'gc> Xml<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
self.root().refresh_cached_child_nodes(activation).unwrap(); // :(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use crate::avm1::activation::Activation;
|
||||
use crate::avm1::error::Error;
|
||||
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
||||
use crate::avm1::{ArrayObject, NativeObject, Object, ScriptObject, TObject, Value};
|
||||
use crate::avm1::{NativeObject, Object, ScriptObject, TObject, Value};
|
||||
use crate::context::GcContext;
|
||||
use crate::string::{AvmString, WStr};
|
||||
use crate::xml::{XmlNode, TEXT_NODE};
|
||||
|
@ -64,6 +64,7 @@ fn append_child<'gc>(
|
|||
if !xmlnode.has_child(child_xmlnode) {
|
||||
let position = xmlnode.children_len();
|
||||
xmlnode.insert_child(activation.context.gc_context, position, child_xmlnode);
|
||||
xmlnode.refresh_cached_child_nodes(activation)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +86,7 @@ fn insert_before<'gc>(
|
|||
if !xmlnode.has_child(child_xmlnode) {
|
||||
if let Some(position) = xmlnode.child_position(insertpoint_xmlnode) {
|
||||
xmlnode.insert_child(activation.context.gc_context, position, child_xmlnode);
|
||||
xmlnode.refresh_cached_child_nodes(activation)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +175,11 @@ fn remove_node<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(mut node) = this.as_xml_node() {
|
||||
let old_parent = node.parent();
|
||||
node.remove_node(activation.context.gc_context);
|
||||
if let Some(old_parent) = old_parent {
|
||||
old_parent.refresh_cached_child_nodes(activation)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
|
@ -275,13 +281,7 @@ fn child_nodes<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(node) = this.as_xml_node() {
|
||||
return Ok(ArrayObject::new(
|
||||
activation.context.gc_context,
|
||||
activation.context.avm1.prototypes().array,
|
||||
node.children()
|
||||
.map(|mut child| child.script_object(activation).into()),
|
||||
)
|
||||
.into());
|
||||
return Ok(node.get_or_init_cached_child_nodes(activation)?.into());
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::avm1::Attribute;
|
||||
use crate::avm1::{Activation, NativeObject};
|
||||
use crate::avm1::{Error, Object, ScriptObject, TObject, Value};
|
||||
use crate::avm1::{ArrayObject, Error, Object, ScriptObject, TObject, Value};
|
||||
use crate::string::{AvmString, WStr, WString};
|
||||
use crate::xml;
|
||||
use gc_arena::{Collect, GcCell, Mutation};
|
||||
|
@ -45,6 +45,9 @@ pub struct XmlNodeData<'gc> {
|
|||
/// Attributes of the element.
|
||||
attributes: ScriptObject<'gc>,
|
||||
|
||||
/// The array object used for AS2 `.childNodes`
|
||||
cached_child_nodes: Option<ArrayObject<'gc>>,
|
||||
|
||||
/// Child nodes of this element.
|
||||
children: Vec<XmlNode<'gc>>,
|
||||
}
|
||||
|
@ -62,6 +65,7 @@ impl<'gc> XmlNode<'gc> {
|
|||
node_type,
|
||||
node_value,
|
||||
attributes: ScriptObject::new(mc, None),
|
||||
cached_child_nodes: None,
|
||||
children: Vec::new(),
|
||||
},
|
||||
))
|
||||
|
@ -373,6 +377,40 @@ impl<'gc> XmlNode<'gc> {
|
|||
self.0.read().attributes
|
||||
}
|
||||
|
||||
/// Gets a lazy-created .childNodes array
|
||||
pub fn get_or_init_cached_child_nodes(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<ArrayObject<'gc>, Error<'gc>> {
|
||||
let array = self.0.read().cached_child_nodes;
|
||||
if let Some(array) = array {
|
||||
Ok(array)
|
||||
} else {
|
||||
let array = ArrayObject::empty(activation);
|
||||
self.0
|
||||
.write(activation.context.gc_context)
|
||||
.cached_child_nodes = Some(array);
|
||||
self.refresh_cached_child_nodes(activation)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes the .childNodes array. Call this after every child list mutation.
|
||||
pub fn refresh_cached_child_nodes(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let array = self.0.read().cached_child_nodes;
|
||||
if let Some(array) = array {
|
||||
array.set_length(activation, 0)?;
|
||||
for (i, mut child) in self.children().enumerate() {
|
||||
let value = child.script_object(activation).into();
|
||||
array.set_element(activation, i as i32, value)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a duplicate copy of this node.
|
||||
///
|
||||
/// If the `deep` flag is set true, then the entire node tree will be cloned.
|
||||
|
@ -392,6 +430,7 @@ impl<'gc> XmlNode<'gc> {
|
|||
node_type: self.0.read().node_type,
|
||||
node_value: self.0.read().node_value,
|
||||
attributes,
|
||||
cached_child_nodes: None,
|
||||
children: Vec::new(),
|
||||
},
|
||||
));
|
||||
|
|
Loading…
Reference in New Issue