diff --git a/core/src/avm2/e4x.rs b/core/src/avm2/e4x.rs index d23494c03..f55a3f3f4 100644 --- a/core/src/avm2/e4x.rs +++ b/core/src/avm2/e4x.rs @@ -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, 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)] diff --git a/core/src/avm2/globals/xml.rs b/core/src/avm2/globals/xml.rs index ef145c698..1db7f0d68 100644 --- a/core/src/avm2/globals/xml.rs +++ b/core/src/avm2/globals/xml.rs @@ -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, 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 + }, + ) } } diff --git a/core/src/avm2/object/xml_object.rs b/core/src/avm2/object/xml_object.rs index 4b3739909..193f95ca0 100644 --- a/core/src/avm2/object/xml_object.rs +++ b/core/src/avm2/object/xml_object.rs @@ -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, 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 { diff --git a/tests/tests/swfs/from_avmplus/e4x/XML/e13_4_4_23/test.toml b/tests/tests/swfs/from_avmplus/e4x/XML/e13_4_4_23/test.toml index 29f3cef79..cf6123969 100644 --- a/tests/tests/swfs/from_avmplus/e4x/XML/e13_4_4_23/test.toml +++ b/tests/tests/swfs/from_avmplus/e4x/XML/e13_4_4_23/test.toml @@ -1,2 +1 @@ num_ticks = 1 -known_failure = true