avm2: Implement XML.removeNamespace
This commit is contained in:
parent
d8b4ec0404
commit
5834f273e2
|
@ -1158,6 +1158,30 @@ impl<'gc> E4XNode<'gc> {
|
||||||
self.0.read().notification
|
self.0.read().notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 13.3.5.4 [[GetNamespace]] ( [ InScopeNamespaces ] )
|
||||||
|
pub fn get_namespace(&self, in_scope_ns: &[E4XNamespace<'gc>]) -> E4XNamespace<'gc> {
|
||||||
|
// 1. If q.uri is null, throw a TypeError exception
|
||||||
|
// NOTE: As stated in the spec, this isn't really possible.
|
||||||
|
match self.namespace() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
pub fn in_scope_namespaces(&self) -> Vec<E4XNamespace<'gc>> {
|
pub fn in_scope_namespaces(&self) -> Vec<E4XNamespace<'gc>> {
|
||||||
let mut result: Vec<E4XNamespace<'gc>> = Vec::new();
|
let mut result: Vec<E4XNamespace<'gc>> = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -339,9 +339,90 @@ pub fn set_namespace<'gc>(
|
||||||
pub fn remove_namespace<'gc>(
|
pub fn remove_namespace<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
_args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
avm2_stub_method!(activation, "XML", "removeNamespace");
|
let xml = this.as_xml_object().unwrap();
|
||||||
|
let node = xml.node();
|
||||||
|
|
||||||
|
// 1. If x.[[Class]] ∈ {"text", "comment", "processing-instruction", "attribute"}, return x
|
||||||
|
if !node.is_element() {
|
||||||
|
return Ok(this.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Let ns be a Namespace object created as if by calling the function Namespace( namespace )
|
||||||
|
let value = args.get_value(0);
|
||||||
|
let ns = activation
|
||||||
|
.avm2()
|
||||||
|
.classes()
|
||||||
|
.namespace
|
||||||
|
.construct(activation, &[value])?
|
||||||
|
.as_namespace_object()
|
||||||
|
.unwrap();
|
||||||
|
let ns = E4XNamespace {
|
||||||
|
prefix: ns.prefix(),
|
||||||
|
uri: ns.namespace().as_uri(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Let thisNS be the result of calling [[GetNamespace]] on x.[[Name]] with argument x.[[InScopeNamespaces]]
|
||||||
|
let in_scope_ns = node.in_scope_namespaces();
|
||||||
|
let this_ns = node.get_namespace(&in_scope_ns);
|
||||||
|
|
||||||
|
// 4. If (thisNS == ns), return x
|
||||||
|
if this_ns == ns {
|
||||||
|
return Ok(this.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let E4XNodeKind::Element { attributes, .. } = &*node.kind() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. For each a in x.[[Attributes]]
|
||||||
|
for attr in attributes {
|
||||||
|
// 5.a. Let aNS be the result of calling [[GetNamespace]] on a.[[Name]] with argument x.[[InScopeNamespaces]]
|
||||||
|
let attr_ns = attr.get_namespace(&in_scope_ns);
|
||||||
|
// 5.b. If (aNS == ns), return x
|
||||||
|
if attr_ns == ns {
|
||||||
|
return Ok(this.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. If ns.prefix == undefined
|
||||||
|
if ns.prefix.is_none() {
|
||||||
|
let E4XNodeKind::Element {
|
||||||
|
ref mut namespaces, ..
|
||||||
|
} = &mut *node.kind_mut(activation.gc())
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
// 6.a. If there exists a namespace n ∈ x.[[InScopeNamespaces]],
|
||||||
|
// such that n.uri == ns.uri, remove the namespace n from x.[[InScopeNamespaces]]
|
||||||
|
namespaces.retain(|namespace| namespace.uri != ns.uri);
|
||||||
|
} else {
|
||||||
|
// 7. Else
|
||||||
|
let E4XNodeKind::Element {
|
||||||
|
ref mut namespaces, ..
|
||||||
|
} = &mut *node.kind_mut(activation.gc())
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
// 7.a. If there exists a namespace n ∈ x.[[InScopeNamespaces]],
|
||||||
|
// such that n.uri == ns.uri and n.prefix == ns.prefix, remove the namespace n from x.[[InScopeNamespaces]]
|
||||||
|
namespaces.retain(|namespace| *namespace != ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
let E4XNodeKind::Element { children, .. } = &*node.kind() else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
// 8. For each property p of x
|
||||||
|
for child in children {
|
||||||
|
// 8.a. If p.[[Class]] = "element", call the removeNamespace method of p with argument ns
|
||||||
|
if child.is_element() {
|
||||||
|
let xml = E4XOrXml::E4X(*child).get_or_create_xml(activation);
|
||||||
|
remove_namespace(activation, xml.into(), args)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 9. Return x
|
// 9. Return x
|
||||||
Ok(this.into())
|
Ok(this.into())
|
||||||
|
|
|
@ -168,28 +168,9 @@ impl<'gc> XmlObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
in_scope_ns: &[E4XNamespace<'gc>],
|
in_scope_ns: &[E4XNamespace<'gc>],
|
||||||
) -> Result<NamespaceObject<'gc>, Error<'gc>> {
|
) -> Result<NamespaceObject<'gc>, Error<'gc>> {
|
||||||
// 13.3.5.4 [[GetNamespace]] ( [ InScopeNamespaces ] )
|
self.node()
|
||||||
// 1. If q.uri is null, throw a TypeError exception
|
.get_namespace(in_scope_ns)
|
||||||
// NOTE: As stated in the spec, this not really possible
|
.as_namespace_object(activation)
|
||||||
match self.0.node.get().namespace() {
|
|
||||||
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 {
|
pub fn matches_name(&self, multiname: &Multiname<'gc>) -> bool {
|
||||||
|
|
Loading…
Reference in New Issue