1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,880 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsIndexedToHTML.h" 1.10 +#include "mozilla/dom/EncodingUtils.h" 1.11 +#include "nsNetUtil.h" 1.12 +#include "netCore.h" 1.13 +#include "nsStringStream.h" 1.14 +#include "nsIFileURL.h" 1.15 +#include "nsEscape.h" 1.16 +#include "nsIDirIndex.h" 1.17 +#include "nsDateTimeFormatCID.h" 1.18 +#include "nsURLHelper.h" 1.19 +#include "nsIPlatformCharset.h" 1.20 +#include "nsIPrefService.h" 1.21 +#include "nsIPrefBranch.h" 1.22 +#include "nsIPrefLocalizedString.h" 1.23 +#include "nsIChromeRegistry.h" 1.24 +#include "nsICharsetConverterManager.h" 1.25 +#include "nsIDateTimeFormat.h" 1.26 +#include "nsIStringBundle.h" 1.27 +#include "nsITextToSubURI.h" 1.28 +#include "nsXPIDLString.h" 1.29 +#include <algorithm> 1.30 + 1.31 +NS_IMPL_ISUPPORTS(nsIndexedToHTML, 1.32 + nsIDirIndexListener, 1.33 + nsIStreamConverter, 1.34 + nsIRequestObserver, 1.35 + nsIStreamListener) 1.36 + 1.37 +static void AppendNonAsciiToNCR(const nsAString& in, nsCString& out) 1.38 +{ 1.39 + nsAString::const_iterator start, end; 1.40 + 1.41 + in.BeginReading(start); 1.42 + in.EndReading(end); 1.43 + 1.44 + while (start != end) { 1.45 + if (*start < 128) { 1.46 + out.Append(*start++); 1.47 + } else { 1.48 + out.AppendLiteral("&#x"); 1.49 + out.AppendInt(*start++, 16); 1.50 + out.Append(';'); 1.51 + } 1.52 + } 1.53 +} 1.54 + 1.55 +nsresult 1.56 +nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { 1.57 + nsresult rv; 1.58 + if (aOuter) 1.59 + return NS_ERROR_NO_AGGREGATION; 1.60 + 1.61 + nsIndexedToHTML* _s = new nsIndexedToHTML(); 1.62 + if (_s == nullptr) 1.63 + return NS_ERROR_OUT_OF_MEMORY; 1.64 + 1.65 + rv = _s->QueryInterface(aIID, aResult); 1.66 + return rv; 1.67 +} 1.68 + 1.69 +nsresult 1.70 +nsIndexedToHTML::Init(nsIStreamListener* aListener) { 1.71 + nsresult rv = NS_OK; 1.72 + 1.73 + mListener = aListener; 1.74 + 1.75 + mDateTime = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); 1.76 + if (NS_FAILED(rv)) 1.77 + return rv; 1.78 + 1.79 + nsCOMPtr<nsIStringBundleService> sbs = 1.80 + do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); 1.81 + if (NS_FAILED(rv)) return rv; 1.82 + rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle)); 1.83 + 1.84 + mExpectAbsLoc = false; 1.85 + 1.86 + return rv; 1.87 +} 1.88 + 1.89 +NS_IMETHODIMP 1.90 +nsIndexedToHTML::Convert(nsIInputStream* aFromStream, 1.91 + const char* aFromType, 1.92 + const char* aToType, 1.93 + nsISupports* aCtxt, 1.94 + nsIInputStream** res) { 1.95 + return NS_ERROR_NOT_IMPLEMENTED; 1.96 +} 1.97 + 1.98 +NS_IMETHODIMP 1.99 +nsIndexedToHTML::AsyncConvertData(const char *aFromType, 1.100 + const char *aToType, 1.101 + nsIStreamListener *aListener, 1.102 + nsISupports *aCtxt) { 1.103 + return Init(aListener); 1.104 +} 1.105 + 1.106 +NS_IMETHODIMP 1.107 +nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) { 1.108 + nsCString buffer; 1.109 + nsresult rv = DoOnStartRequest(request, aContext, buffer); 1.110 + if (NS_FAILED(rv)) { 1.111 + request->Cancel(rv); 1.112 + } 1.113 + 1.114 + rv = mListener->OnStartRequest(request, aContext); 1.115 + if (NS_FAILED(rv)) return rv; 1.116 + 1.117 + // The request may have been canceled, and if that happens, we want to 1.118 + // suppress calls to OnDataAvailable. 1.119 + request->GetStatus(&rv); 1.120 + if (NS_FAILED(rv)) return rv; 1.121 + 1.122 + // Push our buffer to the listener. 1.123 + 1.124 + rv = SendToListener(request, aContext, buffer); 1.125 + return rv; 1.126 +} 1.127 + 1.128 +nsresult 1.129 +nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext, 1.130 + nsCString& aBuffer) { 1.131 + nsresult rv; 1.132 + 1.133 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.134 + nsCOMPtr<nsIURI> uri; 1.135 + rv = channel->GetURI(getter_AddRefs(uri)); 1.136 + if (NS_FAILED(rv)) return rv; 1.137 + 1.138 + channel->SetContentType(NS_LITERAL_CSTRING("text/html")); 1.139 + 1.140 + mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv); 1.141 + if (NS_FAILED(rv)) return rv; 1.142 + 1.143 + rv = mParser->SetListener(this); 1.144 + if (NS_FAILED(rv)) return rv; 1.145 + 1.146 + rv = mParser->OnStartRequest(request, aContext); 1.147 + if (NS_FAILED(rv)) return rv; 1.148 + 1.149 + nsAutoCString baseUri, titleUri; 1.150 + rv = uri->GetAsciiSpec(baseUri); 1.151 + if (NS_FAILED(rv)) return rv; 1.152 + titleUri = baseUri; 1.153 + 1.154 + nsCString parentStr; 1.155 + 1.156 + nsCString buffer; 1.157 + buffer.AppendLiteral("<!DOCTYPE html>\n<html>\n<head>\n"); 1.158 + 1.159 + // XXX - should be using the 300: line from the parser. 1.160 + // We can't guarantee that that comes before any entry, so we'd have to 1.161 + // buffer, and do other painful stuff. 1.162 + // I'll deal with this when I make the changes to handle welcome messages 1.163 + // The .. stuff should also come from the lower level protocols, but that 1.164 + // would muck up the XUL display 1.165 + // - bbaetz 1.166 + 1.167 + bool isScheme = false; 1.168 + bool isSchemeFile = false; 1.169 + if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) { 1.170 + 1.171 + // strip out the password here, so it doesn't show in the page title 1.172 + // This is done by the 300: line generation in ftp, but we don't use 1.173 + // that - see above 1.174 + 1.175 + nsAutoCString pw; 1.176 + rv = uri->GetPassword(pw); 1.177 + if (NS_FAILED(rv)) return rv; 1.178 + if (!pw.IsEmpty()) { 1.179 + nsCOMPtr<nsIURI> newUri; 1.180 + rv = uri->Clone(getter_AddRefs(newUri)); 1.181 + if (NS_FAILED(rv)) return rv; 1.182 + rv = newUri->SetPassword(EmptyCString()); 1.183 + if (NS_FAILED(rv)) return rv; 1.184 + rv = newUri->GetAsciiSpec(titleUri); 1.185 + if (NS_FAILED(rv)) return rv; 1.186 + } 1.187 + 1.188 + nsAutoCString path; 1.189 + rv = uri->GetPath(path); 1.190 + if (NS_FAILED(rv)) return rv; 1.191 + 1.192 + if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) { 1.193 + rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr); 1.194 + if (NS_FAILED(rv)) return rv; 1.195 + } 1.196 + } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) { 1.197 + nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri); 1.198 + nsCOMPtr<nsIFile> file; 1.199 + rv = fileUrl->GetFile(getter_AddRefs(file)); 1.200 + if (NS_FAILED(rv)) return rv; 1.201 + file->SetFollowLinks(true); 1.202 + 1.203 + nsAutoCString url; 1.204 + rv = net_GetURLSpecFromFile(file, url); 1.205 + if (NS_FAILED(rv)) return rv; 1.206 + baseUri.Assign(url); 1.207 + 1.208 + nsCOMPtr<nsIFile> parent; 1.209 + rv = file->GetParent(getter_AddRefs(parent)); 1.210 + 1.211 + if (parent && NS_SUCCEEDED(rv)) { 1.212 + net_GetURLSpecFromDir(parent, url); 1.213 + if (NS_FAILED(rv)) return rv; 1.214 + parentStr.Assign(url); 1.215 + } 1.216 + 1.217 + // Directory index will be always encoded in UTF-8 if this is file url 1.218 + buffer.AppendLiteral("<meta charset=\"UTF-8\">\n"); 1.219 + 1.220 + } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) { 1.221 + nsAutoCString path; 1.222 + rv = uri->GetPath(path); 1.223 + if (NS_FAILED(rv)) return rv; 1.224 + 1.225 + // a top-level jar directory URL is of the form jar:foo.zip!/ 1.226 + // path will be of the form foo.zip!/, and its last two characters 1.227 + // will be "!/" 1.228 + //XXX this won't work correctly when the name of the directory being 1.229 + //XXX displayed ends with "!", but then again, jar: URIs don't deal 1.230 + //XXX particularly well with such directories anyway 1.231 + if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) { 1.232 + rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr); 1.233 + if (NS_FAILED(rv)) return rv; 1.234 + } 1.235 + } 1.236 + else { 1.237 + // default behavior for other protocols is to assume the channel's 1.238 + // URL references a directory ending in '/' -- fixup if necessary. 1.239 + nsAutoCString path; 1.240 + rv = uri->GetPath(path); 1.241 + if (NS_FAILED(rv)) return rv; 1.242 + if (baseUri.Last() != '/') { 1.243 + baseUri.Append('/'); 1.244 + path.Append('/'); 1.245 + uri->SetPath(path); 1.246 + } 1.247 + if (!path.EqualsLiteral("/")) { 1.248 + rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr); 1.249 + if (NS_FAILED(rv)) return rv; 1.250 + } 1.251 + } 1.252 + 1.253 + buffer.AppendLiteral("<style type=\"text/css\">\n" 1.254 + ":root {\n" 1.255 + " font-family: sans-serif;\n" 1.256 + "}\n" 1.257 + "img {\n" 1.258 + " border: 0;\n" 1.259 + "}\n" 1.260 + "th {\n" 1.261 + " text-align: start;\n" 1.262 + " white-space: nowrap;\n" 1.263 + "}\n" 1.264 + "th > a {\n" 1.265 + " color: inherit;\n" 1.266 + "}\n" 1.267 + "table[order] > thead > tr > th {\n" 1.268 + " cursor: pointer;\n" 1.269 + "}\n" 1.270 + "table[order] > thead > tr > th::after {\n" 1.271 + " display: none;\n" 1.272 + " width: .8em;\n" 1.273 + " -moz-margin-end: -.8em;\n" 1.274 + " text-align: end;\n" 1.275 + "}\n" 1.276 + "table[order=\"asc\"] > thead > tr > th::after {\n" 1.277 + " content: \"\\2193\"; /* DOWNWARDS ARROW (U+2193) */\n" 1.278 + "}\n" 1.279 + "table[order=\"desc\"] > thead > tr > th::after {\n" 1.280 + " content: \"\\2191\"; /* UPWARDS ARROW (U+2191) */\n" 1.281 + "}\n" 1.282 + "table[order][order-by=\"0\"] > thead > tr > th:first-child > a ,\n" 1.283 + "table[order][order-by=\"1\"] > thead > tr > th:first-child + th > a ,\n" 1.284 + "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th > a {\n" 1.285 + " text-decoration: underline;\n" 1.286 + "}\n" 1.287 + "table[order][order-by=\"0\"] > thead > tr > th:first-child::after ,\n" 1.288 + "table[order][order-by=\"1\"] > thead > tr > th:first-child + th::after ,\n" 1.289 + "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th::after {\n" 1.290 + " display: inline-block;\n" 1.291 + "}\n" 1.292 + "table.remove-hidden > tbody > tr.hidden-object {\n" 1.293 + " display: none;\n" 1.294 + "}\n" 1.295 + "td {\n" 1.296 + " white-space: nowrap;\n" 1.297 + "}\n" 1.298 + "table.ellipsis {\n" 1.299 + " width: 100%;\n" 1.300 + " table-layout: fixed;\n" 1.301 + " border-spacing: 0;\n" 1.302 + "}\n" 1.303 + "table.ellipsis > tbody > tr > td {\n" 1.304 + " padding: 0;\n" 1.305 + " overflow: hidden;\n" 1.306 + " text-overflow: ellipsis;\n" 1.307 + "}\n" 1.308 + "/* name */\n" 1.309 + "/* name */\n" 1.310 + "th:first-child {\n" 1.311 + " -moz-padding-end: 2em;\n" 1.312 + "}\n" 1.313 + "/* size */\n" 1.314 + "th:first-child + th {\n" 1.315 + " -moz-padding-end: 1em;\n" 1.316 + "}\n" 1.317 + "td:first-child + td {\n" 1.318 + " text-align: end;\n" 1.319 + " -moz-padding-end: 1em;\n" 1.320 + "}\n" 1.321 + "/* date */\n" 1.322 + "td:first-child + td + td {\n" 1.323 + " -moz-padding-start: 1em;\n" 1.324 + " -moz-padding-end: .5em;\n" 1.325 + "}\n" 1.326 + "/* time */\n" 1.327 + "td:first-child + td + td + td {\n" 1.328 + " -moz-padding-start: .5em;\n" 1.329 + "}\n" 1.330 + ".symlink {\n" 1.331 + " font-style: italic;\n" 1.332 + "}\n" 1.333 + ".dir ,\n" 1.334 + ".symlink ,\n" 1.335 + ".file {\n" 1.336 + " -moz-margin-start: 20px;\n" 1.337 + "}\n" 1.338 + ".dir::before ,\n" 1.339 + ".file > img {\n" 1.340 + " -moz-margin-end: 4px;\n" 1.341 + " -moz-margin-start: -20px;\n" 1.342 + " max-width: 16px;\n" 1.343 + " max-height: 16px;\n" 1.344 + " vertical-align: middle;\n" 1.345 + "}\n" 1.346 + ".dir::before {\n" 1.347 + " content: url(resource://gre/res/html/folder.png);\n" 1.348 + "}\n" 1.349 + "</style>\n" 1.350 + "<link rel=\"stylesheet\" media=\"screen, projection\" type=\"text/css\"" 1.351 + " href=\"chrome://global/skin/dirListing/dirListing.css\">\n" 1.352 + "<script type=\"application/javascript\">\n" 1.353 + "var gTable, gOrderBy, gTBody, gRows, gUI_showHidden;\n" 1.354 + "document.addEventListener(\"DOMContentLoaded\", function() {\n" 1.355 + " gTable = document.getElementsByTagName(\"table\")[0];\n" 1.356 + " gTBody = gTable.tBodies[0];\n" 1.357 + " if (gTBody.rows.length < 2)\n" 1.358 + " return;\n" 1.359 + " gUI_showHidden = document.getElementById(\"UI_showHidden\");\n" 1.360 + " var headCells = gTable.tHead.rows[0].cells,\n" 1.361 + " hiddenObjects = false;\n" 1.362 + " function rowAction(i) {\n" 1.363 + " return function(event) {\n" 1.364 + " event.preventDefault();\n" 1.365 + " orderBy(i);\n" 1.366 + " }\n" 1.367 + " }\n" 1.368 + " for (var i = headCells.length - 1; i >= 0; i--) {\n" 1.369 + " var anchor = document.createElement(\"a\");\n" 1.370 + " anchor.href = \"\";\n" 1.371 + " anchor.appendChild(headCells[i].firstChild);\n" 1.372 + " headCells[i].appendChild(anchor);\n" 1.373 + " headCells[i].addEventListener(\"click\", rowAction(i), true);\n" 1.374 + " }\n" 1.375 + " if (gUI_showHidden) {\n" 1.376 + " gRows = Array.slice(gTBody.rows);\n" 1.377 + " hiddenObjects = gRows.some(function (row) row.className == \"hidden-object\");\n" 1.378 + " }\n" 1.379 + " gTable.setAttribute(\"order\", \"\");\n" 1.380 + " if (hiddenObjects) {\n" 1.381 + " gUI_showHidden.style.display = \"block\";\n" 1.382 + " updateHidden();\n" 1.383 + " }\n" 1.384 + "}, \"false\");\n" 1.385 + "function compareRows(rowA, rowB) {\n" 1.386 + " var a = rowA.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n" 1.387 + " var b = rowB.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n" 1.388 + " var intA = +a;\n" 1.389 + " var intB = +b;\n" 1.390 + " if (a == intA && b == intB) {\n" 1.391 + " a = intA;\n" 1.392 + " b = intB;\n" 1.393 + " } else {\n" 1.394 + " a = a.toLowerCase();\n" 1.395 + " b = b.toLowerCase();\n" 1.396 + " }\n" 1.397 + " if (a < b)\n" 1.398 + " return -1;\n" 1.399 + " if (a > b)\n" 1.400 + " return 1;\n" 1.401 + " return 0;\n" 1.402 + "}\n" 1.403 + "function orderBy(column) {\n" 1.404 + " if (!gRows)\n" 1.405 + " gRows = Array.slice(gTBody.rows);\n" 1.406 + " var order;\n" 1.407 + " if (gOrderBy == column) {\n" 1.408 + " order = gTable.getAttribute(\"order\") == \"asc\" ? \"desc\" : \"asc\";\n" 1.409 + " } else {\n" 1.410 + " order = \"asc\";\n" 1.411 + " gOrderBy = column;\n" 1.412 + " gTable.setAttribute(\"order-by\", column);\n" 1.413 + " gRows.sort(compareRows);\n" 1.414 + " }\n" 1.415 + " gTable.removeChild(gTBody);\n" 1.416 + " gTable.setAttribute(\"order\", order);\n" 1.417 + " if (order == \"asc\")\n" 1.418 + " for (var i = 0; i < gRows.length; i++)\n" 1.419 + " gTBody.appendChild(gRows[i]);\n" 1.420 + " else\n" 1.421 + " for (var i = gRows.length - 1; i >= 0; i--)\n" 1.422 + " gTBody.appendChild(gRows[i]);\n" 1.423 + " gTable.appendChild(gTBody);\n" 1.424 + "}\n" 1.425 + "function updateHidden() {\n" 1.426 + " gTable.className = gUI_showHidden.getElementsByTagName(\"input\")[0].checked ?\n" 1.427 + " \"\" :\n" 1.428 + " \"remove-hidden\";\n" 1.429 + "}\n" 1.430 + "</script>\n"); 1.431 + 1.432 + buffer.AppendLiteral("<link rel=\"icon\" type=\"image/png\" href=\""); 1.433 + nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri); 1.434 + if (!innerUri) 1.435 + return NS_ERROR_UNEXPECTED; 1.436 + nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri)); 1.437 + //XXX bug 388553: can't use skinnable icons here due to security restrictions 1.438 + if (fileURL) { 1.439 + //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png"); 1.440 + buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB" 1.441 + "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i" 1.442 + "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR" 1.443 + "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj" 1.444 + "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O" 1.445 + "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ" 1.446 + "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG" 1.447 + "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5" 1.448 + "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS" 1.449 + "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c" 1.450 + "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU" 1.451 + "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF" 1.452 + "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi" 1.453 + "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l" 1.454 + "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M" 1.455 + "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI" 1.456 + "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM" 1.457 + "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B" 1.458 + "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8" 1.459 + "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D"); 1.460 + } else { 1.461 + //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png"); 1.462 + buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB" 1.463 + "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i" 1.464 + "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft" 1.465 + "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%" 1.466 + "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0" 1.467 + "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE" 1.468 + "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM" 1.469 + "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR" 1.470 + "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh" 1.471 + "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz" 1.472 + "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj" 1.473 + "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1" 1.474 + "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9" 1.475 + "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr" 1.476 + "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E" 1.477 + "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2" 1.478 + "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS" 1.479 + "YAAAAABJRU5ErkJggg%3D%3D"); 1.480 + } 1.481 + buffer.AppendLiteral("\">\n<title>"); 1.482 + 1.483 + // Everything needs to end in a /, 1.484 + // otherwise we end up linking to file:///foo/dirfile 1.485 + 1.486 + if (!mTextToSubURI) { 1.487 + mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); 1.488 + if (NS_FAILED(rv)) return rv; 1.489 + } 1.490 + 1.491 + nsXPIDLCString encoding; 1.492 + rv = uri->GetOriginCharset(encoding); 1.493 + if (NS_FAILED(rv)) return rv; 1.494 + if (encoding.IsEmpty()) { 1.495 + encoding.AssignLiteral("UTF-8"); 1.496 + } 1.497 + 1.498 + nsXPIDLString unEscapeSpec; 1.499 + rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(), 1.500 + getter_Copies(unEscapeSpec)); 1.501 + // unescape may fail because 1.502 + // 1. file URL may be encoded in platform charset for backward compatibility 1.503 + // 2. query part may not be encoded in UTF-8 (see bug 261929) 1.504 + // so try the platform's default if this is file url 1.505 + if (NS_FAILED(rv) && isSchemeFile) { 1.506 + nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv)); 1.507 + NS_ENSURE_SUCCESS(rv, rv); 1.508 + nsAutoCString charset; 1.509 + rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset); 1.510 + NS_ENSURE_SUCCESS(rv, rv); 1.511 + 1.512 + rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(), 1.513 + getter_Copies(unEscapeSpec)); 1.514 + } 1.515 + if (NS_FAILED(rv)) return rv; 1.516 + 1.517 + nsXPIDLString htmlEscSpec; 1.518 + htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(), 1.519 + unEscapeSpec.Length())); 1.520 + 1.521 + nsXPIDLString title; 1.522 + const char16_t* formatTitle[] = { 1.523 + htmlEscSpec.get() 1.524 + }; 1.525 + 1.526 + rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"), 1.527 + formatTitle, 1.528 + sizeof(formatTitle)/sizeof(char16_t*), 1.529 + getter_Copies(title)); 1.530 + if (NS_FAILED(rv)) return rv; 1.531 + 1.532 + // we want to convert string bundle to NCR 1.533 + // to ensure they're shown in any charsets 1.534 + AppendNonAsciiToNCR(title, buffer); 1.535 + 1.536 + buffer.AppendLiteral("</title>\n"); 1.537 + 1.538 + // If there is a quote character in the baseUri, then 1.539 + // lets not add a base URL. The reason for this is that 1.540 + // if we stick baseUri containing a quote into a quoted 1.541 + // string, the quote character will prematurely close 1.542 + // the base href string. This is a fall-back check; 1.543 + // that's why it is OK to not use a base rather than 1.544 + // trying to play nice and escaping the quotes. See bug 1.545 + // 358128. 1.546 + 1.547 + if (baseUri.FindChar('"') == kNotFound) 1.548 + { 1.549 + // Great, the baseUri does not contain a char that 1.550 + // will prematurely close the string. Go ahead an 1.551 + // add a base href. 1.552 + buffer.AppendLiteral("<base href=\""); 1.553 + nsAdoptingCString htmlEscapedUri(nsEscapeHTML(baseUri.get())); 1.554 + buffer.Append(htmlEscapedUri); 1.555 + buffer.AppendLiteral("\" />\n"); 1.556 + } 1.557 + else 1.558 + { 1.559 + NS_ERROR("broken protocol handler didn't escape double-quote."); 1.560 + } 1.561 + 1.562 + nsCString direction(NS_LITERAL_CSTRING("ltr")); 1.563 + nsCOMPtr<nsIXULChromeRegistry> reg = 1.564 + mozilla::services::GetXULChromeRegistryService(); 1.565 + if (reg) { 1.566 + bool isRTL = false; 1.567 + reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL); 1.568 + if (isRTL) { 1.569 + direction.AssignLiteral("rtl"); 1.570 + } 1.571 + } 1.572 + 1.573 + buffer.AppendLiteral("</head>\n<body dir=\""); 1.574 + buffer.Append(direction); 1.575 + buffer.AppendLiteral("\">\n<h1>"); 1.576 + 1.577 + const char16_t* formatHeading[] = { 1.578 + htmlEscSpec.get() 1.579 + }; 1.580 + 1.581 + rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"), 1.582 + formatHeading, 1.583 + sizeof(formatHeading)/sizeof(char16_t*), 1.584 + getter_Copies(title)); 1.585 + if (NS_FAILED(rv)) return rv; 1.586 + 1.587 + AppendNonAsciiToNCR(title, buffer); 1.588 + buffer.AppendLiteral("</h1>\n"); 1.589 + 1.590 + if (!parentStr.IsEmpty()) { 1.591 + nsXPIDLString parentText; 1.592 + rv = mBundle->GetStringFromName(MOZ_UTF16("DirGoUp"), 1.593 + getter_Copies(parentText)); 1.594 + if (NS_FAILED(rv)) return rv; 1.595 + 1.596 + buffer.AppendLiteral("<p id=\"UI_goUp\"><a class=\"up\" href=\""); 1.597 + 1.598 + nsAdoptingCString htmlParentStr(nsEscapeHTML(parentStr.get())); 1.599 + buffer.Append(htmlParentStr); 1.600 + buffer.AppendLiteral("\">"); 1.601 + AppendNonAsciiToNCR(parentText, buffer); 1.602 + buffer.AppendLiteral("</a></p>\n"); 1.603 + } 1.604 + 1.605 + if (isSchemeFile) { 1.606 + nsXPIDLString showHiddenText; 1.607 + rv = mBundle->GetStringFromName(MOZ_UTF16("ShowHidden"), 1.608 + getter_Copies(showHiddenText)); 1.609 + if (NS_FAILED(rv)) return rv; 1.610 + 1.611 + buffer.AppendLiteral("<p id=\"UI_showHidden\" style=\"display:none\"><label><input type=\"checkbox\" checked onchange=\"updateHidden()\">"); 1.612 + AppendNonAsciiToNCR(showHiddenText, buffer); 1.613 + buffer.AppendLiteral("</label></p>\n"); 1.614 + } 1.615 + 1.616 + buffer.AppendLiteral("<table>\n"); 1.617 + 1.618 + nsXPIDLString columnText; 1.619 + 1.620 + buffer.AppendLiteral(" <thead>\n" 1.621 + " <tr>\n" 1.622 + " <th>"); 1.623 + 1.624 + rv = mBundle->GetStringFromName(MOZ_UTF16("DirColName"), 1.625 + getter_Copies(columnText)); 1.626 + if (NS_FAILED(rv)) return rv; 1.627 + AppendNonAsciiToNCR(columnText, buffer); 1.628 + buffer.AppendLiteral("</th>\n" 1.629 + " <th>"); 1.630 + 1.631 + rv = mBundle->GetStringFromName(MOZ_UTF16("DirColSize"), 1.632 + getter_Copies(columnText)); 1.633 + if (NS_FAILED(rv)) return rv; 1.634 + AppendNonAsciiToNCR(columnText, buffer); 1.635 + buffer.AppendLiteral("</th>\n" 1.636 + " <th colspan=\"2\">"); 1.637 + 1.638 + rv = mBundle->GetStringFromName(MOZ_UTF16("DirColMTime"), 1.639 + getter_Copies(columnText)); 1.640 + if (NS_FAILED(rv)) return rv; 1.641 + AppendNonAsciiToNCR(columnText, buffer); 1.642 + buffer.AppendLiteral("</th>\n" 1.643 + " </tr>\n" 1.644 + " </thead>\n"); 1.645 + buffer.AppendLiteral(" <tbody>\n"); 1.646 + 1.647 + aBuffer = buffer; 1.648 + return rv; 1.649 +} 1.650 + 1.651 +NS_IMETHODIMP 1.652 +nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext, 1.653 + nsresult aStatus) { 1.654 + if (NS_SUCCEEDED(aStatus)) { 1.655 + nsCString buffer; 1.656 + buffer.AssignLiteral("</tbody></table></body></html>\n"); 1.657 + 1.658 + aStatus = SendToListener(request, aContext, buffer); 1.659 + } 1.660 + 1.661 + mParser->OnStopRequest(request, aContext, aStatus); 1.662 + mParser = 0; 1.663 + 1.664 + return mListener->OnStopRequest(request, aContext, aStatus); 1.665 +} 1.666 + 1.667 +nsresult 1.668 +nsIndexedToHTML::SendToListener(nsIRequest* aRequest, nsISupports *aContext, const nsACString &aBuffer) 1.669 +{ 1.670 + nsCOMPtr<nsIInputStream> inputData; 1.671 + nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputData), aBuffer); 1.672 + NS_ENSURE_SUCCESS(rv, rv); 1.673 + return mListener->OnDataAvailable(aRequest, aContext, 1.674 + inputData, 0, aBuffer.Length()); 1.675 +} 1.676 + 1.677 +NS_IMETHODIMP 1.678 +nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest, 1.679 + nsISupports *aCtxt, 1.680 + nsIInputStream* aInput, 1.681 + uint64_t aOffset, 1.682 + uint32_t aCount) { 1.683 + return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount); 1.684 +} 1.685 + 1.686 +NS_IMETHODIMP 1.687 +nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, 1.688 + nsISupports *aCtxt, 1.689 + nsIDirIndex *aIndex) { 1.690 + nsresult rv; 1.691 + if (!aIndex) 1.692 + return NS_ERROR_NULL_POINTER; 1.693 + 1.694 + nsCString pushBuffer; 1.695 + pushBuffer.AppendLiteral("<tr"); 1.696 + 1.697 + // We don't know the file's character set yet, so retrieve the raw bytes 1.698 + // which will be decoded by the HTML parser. 1.699 + nsXPIDLCString loc; 1.700 + aIndex->GetLocation(getter_Copies(loc)); 1.701 + 1.702 + // Adjust the length in case unescaping shortened the string. 1.703 + loc.Truncate(nsUnescapeCount(loc.BeginWriting())); 1.704 + if (loc.First() == PRUnichar('.')) 1.705 + pushBuffer.AppendLiteral(" class=\"hidden-object\""); 1.706 + 1.707 + pushBuffer.AppendLiteral(">\n <td sortable-data=\""); 1.708 + 1.709 + // The sort key is the name of the item, prepended by either 0, 1 or 2 1.710 + // in order to group items. 1.711 + uint32_t type; 1.712 + aIndex->GetType(&type); 1.713 + switch (type) { 1.714 + case nsIDirIndex::TYPE_SYMLINK: 1.715 + pushBuffer.Append('0'); 1.716 + break; 1.717 + case nsIDirIndex::TYPE_DIRECTORY: 1.718 + pushBuffer.Append('1'); 1.719 + break; 1.720 + default: 1.721 + pushBuffer.Append('2'); 1.722 + break; 1.723 + } 1.724 + nsAdoptingCString escaped(nsEscapeHTML(loc)); 1.725 + pushBuffer.Append(escaped); 1.726 + 1.727 + pushBuffer.AppendLiteral("\"><table class=\"ellipsis\"><tbody><tr><td><a class=\""); 1.728 + switch (type) { 1.729 + case nsIDirIndex::TYPE_DIRECTORY: 1.730 + pushBuffer.AppendLiteral("dir"); 1.731 + break; 1.732 + case nsIDirIndex::TYPE_SYMLINK: 1.733 + pushBuffer.AppendLiteral("symlink"); 1.734 + break; 1.735 + default: 1.736 + pushBuffer.AppendLiteral("file"); 1.737 + break; 1.738 + } 1.739 + 1.740 + pushBuffer.AppendLiteral("\" href=\""); 1.741 + 1.742 + // need to escape links 1.743 + nsAutoCString locEscaped; 1.744 + 1.745 + // Adding trailing slash helps to recognize whether the URL points to a file 1.746 + // or a directory (bug #214405). 1.747 + if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) { 1.748 + loc.Append('/'); 1.749 + } 1.750 + 1.751 + // now minimally re-escape the location... 1.752 + uint32_t escFlags; 1.753 + // for some protocols, we expect the location to be absolute. 1.754 + // if so, and if the location indeed appears to be a valid URI, then go 1.755 + // ahead and treat it like one. 1.756 + if (mExpectAbsLoc && 1.757 + NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) { 1.758 + // escape as absolute 1.759 + escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal; 1.760 + } 1.761 + else { 1.762 + // escape as relative 1.763 + // esc_Directory is needed because directories have a trailing slash. 1.764 + // Without it, the trailing '/' will be escaped, and links from within 1.765 + // that directory will be incorrect 1.766 + escFlags = esc_Forced | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory; 1.767 + } 1.768 + NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped); 1.769 + // esc_Directory does not escape the semicolons, so if a filename 1.770 + // contains semicolons we need to manually escape them. 1.771 + // This replacement should be removed in bug #473280 1.772 + locEscaped.ReplaceSubstring(";", "%3b"); 1.773 + nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get())); 1.774 + pushBuffer.Append(htmlEscapedURL); 1.775 + 1.776 + pushBuffer.AppendLiteral("\">"); 1.777 + 1.778 + if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { 1.779 + pushBuffer.AppendLiteral("<img src=\"moz-icon://"); 1.780 + int32_t lastDot = locEscaped.RFindChar('.'); 1.781 + if (lastDot != kNotFound) { 1.782 + locEscaped.Cut(0, lastDot); 1.783 + nsAdoptingCString htmlFileExt(nsEscapeHTML(locEscaped.get())); 1.784 + pushBuffer.Append(htmlFileExt); 1.785 + } else { 1.786 + pushBuffer.AppendLiteral("unknown"); 1.787 + } 1.788 + pushBuffer.AppendLiteral("?size=16\" alt=\""); 1.789 + 1.790 + nsXPIDLString altText; 1.791 + rv = mBundle->GetStringFromName(MOZ_UTF16("DirFileLabel"), 1.792 + getter_Copies(altText)); 1.793 + if (NS_FAILED(rv)) return rv; 1.794 + AppendNonAsciiToNCR(altText, pushBuffer); 1.795 + pushBuffer.AppendLiteral("\">"); 1.796 + } 1.797 + 1.798 + pushBuffer.Append(escaped); 1.799 + pushBuffer.AppendLiteral("</a></td></tr></tbody></table></td>\n <td"); 1.800 + 1.801 + if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) { 1.802 + pushBuffer.AppendLiteral(">"); 1.803 + } else { 1.804 + int64_t size; 1.805 + aIndex->GetSize(&size); 1.806 + 1.807 + if (uint64_t(size) != UINT64_MAX) { 1.808 + pushBuffer.AppendLiteral(" sortable-data=\""); 1.809 + pushBuffer.AppendInt(size); 1.810 + pushBuffer.AppendLiteral("\">"); 1.811 + nsAutoCString sizeString; 1.812 + FormatSizeString(size, sizeString); 1.813 + pushBuffer.Append(sizeString); 1.814 + } else { 1.815 + pushBuffer.AppendLiteral(">"); 1.816 + } 1.817 + } 1.818 + pushBuffer.AppendLiteral("</td>\n <td"); 1.819 + 1.820 + PRTime t; 1.821 + aIndex->GetLastModified(&t); 1.822 + 1.823 + if (t == -1) { 1.824 + pushBuffer.AppendLiteral("></td>\n <td>"); 1.825 + } else { 1.826 + pushBuffer.AppendLiteral(" sortable-data=\""); 1.827 + pushBuffer.AppendInt(static_cast<int64_t>(t)); 1.828 + pushBuffer.AppendLiteral("\">"); 1.829 + nsAutoString formatted; 1.830 + mDateTime->FormatPRTime(nullptr, 1.831 + kDateFormatShort, 1.832 + kTimeFormatNone, 1.833 + t, 1.834 + formatted); 1.835 + AppendNonAsciiToNCR(formatted, pushBuffer); 1.836 + pushBuffer.AppendLiteral("</td>\n <td>"); 1.837 + mDateTime->FormatPRTime(nullptr, 1.838 + kDateFormatNone, 1.839 + kTimeFormatSeconds, 1.840 + t, 1.841 + formatted); 1.842 + // use NCR to show date in any doc charset 1.843 + AppendNonAsciiToNCR(formatted, pushBuffer); 1.844 + } 1.845 + 1.846 + pushBuffer.AppendLiteral("</td>\n</tr>"); 1.847 + 1.848 + return SendToListener(aRequest, aCtxt, pushBuffer); 1.849 +} 1.850 + 1.851 +NS_IMETHODIMP 1.852 +nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest, 1.853 + nsISupports *aCtxt, 1.854 + const nsAString& aInfo) { 1.855 + nsAutoCString pushBuffer; 1.856 + nsAdoptingString escaped(nsEscapeHTML2(PromiseFlatString(aInfo).get())); 1.857 + if (!escaped) 1.858 + return NS_ERROR_OUT_OF_MEMORY; 1.859 + pushBuffer.AppendLiteral("<tr>\n <td>"); 1.860 + // escaped is provided in Unicode, so write hex NCRs as necessary 1.861 + // to prevent the HTML parser from applying a character set. 1.862 + AppendNonAsciiToNCR(escaped, pushBuffer); 1.863 + pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n"); 1.864 + 1.865 + return SendToListener(aRequest, aCtxt, pushBuffer); 1.866 +} 1.867 + 1.868 +void nsIndexedToHTML::FormatSizeString(int64_t inSize, nsCString& outSizeString) 1.869 +{ 1.870 + outSizeString.Truncate(); 1.871 + if (inSize > int64_t(0)) { 1.872 + // round up to the nearest Kilobyte 1.873 + int64_t upperSize = (inSize + int64_t(1023)) / int64_t(1024); 1.874 + outSizeString.AppendInt(upperSize); 1.875 + outSizeString.AppendLiteral(" KB"); 1.876 + } 1.877 +} 1.878 + 1.879 +nsIndexedToHTML::nsIndexedToHTML() { 1.880 +} 1.881 + 1.882 +nsIndexedToHTML::~nsIndexedToHTML() { 1.883 +}