Rewrite the machinery for nested XML handlers in Rust

The C code is incorrect:  the end_element() functions in the vtables
don't ever get called, because of the way the code checks for nested
handlers.

We now have a simpler scheme in Rust.

Don't free the element_name_stack by hand from the C code.  Let the
Rust code deal with that itself.  We'll change from using a stack of
element names, to a stack of XmlContext.
parent 1dcaee32
......@@ -52,10 +52,9 @@ typedef struct RsvgXmlState RsvgXmlState;
extern RsvgXmlState *rsvg_xml_state_new ();
extern void rsvg_xml_state_free (RsvgXmlState *xml);
extern RsvgTree *rsvg_xml_state_steal_tree(RsvgXmlState *xml);
extern void rsvg_xml_state_free_element_name_stack(RsvgXmlState *xml);
extern void rsvg_xml_state_standard_element_start(RsvgXmlState *xml, RsvgHandle *handle, const char *name, RsvgPropertyBag atts);
extern void rsvg_xml_state_standard_element_end(RsvgXmlState *xml, RsvgHandle *handle, const char *name);
extern void rsvg_xml_state_add_characters(RsvgXmlState *xml, const char *characters, gsize len);
extern void rsvg_xml_state_start_element(RsvgXmlState *xml, RsvgHandle *handle, const char *name, RsvgPropertyBag atts);
extern void rsvg_xml_state_end_element(RsvgXmlState *xml, RsvgHandle *handle, const char *name);
extern void rsvg_xml_state_characters(RsvgXmlState *xml, const char *unterminated_text, gsize len);
/* Holds the XML parsing state */
......@@ -280,7 +279,7 @@ xinclude_handler_characters (RsvgSaxHandler * self, const char *ch, gsize len)
RsvgSaxHandlerXinclude *z = (RsvgSaxHandlerXinclude *) self;
if (z->in_fallback) {
rsvg_xml_state_add_characters (z->load->xml.rust_state, ch, len);
rsvg_xml_state_characters (z->load->xml.rust_state, ch, len);
}
}
......@@ -294,10 +293,10 @@ xinclude_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag
if (!strcmp (name, "xi:include"))
start_xinclude (z->load, atts);
else {
rsvg_xml_state_standard_element_start (z->load->xml.rust_state,
z->load->handle,
(const char *) name,
atts);
rsvg_xml_state_start_element (z->load->xml.rust_state,
z->load->handle,
(const char *) name,
atts);
}
} else if (!strcmp (name, "xi:fallback")) {
z->in_fallback = TRUE;
......@@ -496,7 +495,7 @@ start_xinclude (RsvgLoad *load, RsvgPropertyBag * atts)
data_len = text_data_len;
}
rsvg_xml_state_add_characters (load->xml.rust_state, data, data_len);
rsvg_xml_state_characters (load->xml.rust_state, data, data_len);
g_free (data);
......@@ -553,27 +552,31 @@ sax_start_element_cb (void *data, const xmlChar * name, const xmlChar ** atts)
RsvgLoad *load = data;
bag = rsvg_property_bag_new ((const char **) atts);
#if 0
if (load->xml.handler) {
load->xml.handler_nest++;
load->xml.handler->start_element (load->xml.handler, (const char *) name, bag);
} else {
#endif
const char *tempname;
for (tempname = (const char *) name; *tempname != '\0'; tempname++)
if (*tempname == ':')
name = (const xmlChar *) (tempname + 1);
#if 0
if (!strcmp ((const char *) name, "style"))
start_style (load, bag);
else if (!strcmp ((const char *) name, "include")) /* xi:include */
start_xinclude (load, bag);
else {
rsvg_xml_state_standard_element_start (load->xml.rust_state,
load->handle,
(const char *) name,
bag);
#endif
rsvg_xml_state_start_element (load->xml.rust_state,
load->handle,
(const char *) name,
bag);
#if 0
}
}
#endif
rsvg_property_bag_free (bag);
}
......@@ -584,23 +587,27 @@ sax_end_element_cb (void *data, const xmlChar * xmlname)
RsvgLoad *load = data;
const char *name = (const char *) xmlname;
#if 0
if (load->xml.handler_nest > 0 && load->xml.handler != NULL) {
load->xml.handler->end_element (load->xml.handler, name);
load->xml.handler_nest--;
} else {
#endif
const char *tempname;
for (tempname = name; *tempname != '\0'; tempname++)
if (*tempname == ':')
name = tempname + 1;
#if 0
if (load->xml.handler != NULL) {
load->xml.handler->free (load->xml.handler);
load->xml.handler = NULL;
}
rsvg_xml_state_standard_element_end (load->xml.rust_state, load->handle, name);
#endif
rsvg_xml_state_end_element (load->xml.rust_state, load->handle, name);
#if 0
}
#endif
}
static void
......@@ -608,12 +615,13 @@ sax_characters_cb (void *data, const xmlChar * ch, int len)
{
RsvgLoad *load = data;
#if 0
if (load->xml.handler) {
load->xml.handler->characters (load->xml.handler, (const char *) ch, (gsize) len);
return;
}
rsvg_xml_state_add_characters (load->xml.rust_state, (const char *) ch, (gsize) len);
#endif
rsvg_xml_state_characters (load->xml.rust_state, (const char *) ch, (gsize) len);
}
static xmlEntityPtr
......@@ -907,8 +915,6 @@ close_impl (RsvgLoad *load, GError ** error)
load->xml.ctxt = free_xml_parser_and_doc (load->xml.ctxt);
}
rsvg_xml_state_free_element_name_stack (load->xml.rust_state);
load->error = NULL;
if (real_error != NULL) {
......
......@@ -65,12 +65,11 @@ pub use property_bag::{
pub use structure::rsvg_node_svg_get_size;
pub use xml::{
rsvg_xml_state_add_characters,
rsvg_xml_state_characters,
rsvg_xml_state_end_element,
rsvg_xml_state_free,
rsvg_xml_state_free_element_name_stack,
rsvg_xml_state_new,
rsvg_xml_state_standard_element_end,
rsvg_xml_state_standard_element_start,
rsvg_xml_state_start_element,
rsvg_xml_state_steal_tree,
};
......
......@@ -15,25 +15,118 @@ use util::utf8_cstr;
/// A trait for processing a certain kind of XML subtree
///
/// In the "normal" state of processing, an `XmlContext` may create an RsvgNode
/// In the "normal" state of processing, an `XmlHandler` may create an RsvgNode
/// for each SVG element it finds, and create NodeChars inside those nodes when it
/// encounters character data.
///
/// There may be other, special contexts for different subtrees, for example,
/// for the `<style>` element.
trait XmlContext {
trait XmlHandler {
/// Called when the XML parser sees the beginning of an element
fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag);
fn start_element(
&self,
parent: Option<&Rc<Node>>,
handle: *mut RsvgHandle,
name: &str,
pbag: &PropertyBag,
) -> Box<XmlHandler>;
/// Called when the XML parser sees the end of an element
fn end_element(&mut self, handle: *mut RsvgHandle, name: &str);
/// Called when the XML parser sees the end of an element.
fn end_element(&self, handle: *mut RsvgHandle, name: &str) -> Rc<Node>;
/// Called when the XML parser sees character data or CDATA
fn characters(&mut self, text: &str);
fn characters(&self, text: &str);
fn get_node(&self) -> Rc<Node>;
}
struct NodeCreationContext {
node: Option<Rc<Node>>,
}
impl XmlHandler for NodeCreationContext {
fn start_element(
&self,
parent: Option<&Rc<Node>>,
handle: *mut RsvgHandle,
name: &str,
pbag: &PropertyBag,
) -> Box<XmlHandler> {
let mut defs = handle::get_defs(handle);
let mut is_svg = false;
let new_node = rsvg_load_new_node(name, parent, pbag, &mut defs, &mut is_svg);
if let Some(parent) = parent {
parent.add_child(&new_node);
}
new_node.set_atts(&new_node, handle, pbag);
// The "svg" node is special; it will parse its style attributes
// until the end, in standard_element_end().
if new_node.get_type() != NodeType::Svg {
new_node.parse_style_attributes(handle, name, pbag);
}
new_node.set_overridden_properties();
Box::new(NodeCreationContext {
node: Some(new_node),
})
}
fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Rc<Node> {
let node = self.node.as_ref().unwrap().clone();
// The "svg" node is special; it parses its style attributes
// here, not during element creation.
if node.get_type() == NodeType::Svg {
node.with_impl(|svg: &NodeSvg| {
svg.parse_style_attributes(&node, handle);
});
}
node
}
fn characters(&self, text: &str) {
let node = self.node.as_ref().unwrap();
if text.len() == 0 {
return;
}
if node.accept_chars() {
let chars_node = if let Some(child) = node.find_last_chars_child() {
child
} else {
let child = node_new(
NodeType::Chars,
Some(&node),
None,
None,
Box::new(NodeChars::new()),
);
node.add_child(&child);
child
};
chars_node.with_impl(|chars: &NodeChars| {
chars.append(text);
});
}
}
fn get_node(&self) -> Rc<Node> {
self.node.as_ref().unwrap().clone()
}
}
/// Called when the context terminates, i.e. when the parent element
/// that created this context is closed.
fn finish(&mut self);
/// A concrete parsing context for a surrounding `element_name` and its XML event handlers
struct Context {
element_name: String,
handler: Box<XmlHandler>,
}
// A *const RsvgXmlState is just the type that we export to C
......@@ -41,19 +134,15 @@ pub enum RsvgXmlState {}
struct XmlState {
tree: Option<Box<Tree>>,
current_node: Option<Rc<Node>>,
// Stack of element names while parsing; used to know when to stop
// parsing the current element.
element_name_stack: Vec<String>,
context_stack: Vec<Context>,
}
impl XmlState {
fn new() -> XmlState {
XmlState {
tree: None,
current_node: None,
element_name_stack: Vec::new(),
context_stack: Vec::new(),
}
}
......@@ -69,119 +158,43 @@ impl XmlState {
self.tree.take()
}
pub fn set_current_node(&mut self, node: Option<Rc<Node>>) {
self.current_node = node;
}
pub fn push_element_name(&mut self, name: &str) {
self.element_name_stack.push(name.to_string());
}
pub fn pop_element_name(&mut self) {
self.element_name_stack.pop();
}
pub fn topmost_element_name_is(&mut self, name: &str) -> bool {
let len = self.element_name_stack.len();
if len > 0 {
self.element_name_stack[len - 1] == name
pub fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag) {
let next_context = if let Some(top) = self.context_stack.last() {
top.handler
.start_element(Some(&top.handler.get_node()), handle, name, pbag)
} else {
false
}
}
pub fn free_element_name_stack(&mut self) {
self.element_name_stack.clear();
}
/// Starts a node for an SVG element of type `name` and hooks it to the tree.
///
/// `pbag` is the set of key/value pairs from the element's XML attributes.
pub fn standard_element_start(
&mut self,
handle: *mut RsvgHandle,
name: &str,
pbag: &PropertyBag,
) {
let mut defs = handle::get_defs(handle);
let mut is_svg = false;
let new_node = rsvg_load_new_node(
name,
self.current_node.as_ref(),
pbag,
&mut defs,
&mut is_svg,
);
self.push_element_name(name);
if let Some(ref current_node) = self.current_node {
current_node.add_child(&new_node);
} else if is_svg {
self.set_root(&new_node);
}
self.set_current_node(Some(new_node.clone()));
let default_context = NodeCreationContext { node: None };
new_node.set_atts(&new_node, handle, pbag);
default_context.start_element(None, handle, name, pbag)
};
// The "svg" node is special; it will parse its style attributes
// until the end, in standard_element_end().
if new_node.get_type() != NodeType::Svg {
new_node.parse_style_attributes(handle, name, pbag);
}
let context = Context {
element_name: name.to_string(),
handler: next_context,
};
new_node.set_overridden_properties();
self.context_stack.push(context);
}
/// Ends an SVG element for which we create a node.
pub fn standard_element_end(&mut self, handle: *mut RsvgHandle, name: &str) {
if let Some(ref current_node) = self.current_node.clone() {
// The "svg" node is special; it parses its style attributes
// here, not during element creation.
if current_node.get_type() == NodeType::Svg {
current_node.with_impl(|svg: &NodeSvg| {
svg.parse_style_attributes(current_node, handle);
});
}
if self.topmost_element_name_is(name) {
let parent = current_node.get_parent();
pub fn end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
if let Some(top) = self.context_stack.pop() {
assert!(name == top.element_name);
self.set_current_node(parent);
let node = top.handler.end_element(handle, name);
self.pop_element_name();
if self.context_stack.is_empty() {
self.set_root(&node);
}
} else {
panic!("end_element: XML handler stack is empty!?");
}
}
pub fn add_characters(&mut self, text: &str) {
if text.len() == 0 {
return;
}
if let Some(ref current_node) = self.current_node {
if current_node.accept_chars() {
let chars_node = if let Some(child) = current_node.find_last_chars_child() {
child
} else {
let child = node_new(
NodeType::Chars,
self.current_node.as_ref(),
None,
None,
Box::new(NodeChars::new()),
);
current_node.add_child(&child);
child
};
chars_node.with_impl(|chars: &NodeChars| {
chars.append(text);
});
}
pub fn characters(&mut self, text: &str) {
if let Some(top) = self.context_stack.last() {
top.handler.characters(text);
} else {
panic!("characters: XML handler stack is empty!?");
}
}
}
......@@ -213,15 +226,7 @@ pub extern "C" fn rsvg_xml_state_steal_tree(xml: *mut RsvgXmlState) -> *mut Rsvg
}
#[no_mangle]
pub extern "C" fn rsvg_xml_state_free_element_name_stack(xml: *mut RsvgXmlState) {
assert!(!xml.is_null());
let xml = unsafe { &mut *(xml as *mut XmlState) };
xml.free_element_name_stack();
}
#[no_mangle]
pub extern "C" fn rsvg_xml_state_standard_element_start(
pub extern "C" fn rsvg_xml_state_start_element(
xml: *mut RsvgXmlState,
handle: *mut RsvgHandle,
name: *const libc::c_char,
......@@ -236,11 +241,11 @@ pub extern "C" fn rsvg_xml_state_standard_element_start(
assert!(!pbag.is_null());
let pbag = unsafe { &*pbag };
xml.standard_element_start(handle, name, pbag);
xml.start_element(handle, name, pbag);
}
#[no_mangle]
pub extern "C" fn rsvg_xml_state_standard_element_end(
pub extern "C" fn rsvg_xml_state_end_element(
xml: *mut RsvgXmlState,
handle: *mut RsvgHandle,
name: *const libc::c_char,
......@@ -251,11 +256,11 @@ pub extern "C" fn rsvg_xml_state_standard_element_end(
assert!(!name.is_null());
let name = unsafe { utf8_cstr(name) };
xml.standard_element_end(handle, name);
xml.end_element(handle, name);
}
#[no_mangle]
pub extern "C" fn rsvg_xml_state_add_characters(
pub extern "C" fn rsvg_xml_state_characters(
xml: *mut RsvgXmlState,
unterminated_text: *const libc::c_char,
len: usize,
......@@ -270,5 +275,5 @@ pub extern "C" fn rsvg_xml_state_add_characters(
let bytes = unsafe { std::slice::from_raw_parts(unterminated_text as *const u8, len) };
let utf8 = unsafe { str::from_utf8_unchecked(bytes) };
xml.add_characters(utf8);
xml.characters(utf8);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment