Index: trac/versioncontrol/web_ui/browser.py
===================================================================
--- trac/versioncontrol/web_ui/browser.py	(revision 3285)
+++ trac/versioncontrol/web_ui/browser.py	(working copy)
@@ -57,6 +57,18 @@
         glob patterns, i.e. "*" can be used as a wild card)
         (''since 0.10'')""")
 
+    def href(self, req, path, *args, **dict):
+        base = 'browser'
+        if 'format' in dict:
+            format = dict.pop('format')
+            base = format + base
+            if format == 'txt':
+                path += '.txt'
+        else: # HTML preview
+            path += '.html'
+        return req.href(base, path, *args, **dict)
+        
+
     # INavigationContributor methods
 
     def get_active_navigation_item(self, req):
@@ -77,20 +89,30 @@
 
     def match_request(self, req):
         import re
-        match = re.match(r'/(browser|file)(?:(/.*))?', req.path_info)
+        match = re.match(r'/(raw|txt)?(browser|file)(?:(/.*))?', req.path_info)
         if match:
-            req.args['path'] = match.group(2) or '/'
-            if match.group(1) == 'file':
-                req.redirect(req.href.browser(req.args.get('path'),
-                                              rev=req.args.get('rev'),
-                                              format=req.args.get('format')),
+            req.args['path'] = match.group(3) or '/'
+            if match.group(2) == 'file':
+                req.redirect(self.href(req, req.args.get('path'),
+                                       rev=req.args.get('rev'),
+                                       format=req.args.get('format')),
                              permanent=True)
+            format = match.group(1)
+            if format:
+                req.args['format'] = format
             return True
 
     def process_request(self, req):
         path = req.args.get('path', '/')
         rev = req.args.get('rev') or None
+        format = req.args.get('format')
 
+        if format:
+            if path.endswith('.txt'):
+                path = path[:-4]
+        elif path.endswith('.html'):
+                path = path[:-5]
+
         # Find node for the requested path/rev
         repos = self.env.get_repository(req.authname)
         if rev:
@@ -114,7 +136,7 @@
             'path': path,
             'revision': rev,
             'props': properties,
-            'href': req.href.browser(path, rev=rev),
+            'href': self.href(req, path, rev=rev),
             'log_href': req.href.log(path, rev=rev),
             'restr_changeset_href': req.href.changeset(node.rev,
                                                        node.created_path),
@@ -130,7 +152,7 @@
             req.hdf['browser.is_dir'] = True
             self._render_directory(req, repos, node, rev)
         else:
-            self._render_file(req, repos, node, rev)
+            self._render_file(req, repos, node, rev, format)
 
         add_stylesheet(req, 'common/css/browser.css')
         return 'browser.cs', None
@@ -152,7 +174,7 @@
                 'rev': entry.rev,
                 'permission': 1, # FIXME
                 'log_href': req.href.log(entry.path, rev=rev),
-                'browser_href': req.href.browser(entry.path, rev=rev)
+                'browser_href': self.href(req, entry.path, rev=rev)
             })
         changes = get_changes(self.env, repos, [i['rev'] for i in info])
 
@@ -179,8 +201,8 @@
 
         switch_ordering_hrefs = {}
         for col in ('name', 'size', 'date'):
-            switch_ordering_hrefs[col] = req.href.browser(
-                node.path, rev=rev, order=col,
+            switch_ordering_hrefs[col] = self.href(
+                req, node.path, rev=rev, order=col,
                 desc=(col == order and not desc and 1 or None))
 
         # ''Zip Archive'' alternate link
@@ -196,7 +218,7 @@
                               'items': info, 'changes': changes,
                               'order_href': switch_ordering_hrefs}
 
-    def _render_file(self, req, repos, node, rev=None):
+    def _render_file(self, req, repos, node, rev, format):
         req.perm.assert_permission('FILE_VIEW')
 
         mimeview = Mimeview(self.env)
@@ -210,7 +232,6 @@
                         mime_type or 'text/plain'
 
         # Eventually send the file directly
-        format = req.args.get('format')
         if format in ['raw', 'txt']:
             req.send_response(200)
             req.send_header('Content-Type',
@@ -247,12 +268,12 @@
 
             # add ''Plain Text'' alternate link if needed
             if not is_binary(chunk) and mime_type != 'text/plain':
-                plain_href = req.href.browser(node.path, rev=rev, format='txt')
+                plain_href = self.href(req, node.path, rev=rev, format='txt')
                 add_link(req, 'alternate', plain_href, 'Plain Text',
                          'text/plain')
 
             # add ''Original Format'' alternate link (always)
-            raw_href = req.href.browser(node.path, rev=rev, format='raw')
+            raw_href = self.href(req, node.path, rev=rev, format='raw')
             add_link(req, 'alternate', raw_href, 'Original Format', mime_type)
 
             self.log.debug("Rendering preview of node %s@%s with mime-type %s"
@@ -287,5 +308,7 @@
         else:
             anchor = ''
         label = urllib.unquote(label)
-        return html.A(href=formatter.href.browser(path, rev=rev) + anchor,
-                      class_='source')[label]
+        href = formatter.href.browser(path, rev=rev)
+        if formatter.req:
+            href = self.href(formatter.req, path, rev=rev)
+        return html.A(label, href=href+anchor, class_='source')

