diff --git a/trac/attachment.py b/trac/attachment.py
--- a/trac/attachment.py
+++ b/trac/attachment.py
@@ -809,15 +809,10 @@
                                  title=get_resource_name(self.env, attachment))
                 href = get_resource_url(self.env, attachment, formatter.href)
                 title = get_resource_name(self.env, attachment)
-                img = tag.img(src=formatter.href.chrome('common/download.png'),
-                              alt=_("Download"))
                 return tag(tag.a(label, class_='attachment', title=title,
                                  href=href + params),
-                           tag.span(" ",
-                                    tag.a(img, class_='trac-rawlink',
-                                          href=raw_href + params,
-                                          title=_("Download")),
-                                    class_="noprint"))
+                           tag.a(u'\u200b', class_='trac-rawlink',
+                                 href=raw_href + params, title=_("Download")))
             except ResourceNotFound:
                 pass
             # FIXME: should be either:
diff --git a/trac/htdocs/css/browser.css b/trac/htdocs/css/browser.css
--- a/trac/htdocs/css/browser.css
+++ b/trac/htdocs/css/browser.css
@@ -63,53 +63,57 @@
  vertical-align: middle;
  font-size: 70%;
 }
+table.dirlist td.size a.trac-rawlink {
+ margin-left: .3em;
+}
 table.dirlist td.age {
  border-width: 0 2px 0 0;
  border-style: solid;
  font-size: 85%;
 }
-table.dirlist td.name { width: 100% }
-table.dirlist td.name a, table.dirlist td.name span {
- background-position: 0% 50%;
- background-repeat: no-repeat;
- padding-left: 20px;
+table.dirlist td.name { width: 100%; white-space: nowrap }
+table.dirlist td.name a.parent {
+  background: url(../parent.png) 0 50% no-repeat;
+  padding-left: 20px;
 }
-table.dirlist td.name a.parent { background-image: url(../parent.png) }
-table.dirlist td.name div { white-space: pre }
 table.dirlist tr span.expander { 
-  background-image: url(../expander_normal.png); 
+  background: url(../expander_normal.png) 0 50% no-repeat; 
   cursor: pointer; 
   padding-left: 8px; 
   margin-left: 4px; 
 }
 table.dirlist tr span.expander:hover { 
-  background-image: url(../expander_normal_hover.png); 
+  background: url(../expander_normal_hover.png) 0 50% no-repeat;
 }
 table.dirlist tr.expanded span.expander { 
-  background-image: url(../expander_open.png); 
+  background: url(../expander_open.png) 0 50% no-repeat; 
   padding-left: 12px; 
   margin-left: 0; 
 }
 table.dirlist tr.expanded span.expander:hover { 
-  background-image: url(../expander_open_hover.png); 
+  background: url(../expander_open_hover.png) 0 50% no-repeat; 
 }
-table.dirlist td.name a.dir { background-image: url(../folder.png) }
-table.dirlist td.name a.file { background-image: url(../file.png); display: block }
+table.dirlist td.name a.dir {
+  background: url(../folder.png) 0 50% no-repeat;
+  padding-left: 20px;
+}
+table.dirlist td.name a.file {
+  background: url(../file.png) 0 50% no-repeat;
+  padding-left: 20px;
+}
 table.dirlist td.name a, table.dirlist td.rev a { border-bottom: none }
 table.dirlist td.author, table.dirlist td.change { font-size: 85% }
 table.dirlist td.rev a.chgset { 
-  background-repeat: no-repeat;
-  background-image: url(../changeset.png); 
-  background-position: 100% 50%;
+  background: url(../changeset.png) 100% 50% no-repeat;
   padding: 0 0 0 5px; 
   margin: 0 5px 0 0; 
 }
 table.dirlist td.description { padding-left: 2em }
 table.dirlist td.description > :first-child { margin-top: 0 }
 table.dirlist td.description > :last-child { margin-bottom: 0 }
-
 table.dirlist td span.loading { 
-  background-image: url(../loading.gif); 
+  background: url(../loading.gif) 0 50% no-repeat;
+  padding-left: 20px;
   font-style: italic 
 }
 
diff --git a/trac/htdocs/css/trac.css b/trac/htdocs/css/trac.css
--- a/trac/htdocs/css/trac.css
+++ b/trac/htdocs/css/trac.css
@@ -60,6 +60,10 @@
   background: url(../envelope.png) center center no-repeat;
   padding-left: 14px;
  }
+ a.trac-rawlink {
+  background: url(../download.png) center center no-repeat;
+  padding-left: 21px;
+ }
 }
 
 /* Forms */
@@ -616,7 +620,7 @@
 @media print {
  #header, #altlinks, #footer, #help { display: none }
  .nav, form, .buttons form, form .buttons, form .inlinebuttons,
- .noprint, .trac-rawlink, .trac-nav, .trac-topnav {
+ .noprint, .trac-nav, .trac-topnav {
    display: none;
  }
  form.printableform { display: block }
diff --git a/trac/htdocs/js/expand_dir.js b/trac/htdocs/js/expand_dir.js
--- a/trac/htdocs/js/expand_dir.js
+++ b/trac/htdocs/js/expand_dir.js
@@ -41,7 +41,7 @@
         var expander = $('<span class="expander">&nbsp;</span>')
           .attr("title", _("Expand sub-directory in place"))
           .click(function() { toggleDir($(this), qargs); })
-        a.wrap('<div></div>').before(expander);
+        a.before(expander);
         if (autoexpand && a.text() == autoexpand[0])
           autoexpand_expander = expander;
       }
diff --git a/trac/templates/list_of_attachments.html b/trac/templates/list_of_attachments.html
--- a/trac/templates/list_of_attachments.html
+++ b/trac/templates/list_of_attachments.html
@@ -16,9 +16,8 @@
                foldable = value_of('foldable', False)" py:strip="">
   <py:def function="show_one_attachment(attachment)">
     <i18n:msg params="file, size, author, date">
-      <a href="${url_of(attachment.resource)}" title="View attachment">$attachment.filename</a>
-      <a href="${url_of(attachment.resource, format='raw')}" class="trac-rawlink"
-         title="Download"><img src="${chrome.htdocs_location}download.png" alt="Download"/></a>
+      <a href="${url_of(attachment.resource)}" title="View attachment">${attachment.filename
+        }</a><a href="${url_of(attachment.resource, format='raw')}" class="trac-rawlink" title="Download">&#8203;</a>
        (<span title="${_('%(size)s bytes', size=attachment.size)}">${pretty_size(attachment.size)}</span>) -
       added by <em>${authorinfo(attachment.author)}</em> ${dateinfo(attachment.date)} ago.
     </i18n:msg>
diff --git a/trac/tests/wikisyntax.py b/trac/tests/wikisyntax.py
--- a/trac/tests/wikisyntax.py
+++ b/trac/tests/wikisyntax.py
@@ -40,7 +40,7 @@
 ------------------------------
 """
 
-ATTACHMENT_TEST_CASES = """
+ATTACHMENT_TEST_CASES = u"""
 ============================== attachment: link resolver (deprecated)
 attachment:wiki:WikiStart:file.txt (deprecated)
 attachment:ticket:123:file.txt (deprecated)
@@ -48,10 +48,10 @@
 [attachment:ticket:123:file.txt] (deprecated)
 ------------------------------
 <p>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:wiki:WikiStart:file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)
-<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:ticket:123:file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)
-<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">ticket:123:file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span> (deprecated)
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:wiki:WikiStart:file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> (deprecated)
+<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:ticket:123:file.txt</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> (deprecated)
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a> (deprecated)
+<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">ticket:123:file.txt</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a> (deprecated)
 </p>
 ------------------------------
 ============================== attachment: "foreign" links
@@ -62,11 +62,11 @@
 attachment:foo.txt:wiki:SomePage/SubPage
 ------------------------------
 <p>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt:wiki:WikiStart</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:file.txt:ticket:123</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">file.txt:ticket:123</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/wiki/SomePage/SubPage/foo.txt" title="Attachment 'foo.txt' in SomePage/SubPage">attachment:foo.txt:wiki:SomePage/SubPage</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/SomePage/SubPage/foo.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt:wiki:WikiStart</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">attachment:file.txt:ticket:123</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/ticket/123/file.txt" title="Attachment 'file.txt' in Ticket #123">file.txt:ticket:123</a><a class="trac-rawlink" href="/raw-attachment/ticket/123/file.txt" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/wiki/SomePage/SubPage/foo.txt" title="Attachment 'foo.txt' in SomePage/SubPage">attachment:foo.txt:wiki:SomePage/SubPage</a><a class="trac-rawlink" href="/raw-attachment/wiki/SomePage/SubPage/foo.txt" title="Download">\u200b</a>
 </p>
 ------------------------------
 ============================== attachment: "local" links
@@ -74,8 +74,8 @@
 [attachment:file.txt that file]
 ------------------------------
 <p>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">that file</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">attachment:file.txt</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt" title="Attachment 'file.txt' in WikiStart">that file</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt" title="Download">\u200b</a>
 </p>
 ------------------------------
 ============================== attachment: "missing" links
@@ -101,8 +101,8 @@
 [attachment:file.txt?format=raw that file]
 ------------------------------
 <p>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">attachment:file.txt?format=raw</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
-<a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">that file</a><span class="noprint"> <a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download"><img src="/chrome/common/download.png" alt="Download"/></a></span>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">attachment:file.txt?format=raw</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download">\u200b</a>
+<a class="attachment" href="/attachment/wiki/WikiStart/file.txt?format=raw" title="Attachment 'file.txt' in WikiStart">that file</a><a class="trac-rawlink" href="/raw-attachment/wiki/WikiStart/file.txt?format=raw" title="Download">\u200b</a>
 </p>
 ------------------------------
 """ # "
diff --git a/trac/ticket/templates/ticket_change.html b/trac/ticket/templates/ticket_change.html
--- a/trac/ticket/templates/ticket_change.html
+++ b/trac/ticket/templates/ticket_change.html
@@ -17,9 +17,9 @@
       <strong>${field.label}</strong>
       <py:choose>
         <py:when test="field_name == 'attachment'"><i18n:msg params="name">
-          <a href="${href.attachment('ticket', ticket.id, field.new)}"><em>${field.new}</em></a>
-          <a href="${href('raw-attachment', 'ticket', ticket.id, field.new)}"
-             title="Download" class="trac-rawlink"><img src="${chrome.htdocs_location}download.png" alt="Download"/></a>
+          <a href="${href.attachment('ticket', ticket.id, field.new)}"><em>${field.new
+            }</em></a><a href="${href('raw-attachment', 'ticket', ticket.id, field.new)}"
+                         title="Download" class="trac-rawlink">&#8203;</a>
           added
         </i18n:msg></py:when>
         <py:when test="'rendered' in field">${field.rendered}</py:when>
diff --git a/trac/versioncontrol/templates/dir_entries.html b/trac/versioncontrol/templates/dir_entries.html
--- a/trac/versioncontrol/templates/dir_entries.html
+++ b/trac/versioncontrol/templates/dir_entries.html
@@ -13,7 +13,9 @@
                                   order=(order != 'name' and order or None), desc=desc)}">$entry.name</a>
         </td>
         <td class="size">
