Index: trac/mimeview/api.py
===================================================================
--- trac/mimeview/api.py	(revision 3308)
+++ trac/mimeview/api.py	(working copy)
@@ -253,12 +253,20 @@
         """Return a list of target MIME types in same form as
         `IContentConverter.get_supported_conversions()`, but with the converter
         component appended. Output is ordered from best to worst quality."""
+        return sorted(self._get_all_conversions(mimetype, [], {}),
+                      key=lambda i: i[-1], reverse=True)
+
+    def _get_all_conversions(self, mimetype, pipeline, knowntypes):
         converters = []
         for converter in self.converters:
             for k, n, e, im, om, q in converter.get_supported_conversions():
                 if im == mimetype and q > 0:
-                    converters.append((k, n, e, im, om, q, converter))
-        converters = sorted(converters, key=lambda i: i[-1], reverse=True)
+                    newpipeline = [(converter,im,k)] + pipeline
+                    converters.append((k, n, e, im, om, q, newpipeline))
+                    if not om in knowntypes:
+                        knowntypes[om] = True
+                        converters += self._get_all_conversions(
+                            om, newpipeline, knowntypes)
         return converters
 
     def convert_content(self, req, mimetype, content, key, filename=None,
@@ -288,10 +296,14 @@
                             (mimetype, key))
 
         # First candidate which converts successfully wins.
-        for ck, name, ext, input_mimettype, output_mimetype, quality, \
-                converter in candidates:
+        for key, name, ext, input_mimettype, output_mimetype, quality, \
+                pipeline in candidates:
             try:
-                output = converter.convert_content(req, mimetype, content, ck)
+                output = (content, None)
+                while pipeline:
+                    converter, imimetype, ikey = pipeline.pop()
+                    output = converter.convert_content(req, imimetype,
+                                                       output[0], ikey)
                 if not output:
                     continue
                 return (output[0], output[1], ext)
Index: trac/ticket/web_ui.py
===================================================================
--- trac/ticket/web_ui.py	(revision 3308)
+++ trac/ticket/web_ui.py	(working copy)
@@ -208,7 +208,7 @@
 
     def get_supported_conversions(self):
         yield ('csv', 'Comma-delimited Text', 'csv',
-               'trac.ticket.model.Ticket', 'text/plain', 9)
+               'trac.ticket.model.Ticket', 'text/csv', 9)
         yield ('tab', 'Tab-delimited Text', 'csv', 'trac.ticket.model.Ticket',
                'text/plain', 9)
         yield ('rss', 'RSS Feed', 'xml', 'trac.ticket.model.Ticket',
Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 3308)
+++ trac/ticket/query.py	(working copy)
@@ -352,7 +352,7 @@
         yield ('rss', 'RSS Feed', 'xml', 'trac.ticket.query',
                'application/rss+xml', 9)
         yield ('csv', 'Comma-delimited Text', 'csv',
-               'trac.ticket.query', 'text/plain', 9)
+               'trac.ticket.query', 'text/csv', 9)
         yield ('tab', 'Tab-delimited Text', 'csv', 'trac.ticket.query',
                'text/plain', 9)
 

