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