-          <span title="${_('%(size)s bytes', size=entry.content_length)}">${pretty_size(entry.content_length)}</span>
+          <span title="${_('%(size)s bytes', size=entry.content_length)}">${pretty_size(entry.content_length)
+            }</span><a py:if="entry.raw_href" class="trac-rawlink" href="$entry.raw_href"
+                       title="${entry.kind == 'dir' and _('Download as Zip archive') or _('Download')}">&#8203;</a>
         </td>
         <td class="rev">
           <a title="View Revision Log" href="${href.log(reponame, entry.path, rev=rev)}">${display_rev(entry.rev)}</a>
diff --git a/trac/versioncontrol/templates/repository_index.html b/trac/versioncontrol/templates/repository_index.html
--- a/trac/versioncontrol/templates/repository_index.html
+++ b/trac/versioncontrol/templates/repository_index.html
@@ -5,7 +5,7 @@
   <table class="listing dirlist" id="${repoindex or None}">
     <xi:include href="dirlist_thead.html" />
     <tbody>
-      <py:for each="idx, (reponame, repoinfo, repos, change, err) in enumerate(repo.repositories)"
+      <py:for each="idx, (reponame, repoinfo, repos, change, err, raw_href) in enumerate(repo.repositories)"
               py:with="chgset_context = change and context('changeset', change.rev, parent=repos.resource);
                        chgset_view = change and change.can_view(perm)">
         <tr class="${idx % 2 and 'even' or 'odd'}">
