diff --git a/core/src/avm2/object/xml_object.rs b/core/src/avm2/object/xml_object.rs index 076cfe3e8..361377882 100644 --- a/core/src/avm2/object/xml_object.rs +++ b/core/src/avm2/object/xml_object.rs @@ -636,6 +636,7 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> { } } + // ECMA-357 9.1.1.3 [[Delete]] (P) fn delete_property_local( self, activation: &mut Activation<'_, 'gc>, @@ -643,40 +644,71 @@ impl<'gc> TObject<'gc> for XmlObject<'gc> { ) -> Result> { let name = handle_input_multiname(name.clone(), activation); - if name.has_explicit_namespace() { - return Err(format!( - "Can not set property {:?} with an explicit namespace yet", - name - ) - .into()); + // 1. If ToString(ToUint32(P)) == P, throw a TypeError exception + // NOTE: This doesn't actually throw in Flash. + if let Some(local_name) = name.local_name() { + if local_name.parse::().is_ok() { + return Ok(true); + } } - let mc = activation.context.gc_context; let node = self.0.node.get(); - let mut kind = node.kind_mut(mc); - let E4XNodeKind::Element { - children, - attributes, - .. - } = &mut *kind - else { - return Ok(false); + + // 2. Let n = ToXMLName(P) + + // 3. If Type(n) is AttributeName + if name.is_attribute() { + let E4XNodeKind::Element { attributes, .. } = &mut *node.kind_mut(activation.gc()) + else { + 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>| { - if node.matches_name(&name) { - node.set_parent(None, mc); + // 4. Let dp = 0 + // 5. For q = 0 to x.[[Length]]-1 + 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 + } else if child.is_element() { + !child.matches_name(&name) } else { true - } - }; + }; - if name.is_attribute() { - attributes.retain(retain_non_matching); - } else { - children.retain(retain_non_matching); - } + if !should_retain { + child.set_parent(None, activation.gc()); + } + + should_retain + }); + + // 6. Let x.[[Length]] = x.[[Length]] - dp + // 7. Return true. Ok(true) } }