netwerk/streamconv/converters/nsIndexedToHTML.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsIndexedToHTML.h"
     7 #include "mozilla/dom/EncodingUtils.h"
     8 #include "nsNetUtil.h"
     9 #include "netCore.h"
    10 #include "nsStringStream.h"
    11 #include "nsIFileURL.h"
    12 #include "nsEscape.h"
    13 #include "nsIDirIndex.h"
    14 #include "nsDateTimeFormatCID.h"
    15 #include "nsURLHelper.h"
    16 #include "nsIPlatformCharset.h"
    17 #include "nsIPrefService.h"
    18 #include "nsIPrefBranch.h"
    19 #include "nsIPrefLocalizedString.h"
    20 #include "nsIChromeRegistry.h"
    21 #include "nsICharsetConverterManager.h"
    22 #include "nsIDateTimeFormat.h"
    23 #include "nsIStringBundle.h"
    24 #include "nsITextToSubURI.h"
    25 #include "nsXPIDLString.h"
    26 #include <algorithm>
    28 NS_IMPL_ISUPPORTS(nsIndexedToHTML,
    29                   nsIDirIndexListener,
    30                   nsIStreamConverter,
    31                   nsIRequestObserver,
    32                   nsIStreamListener)
    34 static void AppendNonAsciiToNCR(const nsAString& in, nsCString& out)
    35 {
    36   nsAString::const_iterator start, end;
    38   in.BeginReading(start);
    39   in.EndReading(end);
    41   while (start != end) {
    42     if (*start < 128) {
    43       out.Append(*start++);
    44     } else {
    45       out.AppendLiteral("&#x");
    46       out.AppendInt(*start++, 16);
    47       out.Append(';');
    48     }
    49   }
    50 }
    52 nsresult
    53 nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
    54     nsresult rv;
    55     if (aOuter)
    56         return NS_ERROR_NO_AGGREGATION;
    58     nsIndexedToHTML* _s = new nsIndexedToHTML();
    59     if (_s == nullptr)
    60         return NS_ERROR_OUT_OF_MEMORY;
    62     rv = _s->QueryInterface(aIID, aResult);
    63     return rv;
    64 }
    66 nsresult
    67 nsIndexedToHTML::Init(nsIStreamListener* aListener) {
    68     nsresult rv = NS_OK;
    70     mListener = aListener;
    72     mDateTime = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
    73     if (NS_FAILED(rv))
    74       return rv;
    76     nsCOMPtr<nsIStringBundleService> sbs =
    77         do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    78     if (NS_FAILED(rv)) return rv;
    79     rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle));
    81     mExpectAbsLoc = false;
    83     return rv;
    84 }
    86 NS_IMETHODIMP
    87 nsIndexedToHTML::Convert(nsIInputStream* aFromStream,
    88                          const char* aFromType,
    89                          const char* aToType,
    90                          nsISupports* aCtxt,
    91                          nsIInputStream** res) {
    92     return NS_ERROR_NOT_IMPLEMENTED;
    93 }
    95 NS_IMETHODIMP
    96 nsIndexedToHTML::AsyncConvertData(const char *aFromType,
    97                                   const char *aToType,
    98                                   nsIStreamListener *aListener,
    99                                   nsISupports *aCtxt) {
   100     return Init(aListener);
   101 }
   103 NS_IMETHODIMP
   104 nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) {
   105     nsCString buffer;
   106     nsresult rv = DoOnStartRequest(request, aContext, buffer);
   107     if (NS_FAILED(rv)) {
   108         request->Cancel(rv);
   109     }
   111     rv = mListener->OnStartRequest(request, aContext);
   112     if (NS_FAILED(rv)) return rv;
   114     // The request may have been canceled, and if that happens, we want to
   115     // suppress calls to OnDataAvailable.
   116     request->GetStatus(&rv);
   117     if (NS_FAILED(rv)) return rv;
   119     // Push our buffer to the listener.
   121     rv = SendToListener(request, aContext, buffer);
   122     return rv;
   123 }
   125 nsresult
   126 nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
   127                                   nsCString& aBuffer) {
   128     nsresult rv;
   130     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   131     nsCOMPtr<nsIURI> uri;
   132     rv = channel->GetURI(getter_AddRefs(uri));
   133     if (NS_FAILED(rv)) return rv;
   135     channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
   137     mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv);
   138     if (NS_FAILED(rv)) return rv;
   140     rv = mParser->SetListener(this);
   141     if (NS_FAILED(rv)) return rv;
   143     rv = mParser->OnStartRequest(request, aContext);
   144     if (NS_FAILED(rv)) return rv;
   146     nsAutoCString baseUri, titleUri;
   147     rv = uri->GetAsciiSpec(baseUri);
   148     if (NS_FAILED(rv)) return rv;
   149     titleUri = baseUri;
   151     nsCString parentStr;
   153     nsCString buffer;
   154     buffer.AppendLiteral("<!DOCTYPE html>\n<html>\n<head>\n");
   156     // XXX - should be using the 300: line from the parser.
   157     // We can't guarantee that that comes before any entry, so we'd have to
   158     // buffer, and do other painful stuff.
   159     // I'll deal with this when I make the changes to handle welcome messages
   160     // The .. stuff should also come from the lower level protocols, but that
   161     // would muck up the XUL display
   162     // - bbaetz
   164     bool isScheme = false;
   165     bool isSchemeFile = false;
   166     if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) {
   168         // strip out the password here, so it doesn't show in the page title
   169         // This is done by the 300: line generation in ftp, but we don't use
   170         // that - see above
   172         nsAutoCString pw;
   173         rv = uri->GetPassword(pw);
   174         if (NS_FAILED(rv)) return rv;
   175         if (!pw.IsEmpty()) {
   176              nsCOMPtr<nsIURI> newUri;
   177              rv = uri->Clone(getter_AddRefs(newUri));
   178              if (NS_FAILED(rv)) return rv;
   179              rv = newUri->SetPassword(EmptyCString());
   180              if (NS_FAILED(rv)) return rv;
   181              rv = newUri->GetAsciiSpec(titleUri);
   182              if (NS_FAILED(rv)) return rv;
   183         }
   185         nsAutoCString path;
   186         rv = uri->GetPath(path);
   187         if (NS_FAILED(rv)) return rv;
   189         if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) {
   190             rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr);
   191             if (NS_FAILED(rv)) return rv;
   192         }
   193     } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) {
   194         nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
   195         nsCOMPtr<nsIFile> file;
   196         rv = fileUrl->GetFile(getter_AddRefs(file));
   197         if (NS_FAILED(rv)) return rv;
   198         file->SetFollowLinks(true);
   200         nsAutoCString url;
   201         rv = net_GetURLSpecFromFile(file, url);
   202         if (NS_FAILED(rv)) return rv;
   203         baseUri.Assign(url);
   205         nsCOMPtr<nsIFile> parent;
   206         rv = file->GetParent(getter_AddRefs(parent));
   208         if (parent && NS_SUCCEEDED(rv)) {
   209             net_GetURLSpecFromDir(parent, url);
   210             if (NS_FAILED(rv)) return rv;
   211             parentStr.Assign(url);
   212         }
   214         // Directory index will be always encoded in UTF-8 if this is file url
   215         buffer.AppendLiteral("<meta charset=\"UTF-8\">\n");
   217     } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) {
   218         nsAutoCString path;
   219         rv = uri->GetPath(path);
   220         if (NS_FAILED(rv)) return rv;
   222         // a top-level jar directory URL is of the form jar:foo.zip!/
   223         // path will be of the form foo.zip!/, and its last two characters
   224         // will be "!/"
   225         //XXX this won't work correctly when the name of the directory being
   226         //XXX displayed ends with "!", but then again, jar: URIs don't deal
   227         //XXX particularly well with such directories anyway
   228         if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) {
   229             rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
   230             if (NS_FAILED(rv)) return rv;
   231         }
   232     }
   233     else {
   234         // default behavior for other protocols is to assume the channel's
   235         // URL references a directory ending in '/' -- fixup if necessary.
   236         nsAutoCString path;
   237         rv = uri->GetPath(path);
   238         if (NS_FAILED(rv)) return rv;
   239         if (baseUri.Last() != '/') {
   240             baseUri.Append('/');
   241             path.Append('/');
   242             uri->SetPath(path);
   243         }
   244         if (!path.EqualsLiteral("/")) {
   245             rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
   246             if (NS_FAILED(rv)) return rv;
   247         }
   248     }
   250     buffer.AppendLiteral("<style type=\"text/css\">\n"
   251                          ":root {\n"
   252                          "  font-family: sans-serif;\n"
   253                          "}\n"
   254                          "img {\n"
   255                          "  border: 0;\n"
   256                          "}\n"
   257                          "th {\n"
   258                          "  text-align: start;\n"
   259                          "  white-space: nowrap;\n"
   260                          "}\n"
   261                          "th > a {\n"
   262                          "  color: inherit;\n"
   263                          "}\n"
   264                          "table[order] > thead > tr > th {\n"
   265                          "  cursor: pointer;\n"
   266                          "}\n"
   267                          "table[order] > thead > tr > th::after {\n"
   268                          "  display: none;\n"
   269                          "  width: .8em;\n"
   270                          "  -moz-margin-end: -.8em;\n"
   271                          "  text-align: end;\n"
   272                          "}\n"
   273                          "table[order=\"asc\"] > thead > tr > th::after {\n"
   274                          "  content: \"\\2193\"; /* DOWNWARDS ARROW (U+2193) */\n"
   275                          "}\n"
   276                          "table[order=\"desc\"] > thead > tr > th::after {\n"
   277                          "  content: \"\\2191\"; /* UPWARDS ARROW (U+2191) */\n"
   278                          "}\n"
   279                          "table[order][order-by=\"0\"] > thead > tr > th:first-child > a ,\n"
   280                          "table[order][order-by=\"1\"] > thead > tr > th:first-child + th > a ,\n"
   281                          "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th > a {\n"
   282                          "  text-decoration: underline;\n"
   283                          "}\n"
   284                          "table[order][order-by=\"0\"] > thead > tr > th:first-child::after ,\n"
   285                          "table[order][order-by=\"1\"] > thead > tr > th:first-child + th::after ,\n"
   286                          "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th::after {\n"
   287                          "  display: inline-block;\n"
   288                          "}\n"
   289                          "table.remove-hidden > tbody > tr.hidden-object {\n"
   290                          "  display: none;\n"
   291                          "}\n"
   292                          "td {\n"
   293                          "  white-space: nowrap;\n"
   294                          "}\n"
   295                          "table.ellipsis {\n"
   296                          "  width: 100%;\n"
   297                          "  table-layout: fixed;\n"
   298                          "  border-spacing: 0;\n"
   299                          "}\n"
   300                          "table.ellipsis > tbody > tr > td {\n"
   301                          "  padding: 0;\n"
   302                          "  overflow: hidden;\n"
   303                          "  text-overflow: ellipsis;\n"
   304                          "}\n"
   305                          "/* name */\n"
   306                          "/* name */\n"
   307                          "th:first-child {\n"
   308                          "  -moz-padding-end: 2em;\n"
   309                          "}\n"
   310                          "/* size */\n"
   311                          "th:first-child + th {\n"
   312                          "  -moz-padding-end: 1em;\n"
   313                          "}\n"
   314                          "td:first-child + td {\n"
   315                          "  text-align: end;\n"
   316                          "  -moz-padding-end: 1em;\n"
   317                          "}\n"
   318                          "/* date */\n"
   319                          "td:first-child + td + td {\n"
   320                          "  -moz-padding-start: 1em;\n"
   321                          "  -moz-padding-end: .5em;\n"
   322                          "}\n"
   323                          "/* time */\n"
   324                          "td:first-child + td + td + td {\n"
   325                          "  -moz-padding-start: .5em;\n"
   326                          "}\n"
   327                          ".symlink {\n"
   328                          "  font-style: italic;\n"
   329                          "}\n"
   330                          ".dir ,\n"
   331                          ".symlink ,\n"
   332                          ".file {\n"
   333                          "  -moz-margin-start: 20px;\n"
   334                          "}\n"
   335                          ".dir::before ,\n"
   336                          ".file > img {\n"
   337                          "  -moz-margin-end: 4px;\n"
   338                          "  -moz-margin-start: -20px;\n"
   339                          "  max-width: 16px;\n"
   340                          "  max-height: 16px;\n"
   341                          "  vertical-align: middle;\n"
   342                          "}\n"
   343                          ".dir::before {\n"
   344                          "  content: url(resource://gre/res/html/folder.png);\n"
   345                          "}\n"
   346                          "</style>\n"
   347                          "<link rel=\"stylesheet\" media=\"screen, projection\" type=\"text/css\""
   348                          " href=\"chrome://global/skin/dirListing/dirListing.css\">\n"
   349                          "<script type=\"application/javascript\">\n"
   350                          "var gTable, gOrderBy, gTBody, gRows, gUI_showHidden;\n"
   351                          "document.addEventListener(\"DOMContentLoaded\", function() {\n"
   352                          "  gTable = document.getElementsByTagName(\"table\")[0];\n"
   353                          "  gTBody = gTable.tBodies[0];\n"
   354                          "  if (gTBody.rows.length < 2)\n"
   355                          "    return;\n"
   356                          "  gUI_showHidden = document.getElementById(\"UI_showHidden\");\n"
   357                          "  var headCells = gTable.tHead.rows[0].cells,\n"
   358                          "      hiddenObjects = false;\n"
   359                          "  function rowAction(i) {\n"
   360                          "    return function(event) {\n"
   361                          "      event.preventDefault();\n"
   362                          "      orderBy(i);\n"
   363                          "    }\n"
   364                          "  }\n"
   365                          "  for (var i = headCells.length - 1; i >= 0; i--) {\n"
   366                          "    var anchor = document.createElement(\"a\");\n"
   367                          "    anchor.href = \"\";\n"
   368                          "    anchor.appendChild(headCells[i].firstChild);\n"
   369                          "    headCells[i].appendChild(anchor);\n"
   370                          "    headCells[i].addEventListener(\"click\", rowAction(i), true);\n"
   371                          "  }\n"
   372                          "  if (gUI_showHidden) {\n"
   373                          "    gRows = Array.slice(gTBody.rows);\n"
   374                          "    hiddenObjects = gRows.some(function (row) row.className == \"hidden-object\");\n"
   375                          "  }\n"
   376                          "  gTable.setAttribute(\"order\", \"\");\n"
   377                          "  if (hiddenObjects) {\n"
   378                          "    gUI_showHidden.style.display = \"block\";\n"
   379                          "    updateHidden();\n"
   380                          "  }\n"
   381                          "}, \"false\");\n"
   382                          "function compareRows(rowA, rowB) {\n"
   383                          "  var a = rowA.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
   384                          "  var b = rowB.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
   385                          "  var intA = +a;\n"
   386                          "  var intB = +b;\n"
   387                          "  if (a == intA && b == intB) {\n"
   388                          "    a = intA;\n"
   389                          "    b = intB;\n"
   390                          "  } else {\n"
   391                          "    a = a.toLowerCase();\n"
   392                          "    b = b.toLowerCase();\n"
   393                          "  }\n"
   394                          "  if (a < b)\n"
   395                          "    return -1;\n"
   396                          "  if (a > b)\n"
   397                          "    return 1;\n"
   398                          "  return 0;\n"
   399                          "}\n"
   400                          "function orderBy(column) {\n"
   401                          "  if (!gRows)\n"
   402                          "    gRows = Array.slice(gTBody.rows);\n"
   403                          "  var order;\n"
   404                          "  if (gOrderBy == column) {\n"
   405                          "    order = gTable.getAttribute(\"order\") == \"asc\" ? \"desc\" : \"asc\";\n"
   406                          "  } else {\n"
   407                          "    order = \"asc\";\n"
   408                          "    gOrderBy = column;\n"
   409                          "    gTable.setAttribute(\"order-by\", column);\n"
   410                          "    gRows.sort(compareRows);\n"
   411                          "  }\n"
   412                          "  gTable.removeChild(gTBody);\n"
   413                          "  gTable.setAttribute(\"order\", order);\n"
   414                          "  if (order == \"asc\")\n"
   415                          "    for (var i = 0; i < gRows.length; i++)\n"
   416                          "      gTBody.appendChild(gRows[i]);\n"
   417                          "  else\n"
   418                          "    for (var i = gRows.length - 1; i >= 0; i--)\n"
   419                          "      gTBody.appendChild(gRows[i]);\n"
   420                          "  gTable.appendChild(gTBody);\n"
   421                          "}\n"
   422                          "function updateHidden() {\n"
   423                          "  gTable.className = gUI_showHidden.getElementsByTagName(\"input\")[0].checked ?\n"
   424                          "                     \"\" :\n"
   425                          "                     \"remove-hidden\";\n"
   426                          "}\n"
   427                          "</script>\n");
   429     buffer.AppendLiteral("<link rel=\"icon\" type=\"image/png\" href=\"");
   430     nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
   431     if (!innerUri)
   432         return NS_ERROR_UNEXPECTED;
   433     nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri));
   434     //XXX bug 388553: can't use skinnable icons here due to security restrictions
   435     if (fileURL) {
   436         //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png");
   437         buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
   438                              "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
   439                              "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR"
   440                              "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj"
   441                              "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O"
   442                              "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ"
   443                              "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG"
   444                              "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5"
   445                              "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS"
   446                              "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c"
   447                              "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU"
   448                              "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF"
   449                              "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi"
   450                              "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l"
   451                              "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M"
   452                              "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI"
   453                              "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM"
   454                              "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B"
   455                              "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8"
   456                              "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D");
   457     } else {
   458         //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png");
   459         buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
   460                              "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
   461                              "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft"
   462                              "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%"
   463                              "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0"
   464                              "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE"
   465                              "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM"
   466                              "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR"
   467                              "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh"
   468                              "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz"
   469                              "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj"
   470                              "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1"
   471                              "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9"
   472                              "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr"
   473                              "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E"
   474                              "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2"
   475                              "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS"
   476                              "YAAAAABJRU5ErkJggg%3D%3D");
   477     }
   478     buffer.AppendLiteral("\">\n<title>");
   480     // Everything needs to end in a /,
   481     // otherwise we end up linking to file:///foo/dirfile
   483     if (!mTextToSubURI) {
   484         mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
   485         if (NS_FAILED(rv)) return rv;
   486     }
   488     nsXPIDLCString encoding;
   489     rv = uri->GetOriginCharset(encoding);
   490     if (NS_FAILED(rv)) return rv;
   491     if (encoding.IsEmpty()) {
   492       encoding.AssignLiteral("UTF-8");
   493     }
   495     nsXPIDLString unEscapeSpec;
   496     rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(),
   497                                            getter_Copies(unEscapeSpec));
   498     // unescape may fail because
   499     // 1. file URL may be encoded in platform charset for backward compatibility
   500     // 2. query part may not be encoded in UTF-8 (see bug 261929)
   501     // so try the platform's default if this is file url
   502     if (NS_FAILED(rv) && isSchemeFile) {
   503         nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv));
   504         NS_ENSURE_SUCCESS(rv, rv);
   505         nsAutoCString charset;
   506         rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
   507         NS_ENSURE_SUCCESS(rv, rv);
   509         rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(),
   510                                                getter_Copies(unEscapeSpec));
   511     }
   512     if (NS_FAILED(rv)) return rv;
   514     nsXPIDLString htmlEscSpec;
   515     htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
   516                                     unEscapeSpec.Length()));
   518     nsXPIDLString title;
   519     const char16_t* formatTitle[] = {
   520         htmlEscSpec.get()
   521     };
   523     rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"),
   524                                        formatTitle,
   525                                        sizeof(formatTitle)/sizeof(char16_t*),
   526                                        getter_Copies(title));
   527     if (NS_FAILED(rv)) return rv;
   529     // we want to convert string bundle to NCR
   530     // to ensure they're shown in any charsets
   531     AppendNonAsciiToNCR(title, buffer);
   533     buffer.AppendLiteral("</title>\n");    
   535     // If there is a quote character in the baseUri, then
   536     // lets not add a base URL.  The reason for this is that
   537     // if we stick baseUri containing a quote into a quoted
   538     // string, the quote character will prematurely close
   539     // the base href string.  This is a fall-back check;
   540     // that's why it is OK to not use a base rather than
   541     // trying to play nice and escaping the quotes.  See bug
   542     // 358128.
   544     if (baseUri.FindChar('"') == kNotFound)
   545     {
   546         // Great, the baseUri does not contain a char that
   547         // will prematurely close the string.  Go ahead an
   548         // add a base href.
   549         buffer.AppendLiteral("<base href=\"");
   550         nsAdoptingCString htmlEscapedUri(nsEscapeHTML(baseUri.get()));
   551         buffer.Append(htmlEscapedUri);
   552         buffer.AppendLiteral("\" />\n");
   553     }
   554     else
   555     {
   556         NS_ERROR("broken protocol handler didn't escape double-quote.");
   557     }
   559     nsCString direction(NS_LITERAL_CSTRING("ltr"));
   560     nsCOMPtr<nsIXULChromeRegistry> reg =
   561       mozilla::services::GetXULChromeRegistryService();
   562     if (reg) {
   563       bool isRTL = false;
   564       reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL);
   565       if (isRTL) {
   566         direction.AssignLiteral("rtl");
   567       }
   568     }
   570     buffer.AppendLiteral("</head>\n<body dir=\"");
   571     buffer.Append(direction);
   572     buffer.AppendLiteral("\">\n<h1>");
   574     const char16_t* formatHeading[] = {
   575         htmlEscSpec.get()
   576     };
   578     rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"),
   579                                        formatHeading,
   580                                        sizeof(formatHeading)/sizeof(char16_t*),
   581                                        getter_Copies(title));
   582     if (NS_FAILED(rv)) return rv;
   584     AppendNonAsciiToNCR(title, buffer);
   585     buffer.AppendLiteral("</h1>\n");
   587     if (!parentStr.IsEmpty()) {
   588         nsXPIDLString parentText;
   589         rv = mBundle->GetStringFromName(MOZ_UTF16("DirGoUp"),
   590                                         getter_Copies(parentText));
   591         if (NS_FAILED(rv)) return rv;
   593         buffer.AppendLiteral("<p id=\"UI_goUp\"><a class=\"up\" href=\"");
   595         nsAdoptingCString htmlParentStr(nsEscapeHTML(parentStr.get()));
   596         buffer.Append(htmlParentStr);
   597         buffer.AppendLiteral("\">");
   598         AppendNonAsciiToNCR(parentText, buffer);
   599         buffer.AppendLiteral("</a></p>\n");
   600     }
   602     if (isSchemeFile) {
   603         nsXPIDLString showHiddenText;
   604         rv = mBundle->GetStringFromName(MOZ_UTF16("ShowHidden"),
   605                                         getter_Copies(showHiddenText));
   606         if (NS_FAILED(rv)) return rv;
   608         buffer.AppendLiteral("<p id=\"UI_showHidden\" style=\"display:none\"><label><input type=\"checkbox\" checked onchange=\"updateHidden()\">");
   609         AppendNonAsciiToNCR(showHiddenText, buffer);
   610         buffer.AppendLiteral("</label></p>\n");
   611     }
   613     buffer.AppendLiteral("<table>\n");
   615     nsXPIDLString columnText;
   617     buffer.AppendLiteral(" <thead>\n"
   618                          "  <tr>\n"
   619                          "   <th>");
   621     rv = mBundle->GetStringFromName(MOZ_UTF16("DirColName"),
   622                                     getter_Copies(columnText));
   623     if (NS_FAILED(rv)) return rv;
   624     AppendNonAsciiToNCR(columnText, buffer);
   625     buffer.AppendLiteral("</th>\n"
   626                          "   <th>");
   628     rv = mBundle->GetStringFromName(MOZ_UTF16("DirColSize"),
   629                                     getter_Copies(columnText));
   630     if (NS_FAILED(rv)) return rv;
   631     AppendNonAsciiToNCR(columnText, buffer);
   632     buffer.AppendLiteral("</th>\n"
   633                          "   <th colspan=\"2\">");
   635     rv = mBundle->GetStringFromName(MOZ_UTF16("DirColMTime"),
   636                                     getter_Copies(columnText));
   637     if (NS_FAILED(rv)) return rv;
   638     AppendNonAsciiToNCR(columnText, buffer);
   639     buffer.AppendLiteral("</th>\n"
   640                          "  </tr>\n"
   641                          " </thead>\n");
   642     buffer.AppendLiteral(" <tbody>\n");
   644     aBuffer = buffer;
   645     return rv;
   646 }
   648 NS_IMETHODIMP
   649 nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext,
   650                                nsresult aStatus) {
   651     if (NS_SUCCEEDED(aStatus)) {
   652         nsCString buffer;
   653         buffer.AssignLiteral("</tbody></table></body></html>\n");
   655         aStatus = SendToListener(request, aContext, buffer);
   656     }
   658     mParser->OnStopRequest(request, aContext, aStatus);
   659     mParser = 0;
   661     return mListener->OnStopRequest(request, aContext, aStatus);
   662 }
   664 nsresult
   665 nsIndexedToHTML::SendToListener(nsIRequest* aRequest, nsISupports *aContext, const nsACString &aBuffer)
   666 {
   667     nsCOMPtr<nsIInputStream> inputData;
   668     nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputData), aBuffer);
   669     NS_ENSURE_SUCCESS(rv, rv);
   670     return mListener->OnDataAvailable(aRequest, aContext,
   671                                       inputData, 0, aBuffer.Length());
   672 }
   674 NS_IMETHODIMP
   675 nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest,
   676                                  nsISupports *aCtxt,
   677                                  nsIInputStream* aInput,
   678                                  uint64_t aOffset,
   679                                  uint32_t aCount) {
   680     return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount);
   681 }
   683 NS_IMETHODIMP
   684 nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
   685                                   nsISupports *aCtxt,
   686                                   nsIDirIndex *aIndex) {
   687     nsresult rv;
   688     if (!aIndex)
   689         return NS_ERROR_NULL_POINTER;
   691     nsCString pushBuffer;
   692     pushBuffer.AppendLiteral("<tr");
   694     // We don't know the file's character set yet, so retrieve the raw bytes
   695     // which will be decoded by the HTML parser.
   696     nsXPIDLCString loc;
   697     aIndex->GetLocation(getter_Copies(loc));
   699     // Adjust the length in case unescaping shortened the string.
   700     loc.Truncate(nsUnescapeCount(loc.BeginWriting()));
   701     if (loc.First() == PRUnichar('.'))
   702         pushBuffer.AppendLiteral(" class=\"hidden-object\"");
   704     pushBuffer.AppendLiteral(">\n <td sortable-data=\"");
   706     // The sort key is the name of the item, prepended by either 0, 1 or 2
   707     // in order to group items.
   708     uint32_t type;
   709     aIndex->GetType(&type);
   710     switch (type) {
   711         case nsIDirIndex::TYPE_SYMLINK:
   712             pushBuffer.Append('0');
   713             break;
   714         case nsIDirIndex::TYPE_DIRECTORY:
   715             pushBuffer.Append('1');
   716             break;
   717         default:
   718             pushBuffer.Append('2');
   719             break;
   720     }
   721     nsAdoptingCString escaped(nsEscapeHTML(loc));
   722     pushBuffer.Append(escaped);
   724     pushBuffer.AppendLiteral("\"><table class=\"ellipsis\"><tbody><tr><td><a class=\"");
   725     switch (type) {
   726         case nsIDirIndex::TYPE_DIRECTORY:
   727             pushBuffer.AppendLiteral("dir");
   728             break;
   729         case nsIDirIndex::TYPE_SYMLINK:
   730             pushBuffer.AppendLiteral("symlink");
   731             break;
   732         default:
   733             pushBuffer.AppendLiteral("file");
   734             break;
   735     }
   737     pushBuffer.AppendLiteral("\" href=\"");
   739     // need to escape links
   740     nsAutoCString locEscaped;
   742     // Adding trailing slash helps to recognize whether the URL points to a file
   743     // or a directory (bug #214405).
   744     if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) {
   745         loc.Append('/');
   746     }
   748     // now minimally re-escape the location...
   749     uint32_t escFlags;
   750     // for some protocols, we expect the location to be absolute.
   751     // if so, and if the location indeed appears to be a valid URI, then go
   752     // ahead and treat it like one.
   753     if (mExpectAbsLoc &&
   754         NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) {
   755         // escape as absolute 
   756         escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal;
   757     }
   758     else {
   759         // escape as relative
   760         // esc_Directory is needed because directories have a trailing slash.
   761         // Without it, the trailing '/' will be escaped, and links from within
   762         // that directory will be incorrect
   763         escFlags = esc_Forced | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
   764     }
   765     NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped);
   766     // esc_Directory does not escape the semicolons, so if a filename
   767     // contains semicolons we need to manually escape them.
   768     // This replacement should be removed in bug #473280
   769     locEscaped.ReplaceSubstring(";", "%3b");
   770     nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get()));
   771     pushBuffer.Append(htmlEscapedURL);
   773     pushBuffer.AppendLiteral("\">");
   775     if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) {
   776         pushBuffer.AppendLiteral("<img src=\"moz-icon://");
   777         int32_t lastDot = locEscaped.RFindChar('.');
   778         if (lastDot != kNotFound) {
   779             locEscaped.Cut(0, lastDot);
   780             nsAdoptingCString htmlFileExt(nsEscapeHTML(locEscaped.get()));
   781             pushBuffer.Append(htmlFileExt);
   782         } else {
   783             pushBuffer.AppendLiteral("unknown");
   784         }
   785         pushBuffer.AppendLiteral("?size=16\" alt=\"");
   787         nsXPIDLString altText;
   788         rv = mBundle->GetStringFromName(MOZ_UTF16("DirFileLabel"),
   789                                         getter_Copies(altText));
   790         if (NS_FAILED(rv)) return rv;
   791         AppendNonAsciiToNCR(altText, pushBuffer);
   792         pushBuffer.AppendLiteral("\">");
   793     }
   795     pushBuffer.Append(escaped);
   796     pushBuffer.AppendLiteral("</a></td></tr></tbody></table></td>\n <td");
   798     if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) {
   799         pushBuffer.AppendLiteral(">");
   800     } else {
   801         int64_t size;
   802         aIndex->GetSize(&size);
   804         if (uint64_t(size) != UINT64_MAX) {
   805             pushBuffer.AppendLiteral(" sortable-data=\"");
   806             pushBuffer.AppendInt(size);
   807             pushBuffer.AppendLiteral("\">");
   808             nsAutoCString sizeString;
   809             FormatSizeString(size, sizeString);
   810             pushBuffer.Append(sizeString);
   811         } else {
   812             pushBuffer.AppendLiteral(">");
   813         }
   814     }
   815     pushBuffer.AppendLiteral("</td>\n <td");
   817     PRTime t;
   818     aIndex->GetLastModified(&t);
   820     if (t == -1) {
   821         pushBuffer.AppendLiteral("></td>\n <td>");
   822     } else {
   823         pushBuffer.AppendLiteral(" sortable-data=\"");
   824         pushBuffer.AppendInt(static_cast<int64_t>(t));
   825         pushBuffer.AppendLiteral("\">");
   826         nsAutoString formatted;
   827         mDateTime->FormatPRTime(nullptr,
   828                                 kDateFormatShort,
   829                                 kTimeFormatNone,
   830                                 t,
   831                                 formatted);
   832         AppendNonAsciiToNCR(formatted, pushBuffer);
   833         pushBuffer.AppendLiteral("</td>\n <td>");
   834         mDateTime->FormatPRTime(nullptr,
   835                                 kDateFormatNone,
   836                                 kTimeFormatSeconds,
   837                                 t,
   838                                 formatted);
   839         // use NCR to show date in any doc charset
   840         AppendNonAsciiToNCR(formatted, pushBuffer);
   841     }
   843     pushBuffer.AppendLiteral("</td>\n</tr>");
   845     return SendToListener(aRequest, aCtxt, pushBuffer);
   846 }
   848 NS_IMETHODIMP
   849 nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest,
   850                                         nsISupports *aCtxt,
   851                                         const nsAString& aInfo) {
   852     nsAutoCString pushBuffer;
   853     nsAdoptingString escaped(nsEscapeHTML2(PromiseFlatString(aInfo).get()));
   854     if (!escaped)
   855         return NS_ERROR_OUT_OF_MEMORY;
   856     pushBuffer.AppendLiteral("<tr>\n <td>");
   857     // escaped is provided in Unicode, so write hex NCRs as necessary
   858     // to prevent the HTML parser from applying a character set.
   859     AppendNonAsciiToNCR(escaped, pushBuffer);
   860     pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n");
   862     return SendToListener(aRequest, aCtxt, pushBuffer);
   863 }
   865 void nsIndexedToHTML::FormatSizeString(int64_t inSize, nsCString& outSizeString)
   866 {
   867     outSizeString.Truncate();
   868     if (inSize > int64_t(0)) {
   869         // round up to the nearest Kilobyte
   870         int64_t  upperSize = (inSize + int64_t(1023)) / int64_t(1024);
   871         outSizeString.AppendInt(upperSize);
   872         outSizeString.AppendLiteral(" KB");
   873     }
   874 }
   876 nsIndexedToHTML::nsIndexedToHTML() {
   877 }
   879 nsIndexedToHTML::~nsIndexedToHTML() {
   880 }

mercurial