@@ -13,11 +13,13 @@
             <em py:strip="not err">
               <b py:strip="repoinfo.alias != ''">
                 <a class="dir" title="View Root Directory"
-                  href="${href.browser(repos.reponame, order=(order != 'name' and order or None), desc=desc)}">$reponame</a>
+                   href="${href.browser(repos.reponame, order=(order != 'name' and order or None), desc=desc)}">$reponame</a>
               </b>
             </em>
           </td>
-          <td class="size" />
+          <td class="size">
+            <a py:if="raw_href" class="trac-rawlink" href="$raw_href" title="Download as Zip archive">&#8203;</a>
+          </td>
           <td class="rev">
             <py:if test="not err">
               <a title="View Revision Log" href="${href.log(repos.reponame)}">${repos.display_rev(change.rev)}</a>
diff --git a/trac/versioncontrol/web_ui/browser.py b/trac/versioncontrol/web_ui/browser.py
--- a/trac/versioncontrol/web_ui/browser.py
+++ b/trac/versioncontrol/web_ui/browser.py
@@ -330,7 +330,7 @@
 
         path = req.args.get('path', '/')
         rev = req.args.get('rev', '')
-        if rev in ('', 'HEAD'):
+        if rev.lower() in ('', 'head'):
             rev = None
         order = req.args.get('order', 'name').lower()
         desc = req.args.has_key('desc')
@@ -499,20 +499,23 @@
                             timerange = TimeRange(youngest.date)
                         else:
                             timerange.insert(youngest.date)
-                    entry = (reponame, repoinfo, repos, youngest, None)
+                    raw_href = self._get_download_href(context.href, repos,
+                                                       None, None)
+                    entry = (reponame, repoinfo, repos, youngest, None,
+                             raw_href)
                 else:
-                    entry = (reponame, repoinfo, None, None, "XXX")
+                    entry = (reponame, repoinfo, None, None, "XXX", None)
             except TracError, err:
                 entry = (reponame, repoinfo, None, None,
-                         exception_to_unicode(err))
+                         exception_to_unicode(err), None)
             repositories.append(entry)
 
         # Ordering of repositories
         if order == 'date':
-            def repo_order((reponame, repoinfo, repos, youngest, err)):
+            def repo_order((reponame, repoinfo, repos, youngest, err, href)):
                 return youngest and youngest.date
         else:
-            def repo_order((reponame, repoinfo, repos, youngest, err)):
+            def repo_order((reponame, repoinfo, repos, youngest, err, href)):
                 return embedded_numbers(reponame.lower())
 
         repositories = sorted(repositories, key=repo_order, reverse=desc)
@@ -522,13 +525,16 @@
 
     def _render_dir(self, req, repos, node, rev, order, desc):
         req.perm(node.resource).require('BROWSER_VIEW')
+        download_href = self._get_download_href
 
         # Entries metadata
         class entry(object):
-            __slots__ = 'name rev kind isdir path content_length'.split()
+            _copy = 'name rev kind isdir path content_length'.split()
+            __slots__ = _copy + ['raw_href']
             def __init__(self, node):
-                for f in entry.__slots__:
+                for f in entry._copy:
                     setattr(self, f, getattr(node, f))
+                self.raw_href = download_href(req.href, repos, node, rev)
                 
         entries = [entry(n) for n in node.get_entries()
                    if n.can_view(req.perm)]
@@ -574,16 +580,8 @@
         entries = sorted(entries, key=browse_order, reverse=desc)
 
         # ''Zip Archive'' alternate link
