avm2: Make XML [[Delete]] follow the spec more closely

This commit is contained in:
Tom Schuster 2024-08-24 00:16:30 +02:00
parent c869505e88
commit a4f03173c2
1 changed files with 57 additions and 25 deletions

View File

@ -636,6 +636,7 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> {
} }
} }
// ECMA-357 9.1.1.3 [[Delete]] (P)
fn delete_property_local( fn delete_property_local(
self, self,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -643,40 +644,71 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> {
) -> Result<bool, Error<'gc>> { ) -> Result<bool, Error<'gc>> {
let name = handle_input_multiname(name.clone(), activation); let name = handle_input_multiname(name.clone(), activation);
if name.has_explicit_namespace() { // 1. If ToString(ToUint32(P)) == P, throw a TypeError exception
return Err(format!( // NOTE: This doesn't actually throw in Flash.
"Can not set property {:?} with an explicit namespace yet", if let Some(local_name) = name.local_name() {
name if local_name.parse::<usize>().is_ok() {
) return Ok(true);
.into()); }
} }
let mc = activation.context.gc_context;
let node = self.0.node.get(); let node = self.0.node.get();
let mut kind = node.kind_mut(mc);
let E4XNodeKind::Element { // 2. Let n = ToXMLName(P)
children,
attributes, // 3. If Type(n) is AttributeName
.. if name.is_attribute() {
} = &mut *kind let E4XNodeKind::Element { attributes, .. } = &mut *node.kind_mut(activation.gc())
else { else {
return Ok(false); return Ok(true);
};
// 3.a. For each a in x.[[Attributes]]
attributes.retain(|attr| {
// 3.a.i. If ((n.[[Name]].localName == "*") or
// (n.[[Name]].localName == a.[[Name]].localName))
// and ((n.[[Name]].uri == null) or (n.[[Name]].uri == a.[[Name]].uri))
if attr.matches_name(&name) {
// 3.a.i.1. Let a.[[Parent]] = null
attr.set_parent(None, activation.gc());
// 3.a.i.2. Remove the attribute a from x.[[Attributes]]
false
} else {
true
}
});
// 3.b. Return true
return Ok(true);
}
let E4XNodeKind::Element { children, .. } = &mut *node.kind_mut(activation.gc()) else {
return Ok(true);
}; };
let retain_non_matching = |node: &E4XNode<'gc>| { // 4. Let dp = 0
if node.matches_name(&name) { // 5. For q = 0 to x.[[Length]]-1
node.set_parent(None, mc); children.retain(|child| {
// 5.a. If ((n.localName == "*")
// or (x[q].[[Class]] == "element" and x[q].[[Name]].localName == n.localName))
// and ((n.uri == null) or (x[q].[[Class]] == “element” and n.uri == x[q].[[Name]].uri ))
let should_retain = if name.is_any_name() {
false false
} else if child.is_element() {
!child.matches_name(&name)
} else { } else {
true true
} };
};
if name.is_attribute() { if !should_retain {
attributes.retain(retain_non_matching); child.set_parent(None, activation.gc());
} else { }
children.retain(retain_non_matching);
} should_retain
});
// 6. Let x.[[Length]] = x.[[Length]] - dp
// 7. Return true.
Ok(true) Ok(true)
} }
} }