avm2: Implement XML.children, XMLList.children, and related methods
This commit is contained in:
parent
7f58b92348
commit
c04b463f1f
|
@ -10,6 +10,8 @@ package {
|
|||
AS3 native function name():Object;
|
||||
AS3 native function localName():Object;
|
||||
AS3 native function toXMLString():String;
|
||||
AS3 native function children():XMLList;
|
||||
|
||||
|
||||
AS3 native function toString():String;
|
||||
|
||||
|
@ -32,6 +34,11 @@ package {
|
|||
return self.AS3::toXMLString();
|
||||
};
|
||||
|
||||
prototype.children = function():String {
|
||||
var self:XML = this;
|
||||
return self.AS3::children();
|
||||
};
|
||||
|
||||
prototype.toString = function():String {
|
||||
if (this === prototype) {
|
||||
return "";
|
||||
|
@ -39,6 +46,5 @@ package {
|
|||
var self:XML = this;
|
||||
return self.AS3::toString();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ package {
|
|||
|
||||
AS3 native function hasSimpleContent():Boolean;
|
||||
AS3 native function length():int
|
||||
AS3 native function children():XMLList;
|
||||
|
||||
public native function toString():String;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! XML builtin and prototype
|
||||
|
||||
use crate::avm2::e4x::E4XNode;
|
||||
use crate::avm2::e4x::{E4XNode, E4XNodeKind};
|
||||
pub use crate::avm2::object::xml_allocator;
|
||||
use crate::avm2::object::{QNameObject, TObject};
|
||||
use crate::avm2::object::{E4XOrXml, QNameObject, TObject, XmlListObject};
|
||||
use crate::avm2::{Activation, Error, Object, QName, Value};
|
||||
use crate::avm2_stub_method;
|
||||
|
||||
|
@ -81,3 +81,23 @@ pub fn to_xml_string<'gc>(
|
|||
let node = xml.node();
|
||||
Ok(Value::String(node.xml_to_xml_string(activation)?))
|
||||
}
|
||||
|
||||
pub fn children<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let xml = this.unwrap().as_xml_object().unwrap();
|
||||
let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() {
|
||||
// FIXME - avoid clone
|
||||
children.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Ok(XmlListObject::new(
|
||||
activation,
|
||||
children.iter().map(|node| E4XOrXml::E4X(*node)).collect(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ pub use crate::avm2::object::xml_list_allocator;
|
|||
use crate::{
|
||||
avm2::{
|
||||
e4x::{simple_content_to_string, E4XNode, E4XNodeKind},
|
||||
object::E4XOrXml,
|
||||
object::{E4XOrXml, XmlListObject},
|
||||
Activation, Error, Object, TObject, Value,
|
||||
},
|
||||
avm2_stub_method,
|
||||
|
@ -79,3 +79,19 @@ pub fn length<'gc>(
|
|||
let children = list.children();
|
||||
Ok(children.len().into())
|
||||
}
|
||||
|
||||
pub fn children<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let list = this.unwrap().as_xml_list_object().unwrap();
|
||||
let children = list.children();
|
||||
let mut sub_children = Vec::new();
|
||||
for child in &*children {
|
||||
if let E4XNodeKind::Element { ref children, .. } = &*child.node().kind() {
|
||||
sub_children.extend(children.iter().map(|node| E4XOrXml::E4X(*node)));
|
||||
}
|
||||
}
|
||||
Ok(XmlListObject::new(activation, sub_children).into())
|
||||
}
|
||||
|
|
|
@ -195,4 +195,35 @@ impl<'gc> TObject<'gc> for XmlListObject<'gc> {
|
|||
) -> Result<(), Error<'gc>> {
|
||||
Err("Modifying an XMLList object is not yet implemented".into())
|
||||
}
|
||||
|
||||
fn get_next_enumerant(
|
||||
self,
|
||||
last_index: u32,
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Option<u32>, Error<'gc>> {
|
||||
let read = self.0.read();
|
||||
if (last_index as usize) < read.children.len() {
|
||||
return Ok(Some(last_index + 1));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_enumerant_name(
|
||||
self,
|
||||
index: u32,
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let children_len = self.0.read().children.len() as u32;
|
||||
if children_len >= index {
|
||||
Ok(index
|
||||
.checked_sub(1)
|
||||
.map(|index| index.into())
|
||||
.unwrap_or(Value::Undefined))
|
||||
} else {
|
||||
Ok(self
|
||||
.base()
|
||||
.get_enumerant_name(index - children_len)
|
||||
.unwrap_or(Value::Undefined))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,12 +149,33 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> {
|
|||
read.base.get_property_local(name, activation)
|
||||
}
|
||||
|
||||
fn set_property_local(
|
||||
self,
|
||||
_name: &Multiname<'gc>,
|
||||
_value: Value<'gc>,
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
Err("Modifying an XML object is not yet implemented".into())
|
||||
fn has_own_property(self, name: &Multiname<'gc>) -> bool {
|
||||
let read = self.0.read();
|
||||
|
||||
// FIXME - see if we can deduplicate this with get_property_local in
|
||||
// an efficient way
|
||||
if name.contains_public_namespace() {
|
||||
if let Some(local_name) = name.local_name() {
|
||||
// The only supported numerical index is 0
|
||||
if let Ok(index) = local_name.parse::<usize>() {
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
if let E4XNodeKind::Element {
|
||||
children,
|
||||
attributes,
|
||||
} = &*read.node.kind()
|
||||
{
|
||||
let search_children = if name.is_attribute() {
|
||||
attributes
|
||||
} else {
|
||||
children
|
||||
};
|
||||
|
||||
return search_children.iter().any(|child| child.matches_name(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
read.base.has_own_dynamic_property(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package {
|
||||
public class Test {
|
||||
public static function test() {
|
||||
var outer = <outer>
|
||||
<child kind="A">First Child</child>
|
||||
<child kind="B">Second Child</child>
|
||||
<child kind="A">Third Child: <p>Inner element</p></child>
|
||||
</outer>;
|
||||
|
||||
trace("Children length: " + outer.children().length());
|
||||
|
||||
trace("'child' in outer: " + ('child' in outer));
|
||||
|
||||
for each (var child in outer.children()) {
|
||||
trace("Child kind= " + child.@kind);
|
||||
}
|
||||
|
||||
for each (var innerChild in outer.children().children()) {
|
||||
trace("Inner child localName " + innerChild.localName());
|
||||
}
|
||||
|
||||
var empty = <myelem/>;
|
||||
trace("Empty children: " + empty.children().length());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
Children length: 3
|
||||
'child' in outer: true
|
||||
Child kind= A
|
||||
Child kind= B
|
||||
Child kind= A
|
||||
Inner child localName null
|
||||
Inner child localName null
|
||||
Inner child localName null
|
||||
Inner child localName p
|
||||
Empty children: 0
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_frames = 1
|
Loading…
Reference in New Issue