-        path = node.path.strip('/')
-        if repos.reponame:
-            path = repos.reponame + '/' + path
-        if any(fnmatchcase(path, p.strip('/'))
-               for p in self.downloadable_paths):
-            zip_href = req.href.changeset(rev or repos.youngest_rev, 
-                                          repos.reponame or None, node.path,
-                                          old=rev,
-                                          old_path=repos.reponame or '/',
-                                          format='zip')
+        zip_href = self._get_download_href(req.href, repos, node, rev)
+        if zip_href:
             add_link(req, 'alternate', zip_href, _('Zip Archive'),
                      'application/zip', 'zip')
 
@@ -675,6 +673,21 @@
                 'annotate': force_source,
                 }
 
+    def _get_download_href(self, href, repos, node, rev):
+        """Return the URL for downloading a file, or a directory as a ZIP."""
+        if node is not None and node.isfile:
+            return href.export(rev or 'HEAD', repos.reponame or None,
+                               node.path)
+        path = npath = node is not None and node.path.strip('/') or ''
+        if repos.reponame:
+            path = (repos.reponame + '/' + npath).rstrip('/')
+        if any(fnmatchcase(path, p.strip('/'))
+               for p in self.downloadable_paths):
+            return href.changeset(rev or repos.youngest_rev, 
+                                  repos.reponame or None, npath,
+                                  old=rev, old_path=repos.reponame or '/',
+                                  format='zip')
+
     # public methods
     
     def render_properties(self, mode, context, props):
@@ -739,9 +752,13 @@
         elif '@' in export:
             path, rev = export.split('@', 1)
         else:
-            rev, path = 'HEAD', export
-        return tag.a(label, class_='export',
-                     href=formatter.href.export(rev, path) + fragment)
+            rev, path = None, export
+        node, raw_href, title = self._get_link_info(path, rev, formatter.href,
+                                                    formatter.perm)
+        if raw_href:
+            return tag.a(label, class_='export', href=raw_href + fragment,
+                         title=title)
+        return tag.a(label, class_='missing export')
 
     def _format_browser_link(self, formatter, ns, path, label):
         path, query, fragment = formatter.split_link(path)
@@ -749,15 +766,34 @@
         match = self.PATH_LINK_RE.match(path)
         if match:
             path, rev, marks = match.groups()
-        return tag.a(label, class_='source',
-                     href=(formatter.href.browser(path, rev=rev, marks=marks) +
-                           query + fragment))
+        href = formatter.href
+        src_href = href.browser(path, rev=rev, marks=marks) + query + fragment
+        node, raw_href, title = self._get_link_info(path, rev, formatter.href,
+                                                    formatter.perm)
+        if not node:
+            return tag.a(label, class_='missing source')
+        link = tag.a(label, class_='source', href=src_href)
+        if raw_href:
+            link = tag(link, tag.a(u'\u200b', href=raw_href + fragment,
+                                   title=title, class_='trac-rawlink'))
+        return link
 
     PATH_LINK_RE = re.compile(r"([^@#:]*)"     # path
                               r"[@:]([^#:]+)?" # rev
                               r"(?::(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?" # marks
                               )
 
+    def _get_link_info(self, path, rev, href, perm):
+        rm = RepositoryManager(self.env)
+        reponame, repos, npath = rm.get_repository_by_path(path)
+        node = get_allowed_node(repos, npath, rev, perm)
+        if node is not None:
+            raw_href = self._get_download_href(href, repos, node, rev)
+            title = node.isfile and _("Download") \
+                    or _("Download as Zip archive")
+            return (node, raw_href, title)
+        return (None, None, None)
+        
     # IHTMLPreviewAnnotator methods
 
     def get_annotation_type(self):
diff --git a/trac/versioncontrol/web_ui/tests/wikisyntax.py b/trac/versioncontrol/web_ui/tests/wikisyntax.py
--- a/trac/versioncontrol/web_ui/tests/wikisyntax.py
+++ b/trac/versioncontrol/web_ui/tests/wikisyntax.py
@@ -3,7 +3,7 @@
 import unittest
 
 from trac.test import Mock
-from trac.versioncontrol import NoSuchChangeset
+from trac.versioncontrol import NoSuchChangeset, NoSuchNode
 from trac.versioncontrol.api import *
 from trac.versioncontrol.web_ui import *
 from trac.wiki.tests import formatter
@@ -23,11 +23,22 @@
             return '200'
         else:
             raise NoSuchChangeset(rev)
-    
+
+def _get_node(path, rev=None):
+    if path == 'foo':
+        return Mock(path=path, rev=rev, isfile=False,
+                    can_view=lambda resource: True)
+    elif path == 'missing/file':
+        raise NoSuchNode(path, rev)
+    else:
+        return Mock(path=path, rev=rev, isfile=True,
+                    can_view=lambda resource: True)
+
 def _get_repository(reponame):
     return Mock(reponame=reponame, youngest_rev='200',
                 get_changeset=_get_changeset,
-                normalize_rev=_normalize_rev)
+                normalize_rev=_normalize_rev,
+                get_node=_get_node)
 
 def repository_setup(tc):
     setattr(tc.env, 'get_repository', _get_repository)
@@ -252,7 +263,7 @@
 """
 
 
-SOURCE_TEST_CASES = """
+SOURCE_TEST_CASES = u"""
 ============================== source: link resolver
 source:/foo/bar
 source:/foo/bar#42   # no long works as rev spec
@@ -264,18 +275,20 @@
 source:/foo/bar@42#L20
 source:/foo/bar@head#L20
 source:/foo/bar@#L20
+source:/missing/file
 ------------------------------
 <p>
-<a class="source" href="/browser/foo/bar">source:/foo/bar</a>
-<a class="source" href="/browser/foo/bar#42">source:/foo/bar#42</a>   # no long works as rev spec
-<a class="source" href="/browser/foo/bar#head">source:/foo/bar#head</a> #
-<a class="source" href="/browser/foo/bar?rev=42">source:/foo/bar@42</a>
-<a class="source" href="/browser/foo/bar?rev=head">source:/foo/bar@head</a>
-<a class="source" href="/browser/foo%2520bar/baz%252Bquux">source:/foo%20bar/baz%2Bquux</a>
-<a class="source" href="/browser/?rev=42">source:@42</a>
-<a class="source" href="/browser/foo/bar?rev=42#L20">source:/foo/bar@42#L20</a>
-<a class="source" href="/browser/foo/bar?rev=head#L20">source:/foo/bar@head#L20</a>
-<a class="source" href="/browser/foo/bar#L20">source:/foo/bar@#L20</a>
+<a class="source" href="/browser/foo/bar">source:/foo/bar</a><a class="trac-rawlink" href="/export/HEAD/foo/bar" title="Download">\u200b</a>
+<a class="source" href="/browser/foo/bar#42">source:/foo/bar#42</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#42" title="Download">\u200b</a>   # no long works as rev spec
+<a class="source" href="/browser/foo/bar#head">source:/foo/bar#head</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#head" title="Download">\u200b</a> #
+<a class="source" href="/browser/foo/bar?rev=42">source:/foo/bar@42</a><a class="trac-rawlink" href="/export/42/foo/bar" title="Download">\u200b</a>
+<a class="source" href="/browser/foo/bar?rev=head">source:/foo/bar@head</a><a class="trac-rawlink" href="/export/head/foo/bar" title="Download">\u200b</a>
+<a class="source" href="/browser/foo%2520bar/baz%252Bquux">source:/foo%20bar/baz%2Bquux</a><a class="trac-rawlink" href="/export/HEAD/foo%2520bar/baz%252Bquux" title="Download">\u200b</a>
+<a class="source" href="/browser/?rev=42">source:@42</a><a class="trac-rawlink" href="/export/42/" title="Download">\u200b</a>
+<a class="source" href="/browser/foo/bar?rev=42#L20">source:/foo/bar@42#L20</a><a class="trac-rawlink" href="/export/42/foo/bar#L20" title="Download">\u200b</a>
+<a class="source" href="/browser/foo/bar?rev=head#L20">source:/foo/bar@head#L20</a><a class="trac-rawlink" href="/export/head/foo/bar#L20" title="Download">\u200b</a>
+<a class="source" href="/browser/foo/bar#L20">source:/foo/bar@#L20</a><a class="trac-rawlink" href="/export/HEAD/foo/bar#L20" title="Download">\u200b</a>
+<a class="missing source">source:/missing/file</a>
 </p>
 ------------------------------
 ============================== source: link resolver + query 
@@ -284,7 +297,7 @@
 ------------------------------
 <p>
 <a class="source" href="/browser/foo?order=size&amp;desc=1">source:/foo?order=size&amp;desc=1</a>
-<a class="source" href="/browser/foo/bar?format=raw">source:/foo/bar?format=raw</a>
+<a class="source" href="/browser/foo/bar?format=raw">source:/foo/bar?format=raw</a><a class="trac-rawlink" href="/export/HEAD/foo/bar" title="Download">\u200b</a>
 </p>
 ------------------------------
 ============================== source: provider, with quoting
@@ -294,10 +307,10 @@
 [source:"even with whitespaces" Path with spaces]
 ------------------------------
 <p>
-<a class="source" href="/browser/even%20with%20whitespaces">source:'even with whitespaces'</a>
-<a class="source" href="/browser/even%20with%20whitespaces">source:"even with whitespaces"</a>
-<a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a>
-<a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a>
+<a class="source" href="/browser/even%20with%20whitespaces">source:'even with whitespaces'</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a>
+<a class="source" href="/browser/even%20with%20whitespaces">source:"even with whitespaces"</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a>
+<a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a>
+<a class="source" href="/browser/even%20with%20whitespaces">Path with spaces</a><a class="trac-rawlink" href="/export/HEAD/even%20with%20whitespaces" title="Download">\u200b</a>
 </p>
 ------------------------------
 ============================== export: link resolver
@@ -306,16 +319,16 @@
 export:/foo/pict.gif@123
 ------------------------------
 <p>
-<a class="export" href="/export/HEAD/foo/bar.html">export:/foo/bar.html</a>
-<a class="export" href="/export/123/foo/pict.gif">export:123:/foo/pict.gif</a>
-<a class="export" href="/export/123/foo/pict.gif">export:/foo/pict.gif@123</a>
+<a class="export" href="/export/HEAD/foo/bar.html" title="Download">export:/foo/bar.html</a>
+<a class="export" href="/export/123/foo/pict.gif" title="Download">export:123:/foo/pict.gif</a>
+<a class="export" href="/export/123/foo/pict.gif" title="Download">export:/foo/pict.gif@123</a>
 </p>
 ------------------------------
 ============================== export: link resolver + fragment
 export:/foo/bar.html#header
 ------------------------------
 <p>
-<a class="export" href="/export/HEAD/foo/bar.html#header">export:/foo/bar.html#header</a>
+<a class="export" href="/export/HEAD/foo/bar.html#header" title="Download">export:/foo/bar.html#header</a>
 </p>
 ------------------------------
 """ # " (be Emacs friendly...)
diff --git a/trac/versioncontrol/web_ui/util.py b/trac/versioncontrol/web_ui/util.py
--- a/trac/versioncontrol/web_ui/util.py
+++ b/trac/versioncontrol/web_ui/util.py
@@ -19,12 +19,11 @@
 from genshi.builder import tag
 
 from trac.resource import ResourceNotFound 
-from trac.util.datefmt import pretty_timedelta
-from trac.util.text import shorten_line
 from trac.util.translation import tag_, _
 from trac.versioncontrol.api import NoSuchNode, NoSuchChangeset
 
-__all__ = ['get_changes', 'get_path_links', 'get_existing_node']
+__all__ = ['get_changes', 'get_path_links', 'get_existing_node',
+           'get_allowed_node']
 
 def get_changes(repos, revs):
     changes = {}
@@ -69,3 +68,12 @@
             tag.p(tag_("You can %(search)s in the repository history to see "
                        "if that path existed but was later removed",
                        search=search_a))))
+
+def get_allowed_node(repos, path, rev, perm):
+    if repos is not None:
+        try:
+            node = repos.get_node(path, rev)
+        except NoSuchNode:
+            return None
+        if node.can_view(perm):
+            return node

