xml: Refactor `XmlNode::into_string`
* Don't use `quick_xml::Writer` for formatting the XML, being much more simple. * Return `WString` instead of `String`, reducing `to_utf8_lossy()` calls except when the string needs to be escaped (attribute values and text contents).
This commit is contained in:
parent
a3288fa20c
commit
dbddefb44d
|
@ -80,10 +80,10 @@ fn serialize_value<'gc>(
|
||||||
let length = o.length(activation).unwrap();
|
let length = o.length(activation).unwrap();
|
||||||
Some(AmfValue::ECMAArray(vec![], values, length as u32))
|
Some(AmfValue::ECMAArray(vec![], values, length as u32))
|
||||||
} else if let Some(xml_node) = o.as_xml_node() {
|
} else if let Some(xml_node) = o.as_xml_node() {
|
||||||
xml_node
|
Some(AmfValue::XML(
|
||||||
.into_string()
|
xml_node.into_string().to_utf8_lossy().into_owned(),
|
||||||
.map(|xml_string| AmfValue::XML(xml_string, true))
|
true,
|
||||||
.ok()
|
))
|
||||||
} else if let Some(date) = o.as_date_object() {
|
} else if let Some(date) = o.as_date_object() {
|
||||||
date.date_time()
|
date.date_time()
|
||||||
.map(|date_time| AmfValue::Date(date_time.timestamp_millis() as f64, None))
|
.map(|date_time| AmfValue::Date(date_time.timestamp_millis() as f64, None))
|
||||||
|
|
|
@ -294,7 +294,7 @@ fn spawn_xml_fetch<'gc>(
|
||||||
let request_options = if let Some(node) = send_object {
|
let request_options = if let Some(node) = send_object {
|
||||||
// Send `node` as string.
|
// Send `node` as string.
|
||||||
RequestOptions::post(Some((
|
RequestOptions::post(Some((
|
||||||
node.into_string().unwrap_or_default().into_bytes(),
|
node.into_string().to_utf8_lossy().into_owned().into_bytes(),
|
||||||
"application/x-www-form-urlencoded".to_string(),
|
"application/x-www-form-urlencoded".to_string(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -211,16 +211,7 @@ fn to_string<'gc>(
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(node) = this.as_xml_node() {
|
if let Some(node) = this.as_xml_node() {
|
||||||
let result = node.into_string();
|
return Ok(AvmString::new(activation.context.gc_context, node.into_string()).into());
|
||||||
|
|
||||||
return Ok(AvmString::new_utf8(
|
|
||||||
activation.context.gc_context,
|
|
||||||
result.unwrap_or_else(|e| {
|
|
||||||
avm_warn!(activation, "XMLNode toString failed: {}", e);
|
|
||||||
"".to_string()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok("".into())
|
Ok("".into())
|
||||||
|
|
|
@ -8,13 +8,11 @@ use crate::string::{AvmString, WStr, WString};
|
||||||
use crate::xml;
|
use crate::xml;
|
||||||
use crate::xml::Error;
|
use crate::xml::Error;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use quick_xml::events::attributes::Attribute;
|
use quick_xml::escape::escape;
|
||||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
use quick_xml::events::{BytesStart, BytesText};
|
||||||
use quick_xml::Writer;
|
|
||||||
use smallvec::alloc::borrow::Cow;
|
use smallvec::alloc::borrow::Cow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Cursor, Write};
|
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
|
|
||||||
/// Represents a node in the XML tree.
|
/// Represents a node in the XML tree.
|
||||||
|
@ -828,80 +826,67 @@ impl<'gc> XmlNode<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the given node to a string of UTF-8 encoded XML.
|
/// Convert the given node to a string of UTF-8 encoded XML.
|
||||||
pub fn into_string(self) -> Result<String, Error> {
|
pub fn into_string(self) -> WString {
|
||||||
let mut buf = Vec::new();
|
let mut result = WString::new();
|
||||||
let mut writer = Writer::new(Cursor::new(&mut buf));
|
self.write_node_to_string(&mut result);
|
||||||
self.write_node_to_event_writer(&mut writer)?;
|
result
|
||||||
Ok(String::from_utf8(buf)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the contents of this node, including its children, to the given writer.
|
/// Write the contents of this node, including its children, to the given string.
|
||||||
fn write_node_to_event_writer<W>(self, writer: &mut Writer<W>) -> Result<(), Error>
|
fn write_node_to_string(self, result: &mut WString) {
|
||||||
where
|
// TODO: we convert some strings to utf8, replacing unpaired surrogates by the replacement char.
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
// TODO: we convert all strings to utf8, replacing unpaired surrogates by the replacement char.
|
|
||||||
// It is correct?
|
// It is correct?
|
||||||
|
|
||||||
let children: Vec<_> = self.children().collect();
|
let children: Vec<_> = self.children().collect();
|
||||||
let children_len = children.len();
|
|
||||||
|
|
||||||
match &*self.0.read() {
|
match &*self.0.read() {
|
||||||
XmlNodeData::DocumentRoot { .. } => Ok(()),
|
XmlNodeData::DocumentRoot { .. } => {}
|
||||||
XmlNodeData::Element {
|
XmlNodeData::Element {
|
||||||
tag_name,
|
tag_name,
|
||||||
attributes,
|
attributes,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut node_name = tag_name.to_utf8_lossy();
|
result.push_byte(b'<');
|
||||||
if children_len == 0 {
|
result.push_str(tag_name);
|
||||||
let mut n = node_name.into_owned();
|
|
||||||
n.push(' ');
|
|
||||||
node_name = n.into();
|
|
||||||
}
|
|
||||||
let mut bs = match node_name {
|
|
||||||
Cow::Borrowed(name) => BytesStart::borrowed_name(name.as_bytes()),
|
|
||||||
Cow::Owned(name) => BytesStart::owned_name(name),
|
|
||||||
};
|
|
||||||
for (key, value) in attributes {
|
for (key, value) in attributes {
|
||||||
bs.push_attribute(Attribute::from((
|
result.push_byte(b' ');
|
||||||
key.to_utf8_lossy().as_ref(),
|
result.push_str(key);
|
||||||
value.to_utf8_lossy().as_ref(),
|
result.push_str(WStr::from_units(b"=\""));
|
||||||
)));
|
let encoded_value = value.to_utf8_lossy();
|
||||||
|
let escaped_value = escape(encoded_value.as_bytes());
|
||||||
|
result.push_str(WStr::from_units(&*escaped_value));
|
||||||
|
result.push_byte(b'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
if children_len > 0 {
|
if children.is_empty() {
|
||||||
writer.write_event(&Event::Start(bs))
|
result.push_str(WStr::from_units(b" />"));
|
||||||
} else {
|
} else {
|
||||||
writer.write_event(&Event::Empty(bs))
|
result.push_byte(b'>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XmlNodeData::Text { contents, .. } => {
|
||||||
|
let encoded = contents.to_utf8_lossy();
|
||||||
|
let escaped = escape(encoded.as_bytes());
|
||||||
|
result.push_str(WStr::from_units(&*escaped));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XmlNodeData::Text { contents, .. } => writer.write_event(&Event::Text(
|
|
||||||
BytesText::from_plain_str(&contents.to_utf8_lossy()),
|
|
||||||
)),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
for child in children {
|
for child in &children {
|
||||||
child.write_node_to_event_writer(writer)?;
|
child.write_node_to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &*self.0.read() {
|
match &*self.0.read() {
|
||||||
XmlNodeData::DocumentRoot { .. } => Ok(()),
|
XmlNodeData::DocumentRoot { .. } => {}
|
||||||
XmlNodeData::Element { tag_name, .. } => {
|
XmlNodeData::Element { tag_name, .. } => {
|
||||||
if children_len > 0 {
|
if !children.is_empty() {
|
||||||
let bs = match tag_name.to_utf8_lossy() {
|
result.push_str(WStr::from_units(b"</"));
|
||||||
Cow::Borrowed(name) => BytesEnd::borrowed(name.as_bytes()),
|
result.push_str(tag_name);
|
||||||
Cow::Owned(name) => BytesEnd::owned(name.into()),
|
result.push_byte(b'>');
|
||||||
};
|
|
||||||
writer.write_event(&Event::End(bs))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XmlNodeData::Text { .. } => Ok(()),
|
XmlNodeData::Text { .. } => {}
|
||||||
}?;
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue