avm2: Implement XML.namespace
This commit is contained in:
parent
3f99866ca4
commit
f20f57c6f1
|
@ -14,7 +14,7 @@ use crate::{avm2::TObject, xml::custom_unescape};
|
|||
|
||||
use super::{
|
||||
error::{make_error_1010, make_error_1118, type_error},
|
||||
object::{E4XOrXml, FunctionObject},
|
||||
object::{E4XOrXml, FunctionObject, NamespaceObject},
|
||||
string::AvmString,
|
||||
Activation, Error, Multiname, Value,
|
||||
};
|
||||
|
@ -110,6 +110,34 @@ impl<'gc> E4XNamespace<'gc> {
|
|||
pub fn new_uri(uri: AvmString<'gc>) -> Self {
|
||||
E4XNamespace { prefix: None, uri }
|
||||
}
|
||||
|
||||
pub fn default_namespace() -> Self {
|
||||
E4XNamespace {
|
||||
prefix: None,
|
||||
uri: "".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> E4XNamespace<'gc> {
|
||||
pub fn as_namespace_object(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<NamespaceObject<'gc>, Error<'gc>> {
|
||||
let args = if let Some(prefix) = self.prefix {
|
||||
vec![prefix.into(), self.uri.into()]
|
||||
} else {
|
||||
vec![self.uri.into()]
|
||||
};
|
||||
let obj = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.namespace
|
||||
.construct(activation, &args)?;
|
||||
Ok(obj
|
||||
.as_namespace_object()
|
||||
.expect("just constructed a namespace"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Collect, Debug)]
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
//! XML builtin and prototype
|
||||
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm2::e4x::{name_to_multiname, E4XNamespace, E4XNode, E4XNodeKind};
|
||||
use crate::avm2::error::{make_error_1117, type_error};
|
||||
pub use crate::avm2::object::xml_allocator;
|
||||
use crate::avm2::object::{
|
||||
E4XOrXml, NamespaceObject, QNameObject, TObject, XmlListObject, XmlObject,
|
||||
};
|
||||
use crate::avm2::object::{E4XOrXml, QNameObject, TObject, XmlListObject, XmlObject};
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::string::AvmString;
|
||||
use crate::avm2::{Activation, ArrayObject, Error, Multiname, Namespace, Object, Value};
|
||||
use crate::avm2::{Activation, ArrayObject, Error, Multiname, Object, Value};
|
||||
use crate::avm2_stub_method;
|
||||
|
||||
fn ill_formed_markup_err<'gc>(
|
||||
|
@ -121,7 +118,7 @@ pub fn name<'gc>(
|
|||
let xml = this.as_xml_object().unwrap();
|
||||
|
||||
if let Some(local_name) = xml.local_name() {
|
||||
let namespace = xml.namespace(activation);
|
||||
let namespace = xml.namespace_object(activation, &[])?.namespace();
|
||||
let mut multiname = Multiname::new(namespace, local_name);
|
||||
multiname.set_is_attribute(xml.node().is_attribute());
|
||||
Ok(QNameObject::from_name(activation, multiname)?.into())
|
||||
|
@ -179,17 +176,15 @@ pub fn namespace_internal_impl<'gc>(
|
|||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
avm2_stub_method!(activation, "XML", "namespace");
|
||||
let xml = this.as_xml_object().unwrap();
|
||||
let node = xml.node();
|
||||
|
||||
// FIXME:
|
||||
// 1. Let y = x
|
||||
// 2. Let inScopeNS = { }
|
||||
// 3. While (y is not null)
|
||||
// a. For each ns in y.[[InScopeNamespaces]]
|
||||
// ....
|
||||
|
||||
let xml = this.as_xml_object().unwrap();
|
||||
let node = xml.node();
|
||||
let in_scope_ns = node.in_scope_namespaces();
|
||||
|
||||
// 4. If prefix was not specified
|
||||
if args[0] == Value::Bool(false) {
|
||||
|
@ -205,28 +200,20 @@ pub fn namespace_internal_impl<'gc>(
|
|||
}
|
||||
|
||||
// b. Return the result of calling the [[GetNamespace]] method of x.[[Name]] with argument inScopeNS
|
||||
// FIXME: Use inScopeNS
|
||||
let namespace = xml.namespace(activation);
|
||||
Ok(NamespaceObject::from_namespace(activation, namespace)?.into())
|
||||
Ok(xml.namespace_object(activation, &in_scope_ns)?.into())
|
||||
} else {
|
||||
// a. Let prefix = ToString(prefix)
|
||||
let prefix = args.get_string(activation, 1)?;
|
||||
|
||||
// b. Find a Namespace ns ∈ inScopeNS, such that ns.prefix = prefix. If no such ns exists, let ns = undefined.
|
||||
// c. Return ns
|
||||
|
||||
// FIXME: Nodes currently either have zero or one namespace, which has the prefix "" (empty string)
|
||||
Ok(match node.namespace() {
|
||||
Some(ns) if prefix.is_empty() => {
|
||||
let namespace = Namespace::package(
|
||||
ns.uri,
|
||||
ApiVersion::AllVersions,
|
||||
&mut activation.context.borrow_gc(),
|
||||
);
|
||||
NamespaceObject::from_namespace(activation, namespace)?.into()
|
||||
}
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
Ok(
|
||||
if let Some(ns) = in_scope_ns.iter().find(|ns| ns.prefix == Some(prefix)) {
|
||||
ns.as_namespace_object(activation)?.into()
|
||||
} else {
|
||||
Value::Undefined
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
//! Object representation for XML objects
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm2::e4x::{string_to_multiname, E4XNamespace, E4XNode, E4XNodeKind};
|
||||
use crate::avm2::error::make_error_1087;
|
||||
use crate::avm2::multiname::NamespaceSet;
|
||||
use crate::avm2::object::script_object::ScriptObjectData;
|
||||
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject, XmlListObject};
|
||||
use crate::avm2::object::{
|
||||
ClassObject, NamespaceObject, Object, ObjectPtr, TObject, XmlListObject,
|
||||
};
|
||||
use crate::avm2::string::AvmString;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Namespace;
|
||||
use crate::avm2::{Error, Multiname};
|
||||
use core::fmt;
|
||||
use gc_arena::{Collect, GcCell, GcWeakCell, Mutation};
|
||||
|
@ -158,15 +158,33 @@ impl<'gc> XmlObject<'gc> {
|
|||
self.0.read().node.local_name()
|
||||
}
|
||||
|
||||
pub fn namespace(&self, activation: &mut Activation<'_, 'gc>) -> Namespace<'gc> {
|
||||
pub fn namespace_object(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
in_scope_ns: &[E4XNamespace<'gc>],
|
||||
) -> Result<NamespaceObject<'gc>, Error<'gc>> {
|
||||
// 13.3.5.4 [[GetNamespace]] ( [ InScopeNamespaces ] )
|
||||
// 1. If q.uri is null, throw a TypeError exception
|
||||
// NOTE: As stated in the spec, this not really possible
|
||||
match self.0.read().node.namespace() {
|
||||
Some(ns) => Namespace::package(
|
||||
ns.uri,
|
||||
ApiVersion::AllVersions,
|
||||
&mut activation.context.borrow_gc(),
|
||||
),
|
||||
None => activation.avm2().public_namespace_base_version,
|
||||
None => E4XNamespace::default_namespace(),
|
||||
Some(ns) => {
|
||||
// 2. If InScopeNamespaces was not specified, let InScopeNamespaces = { }
|
||||
// 3. Find a Namespace ns in InScopeNamespaces, such that ns.uri == q.uri. If more than one such
|
||||
// Namespace ns exists, the implementation may choose one of the matching Namespaces arbitrarily.
|
||||
// NOTE: Flash just uses whatever namespace URI matches first. They don't do anything with the prefix.
|
||||
if let Some(ns) = in_scope_ns.iter().find(|scope_ns| scope_ns.uri == ns.uri) {
|
||||
*ns
|
||||
} else {
|
||||
// 4. If no such namespace ns exists
|
||||
// a. Let ns be a new namespace created as if by calling the constructor new Namespace(q.uri)
|
||||
// NOTE: We could preserve the prefix here, but Flash doesn't bother.
|
||||
E4XNamespace::new_uri(ns.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 5. Return ns
|
||||
.as_namespace_object(activation)
|
||||
}
|
||||
|
||||
pub fn matches_name(&self, multiname: &Multiname<'gc>) -> bool {
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
num_ticks = 1
|
||||
known_failure = true
|
||||
|
|
Loading…
Reference in New Issue