1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Bradley Baetz <bbaetz@cs.mcgill.ca>
24 : * Christopher A. Aillon <christopher@aillon.com>
25 : * Dão Gottwald <dao@design-noir.de>
26 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsIndexedToHTML.h"
43 : #include "nsNetUtil.h"
44 : #include "netCore.h"
45 : #include "nsStringStream.h"
46 : #include "nsIFileURL.h"
47 : #include "nsEscape.h"
48 : #include "nsIDirIndex.h"
49 : #include "prtime.h"
50 : #include "nsDateTimeFormatCID.h"
51 : #include "nsURLHelper.h"
52 : #include "nsCRT.h"
53 : #include "nsIPlatformCharset.h"
54 : #include "nsIPrefService.h"
55 : #include "nsIPrefBranch.h"
56 : #include "nsIPrefLocalizedString.h"
57 : #include "nsIChromeRegistry.h"
58 :
59 0 : NS_IMPL_ISUPPORTS4(nsIndexedToHTML,
60 : nsIDirIndexListener,
61 : nsIStreamConverter,
62 : nsIRequestObserver,
63 : nsIStreamListener)
64 :
65 0 : static void AppendNonAsciiToNCR(const nsAString& in, nsAFlatString& out)
66 : {
67 0 : nsAString::const_iterator start, end;
68 :
69 0 : in.BeginReading(start);
70 0 : in.EndReading(end);
71 :
72 0 : while (start != end) {
73 0 : if (*start < 128) {
74 0 : out.Append(*start++);
75 : } else {
76 0 : out.AppendLiteral("&#x");
77 0 : nsAutoString hex;
78 0 : hex.AppendInt(*start++, 16);
79 0 : out.Append(hex);
80 0 : out.Append((PRUnichar)';');
81 : }
82 : }
83 0 : }
84 :
85 : nsresult
86 0 : nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
87 : nsresult rv;
88 0 : if (aOuter)
89 0 : return NS_ERROR_NO_AGGREGATION;
90 :
91 0 : nsIndexedToHTML* _s = new nsIndexedToHTML();
92 0 : if (_s == nsnull)
93 0 : return NS_ERROR_OUT_OF_MEMORY;
94 :
95 0 : rv = _s->QueryInterface(aIID, aResult);
96 0 : return rv;
97 : }
98 :
99 : nsresult
100 0 : nsIndexedToHTML::Init(nsIStreamListener* aListener) {
101 :
102 0 : nsXPIDLString ellipsis;
103 0 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
104 0 : if (prefs) {
105 0 : nsCOMPtr<nsIPrefLocalizedString> prefVal;
106 0 : prefs->GetComplexValue("intl.ellipsis",
107 : NS_GET_IID(nsIPrefLocalizedString),
108 0 : getter_AddRefs(prefVal));
109 0 : if (prefVal)
110 0 : prefVal->ToString(getter_Copies(ellipsis));
111 : }
112 0 : if (ellipsis.IsEmpty())
113 0 : mEscapedEllipsis.AppendLiteral("…");
114 : else
115 0 : mEscapedEllipsis.Adopt(nsEscapeHTML2(ellipsis.get(), ellipsis.Length()));
116 :
117 0 : nsresult rv = NS_OK;
118 :
119 0 : mListener = aListener;
120 :
121 0 : mDateTime = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
122 0 : if (NS_FAILED(rv))
123 0 : return rv;
124 :
125 : nsCOMPtr<nsIStringBundleService> sbs =
126 0 : do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
127 0 : if (NS_FAILED(rv)) return rv;
128 0 : rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle));
129 :
130 0 : mExpectAbsLoc = false;
131 :
132 0 : return rv;
133 : }
134 :
135 : NS_IMETHODIMP
136 0 : nsIndexedToHTML::Convert(nsIInputStream* aFromStream,
137 : const char* aFromType,
138 : const char* aToType,
139 : nsISupports* aCtxt,
140 : nsIInputStream** res) {
141 0 : return NS_ERROR_NOT_IMPLEMENTED;
142 : }
143 :
144 : NS_IMETHODIMP
145 0 : nsIndexedToHTML::AsyncConvertData(const char *aFromType,
146 : const char *aToType,
147 : nsIStreamListener *aListener,
148 : nsISupports *aCtxt) {
149 0 : return Init(aListener);
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) {
154 0 : nsString buffer;
155 0 : nsresult rv = DoOnStartRequest(request, aContext, buffer);
156 0 : if (NS_FAILED(rv)) {
157 0 : request->Cancel(rv);
158 : }
159 :
160 0 : rv = mListener->OnStartRequest(request, aContext);
161 0 : if (NS_FAILED(rv)) return rv;
162 :
163 : // The request may have been canceled, and if that happens, we want to
164 : // suppress calls to OnDataAvailable.
165 0 : request->GetStatus(&rv);
166 0 : if (NS_FAILED(rv)) return rv;
167 :
168 : // Push our buffer to the listener.
169 :
170 0 : rv = FormatInputStream(request, aContext, buffer);
171 0 : return rv;
172 : }
173 :
174 : nsresult
175 0 : nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
176 : nsString& aBuffer) {
177 : nsresult rv;
178 :
179 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
180 0 : nsCOMPtr<nsIURI> uri;
181 0 : rv = channel->GetURI(getter_AddRefs(uri));
182 0 : if (NS_FAILED(rv)) return rv;
183 :
184 0 : channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
185 :
186 0 : mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv);
187 0 : if (NS_FAILED(rv)) return rv;
188 :
189 0 : rv = mParser->SetListener(this);
190 0 : if (NS_FAILED(rv)) return rv;
191 :
192 0 : rv = mParser->OnStartRequest(request, aContext);
193 0 : if (NS_FAILED(rv)) return rv;
194 :
195 0 : nsCAutoString baseUri, titleUri;
196 0 : rv = uri->GetAsciiSpec(baseUri);
197 0 : if (NS_FAILED(rv)) return rv;
198 0 : titleUri = baseUri;
199 :
200 0 : nsCString parentStr;
201 :
202 : // XXX - should be using the 300: line from the parser.
203 : // We can't guarantee that that comes before any entry, so we'd have to
204 : // buffer, and do other painful stuff.
205 : // I'll deal with this when I make the changes to handle welcome messages
206 : // The .. stuff should also come from the lower level protocols, but that
207 : // would muck up the XUL display
208 : // - bbaetz
209 :
210 0 : bool isScheme = false;
211 0 : bool isSchemeFile = false;
212 0 : if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) {
213 :
214 : // strip out the password here, so it doesn't show in the page title
215 : // This is done by the 300: line generation in ftp, but we don't use
216 : // that - see above
217 :
218 0 : nsCAutoString pw;
219 0 : rv = uri->GetPassword(pw);
220 0 : if (NS_FAILED(rv)) return rv;
221 0 : if (!pw.IsEmpty()) {
222 0 : nsCOMPtr<nsIURI> newUri;
223 0 : rv = uri->Clone(getter_AddRefs(newUri));
224 0 : if (NS_FAILED(rv)) return rv;
225 0 : rv = newUri->SetPassword(EmptyCString());
226 0 : if (NS_FAILED(rv)) return rv;
227 0 : rv = newUri->GetAsciiSpec(titleUri);
228 0 : if (NS_FAILED(rv)) return rv;
229 : }
230 :
231 0 : nsCAutoString path;
232 0 : rv = uri->GetPath(path);
233 0 : if (NS_FAILED(rv)) return rv;
234 :
235 0 : if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) {
236 0 : rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr);
237 0 : if (NS_FAILED(rv)) return rv;
238 : }
239 0 : } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) {
240 0 : nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
241 0 : nsCOMPtr<nsIFile> file;
242 0 : rv = fileUrl->GetFile(getter_AddRefs(file));
243 0 : if (NS_FAILED(rv)) return rv;
244 0 : nsCOMPtr<nsILocalFile> lfile = do_QueryInterface(file, &rv);
245 0 : if (NS_FAILED(rv)) return rv;
246 0 : lfile->SetFollowLinks(true);
247 :
248 0 : nsCAutoString url;
249 0 : rv = net_GetURLSpecFromFile(file, url);
250 0 : if (NS_FAILED(rv)) return rv;
251 0 : baseUri.Assign(url);
252 :
253 0 : nsCOMPtr<nsIFile> parent;
254 0 : rv = file->GetParent(getter_AddRefs(parent));
255 :
256 0 : if (parent && NS_SUCCEEDED(rv)) {
257 0 : net_GetURLSpecFromDir(parent, url);
258 0 : if (NS_FAILED(rv)) return rv;
259 0 : parentStr.Assign(url);
260 : }
261 :
262 : // Directory index will be always encoded in UTF-8 if this is file url
263 0 : rv = mParser->SetEncoding("UTF-8");
264 0 : NS_ENSURE_SUCCESS(rv, rv);
265 :
266 0 : } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) {
267 0 : nsCAutoString path;
268 0 : rv = uri->GetPath(path);
269 0 : if (NS_FAILED(rv)) return rv;
270 :
271 : // a top-level jar directory URL is of the form jar:foo.zip!/
272 : // path will be of the form foo.zip!/, and its last two characters
273 : // will be "!/"
274 : //XXX this won't work correctly when the name of the directory being
275 : //XXX displayed ends with "!", but then again, jar: URIs don't deal
276 : //XXX particularly well with such directories anyway
277 0 : if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) {
278 0 : rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
279 0 : if (NS_FAILED(rv)) return rv;
280 : }
281 : }
282 : else {
283 : // default behavior for other protocols is to assume the channel's
284 : // URL references a directory ending in '/' -- fixup if necessary.
285 0 : nsCAutoString path;
286 0 : rv = uri->GetPath(path);
287 0 : if (NS_FAILED(rv)) return rv;
288 0 : if (baseUri.Last() != '/') {
289 0 : baseUri.Append('/');
290 0 : path.Append('/');
291 0 : uri->SetPath(path);
292 : }
293 0 : if (!path.EqualsLiteral("/")) {
294 0 : rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
295 0 : if (NS_FAILED(rv)) return rv;
296 : }
297 : }
298 :
299 0 : nsString buffer;
300 : buffer.AppendLiteral("<!DOCTYPE html>\n"
301 : "<html>\n<head>\n"
302 0 : "<meta http-equiv=\"content-type\" content=\"text/html; charset=");
303 :
304 : // Get the encoding from the parser
305 : // XXX - this won't work for any encoding set via a 301: line in the
306 : // format - this output stuff would need to move to OnDataAvailable
307 : // for that.
308 :
309 0 : nsXPIDLCString encoding;
310 0 : rv = mParser->GetEncoding(getter_Copies(encoding));
311 0 : if (NS_FAILED(rv)) return rv;
312 :
313 0 : AppendASCIItoUTF16(encoding, buffer);
314 : buffer.AppendLiteral("\">\n"
315 : "<style type=\"text/css\">\n"
316 : ":root {\n"
317 : " font-family: sans-serif;\n"
318 : "}\n"
319 : "img {\n"
320 : " border: 0;\n"
321 : "}\n"
322 : "th {\n"
323 : " text-align: start;\n"
324 : " white-space: nowrap;\n"
325 : "}\n"
326 : "th > a {\n"
327 : " color: inherit;\n"
328 : "}\n"
329 : "table[order] > thead > tr > th {\n"
330 : " cursor: pointer;\n"
331 : "}\n"
332 : "table[order] > thead > tr > th::after {\n"
333 : " display: none;\n"
334 : " width: .8em;\n"
335 : " -moz-margin-end: -.8em;\n"
336 : " text-align: end;\n"
337 : "}\n"
338 : "table[order=\"asc\"] > thead > tr > th::after {\n"
339 : " content: \"\\2193\"; /* DOWNWARDS ARROW (U+2193) */\n"
340 : "}\n"
341 : "table[order=\"desc\"] > thead > tr > th::after {\n"
342 : " content: \"\\2191\"; /* UPWARDS ARROW (U+2191) */\n"
343 : "}\n"
344 : "table[order][order-by=\"0\"] > thead > tr > th:first-child > a ,\n"
345 : "table[order][order-by=\"1\"] > thead > tr > th:first-child + th > a ,\n"
346 : "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th > a {\n"
347 : " text-decoration: underline;\n"
348 : "}\n"
349 : "table[order][order-by=\"0\"] > thead > tr > th:first-child::after ,\n"
350 : "table[order][order-by=\"1\"] > thead > tr > th:first-child + th::after ,\n"
351 : "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th::after {\n"
352 : " display: inline-block;\n"
353 : "}\n"
354 : "table.remove-hidden > tbody > tr.hidden-object {\n"
355 : " display: none;\n"
356 : "}\n"
357 : "td > a {\n"
358 : " display: inline-block;\n"
359 : "}\n"
360 : "/* name */\n"
361 : "th:first-child {\n"
362 : " -moz-padding-end: 2em;\n"
363 : "}\n"
364 : "/* size */\n"
365 : "th:first-child + th {\n"
366 : " -moz-padding-end: 1em;\n"
367 : "}\n"
368 : "td:first-child + td {\n"
369 : " text-align: end;\n"
370 : " -moz-padding-end: 1em;\n"
371 : " white-space: nowrap;\n"
372 : "}\n"
373 : "/* date */\n"
374 : "td:first-child + td + td {\n"
375 : " -moz-padding-start: 1em;\n"
376 : " -moz-padding-end: .5em;\n"
377 : " white-space: nowrap;\n"
378 : "}\n"
379 : "/* time */\n"
380 : "td:last-child {\n"
381 : " -moz-padding-start: .5em;\n"
382 : " white-space: nowrap;\n"
383 : "}\n"
384 : ".symlink {\n"
385 : " font-style: italic;\n"
386 : "}\n"
387 : ".dir ,\n"
388 : ".symlink ,\n"
389 : ".file {\n"
390 : " -moz-margin-start: 20px;\n"
391 : "}\n"
392 : ".dir::before ,\n"
393 : ".file > img {\n"
394 : " -moz-margin-end: 4px;\n"
395 : " -moz-margin-start: -20px;\n"
396 : " vertical-align: middle;\n"
397 : "}\n"
398 : ".dir::before {\n"
399 : " content: url(resource://gre/res/html/folder.png);\n"
400 : "}\n"
401 : "</style>\n"
402 : "<link rel=\"stylesheet\" media=\"screen, projection\" type=\"text/css\""
403 : " href=\"chrome://global/skin/dirListing/dirListing.css\">\n"
404 : "<script type=\"application/javascript\">\n"
405 : "var gTable, gOrderBy, gTBody, gRows, gUI_showHidden;\n"
406 : "document.addEventListener(\"DOMContentLoaded\", function() {\n"
407 : " gTable = document.getElementsByTagName(\"table\")[0];\n"
408 : " gTBody = gTable.tBodies[0];\n"
409 : " if (gTBody.rows.length < 2)\n"
410 : " return;\n"
411 : " gUI_showHidden = document.getElementById(\"UI_showHidden\");\n"
412 : " var headCells = gTable.tHead.rows[0].cells,\n"
413 : " hiddenObjects = false;\n"
414 : " function rowAction(i) {\n"
415 : " return function(event) {\n"
416 : " event.preventDefault();\n"
417 : " orderBy(i);\n"
418 : " }\n"
419 : " }\n"
420 : " for (var i = headCells.length - 1; i >= 0; i--) {\n"
421 : " var anchor = document.createElement(\"a\");\n"
422 : " anchor.href = \"\";\n"
423 : " anchor.appendChild(headCells[i].firstChild);\n"
424 : " headCells[i].appendChild(anchor);\n"
425 : " headCells[i].addEventListener(\"click\", rowAction(i), true);\n"
426 : " }\n"
427 : " if (gUI_showHidden) {\n"
428 : " gRows = Array.slice(gTBody.rows);\n"
429 : " hiddenObjects = gRows.some(function (row) row.className == \"hidden-object\");\n"
430 : " }\n"
431 : " gTable.setAttribute(\"order\", \"\");\n"
432 : " if (hiddenObjects) {\n"
433 : " gUI_showHidden.style.display = \"block\";\n"
434 : " updateHidden();\n"
435 : " }\n"
436 : "}, \"false\");\n"
437 : "function compareRows(rowA, rowB) {\n"
438 : " var a = rowA.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
439 : " var b = rowB.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
440 : " var intA = +a;\n"
441 : " var intB = +b;\n"
442 : " if (a == intA && b == intB) {\n"
443 : " a = intA;\n"
444 : " b = intB;\n"
445 : " } else {\n"
446 : " a = a.toLowerCase();\n"
447 : " b = b.toLowerCase();\n"
448 : " }\n"
449 : " if (a < b)\n"
450 : " return -1;\n"
451 : " if (a > b)\n"
452 : " return 1;\n"
453 : " return 0;\n"
454 : "}\n"
455 : "function orderBy(column) {\n"
456 : " if (!gRows)\n"
457 : " gRows = Array.slice(gTBody.rows);\n"
458 : " var order;\n"
459 : " if (gOrderBy == column) {\n"
460 : " order = gTable.getAttribute(\"order\") == \"asc\" ? \"desc\" : \"asc\";\n"
461 : " } else {\n"
462 : " order = \"asc\";\n"
463 : " gOrderBy = column;\n"
464 : " gTable.setAttribute(\"order-by\", column);\n"
465 : " gRows.sort(compareRows);\n"
466 : " }\n"
467 : " gTable.removeChild(gTBody);\n"
468 : " gTable.setAttribute(\"order\", order);\n"
469 : " if (order == \"asc\")\n"
470 : " for (var i = 0; i < gRows.length; i++)\n"
471 : " gTBody.appendChild(gRows[i]);\n"
472 : " else\n"
473 : " for (var i = gRows.length - 1; i >= 0; i--)\n"
474 : " gTBody.appendChild(gRows[i]);\n"
475 : " gTable.appendChild(gTBody);\n"
476 : "}\n"
477 : "function updateHidden() {\n"
478 : " gTable.className = gUI_showHidden.getElementsByTagName(\"input\")[0].checked ?\n"
479 : " \"\" :\n"
480 : " \"remove-hidden\";\n"
481 : "}\n"
482 0 : "</script>\n");
483 :
484 0 : buffer.AppendLiteral("<link rel=\"icon\" type=\"image/png\" href=\"");
485 0 : nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
486 0 : if (!innerUri)
487 0 : return NS_ERROR_UNEXPECTED;
488 0 : nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri));
489 : //XXX bug 388553: can't use skinnable icons here due to security restrictions
490 0 : if (fileURL) {
491 : //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png");
492 : buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
493 : "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
494 : "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR"
495 : "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj"
496 : "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O"
497 : "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ"
498 : "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG"
499 : "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5"
500 : "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS"
501 : "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c"
502 : "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU"
503 : "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF"
504 : "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi"
505 : "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l"
506 : "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M"
507 : "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI"
508 : "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM"
509 : "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B"
510 : "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8"
511 0 : "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D");
512 : } else {
513 : //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png");
514 : buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
515 : "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
516 : "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft"
517 : "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%"
518 : "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0"
519 : "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE"
520 : "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM"
521 : "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR"
522 : "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh"
523 : "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz"
524 : "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj"
525 : "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1"
526 : "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9"
527 : "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr"
528 : "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E"
529 : "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2"
530 : "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS"
531 0 : "YAAAAABJRU5ErkJggg%3D%3D");
532 : }
533 0 : buffer.AppendLiteral("\">\n<title>");
534 :
535 : // Everything needs to end in a /,
536 : // otherwise we end up linking to file:///foo/dirfile
537 :
538 0 : if (!mTextToSubURI) {
539 0 : mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
540 0 : if (NS_FAILED(rv)) return rv;
541 : }
542 :
543 0 : nsXPIDLString unEscapeSpec;
544 0 : rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(),
545 0 : getter_Copies(unEscapeSpec));
546 : // unescape may fail because
547 : // 1. file URL may be encoded in platform charset for backward compatibility
548 : // 2. query part may not be encoded in UTF-8 (see bug 261929)
549 : // so try the platform's default if this is file url
550 0 : if (NS_FAILED(rv) && isSchemeFile) {
551 0 : nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv));
552 0 : NS_ENSURE_SUCCESS(rv, rv);
553 0 : nsCAutoString charset;
554 0 : rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
555 0 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 0 : rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(),
558 0 : getter_Copies(unEscapeSpec));
559 : }
560 0 : if (NS_FAILED(rv)) return rv;
561 :
562 0 : nsXPIDLString htmlEscSpec;
563 : htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
564 0 : unEscapeSpec.Length()));
565 :
566 0 : nsXPIDLString title;
567 : const PRUnichar* formatTitle[] = {
568 0 : htmlEscSpec.get()
569 0 : };
570 :
571 0 : rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("DirTitle").get(),
572 : formatTitle,
573 : sizeof(formatTitle)/sizeof(PRUnichar*),
574 0 : getter_Copies(title));
575 0 : if (NS_FAILED(rv)) return rv;
576 :
577 : // we want to convert string bundle to NCR
578 : // to ensure they're shown in any charsets
579 0 : AppendNonAsciiToNCR(title, buffer);
580 :
581 0 : buffer.AppendLiteral("</title>\n");
582 :
583 : // If there is a quote character in the baseUri, then
584 : // lets not add a base URL. The reason for this is that
585 : // if we stick baseUri containing a quote into a quoted
586 : // string, the quote character will prematurely close
587 : // the base href string. This is a fall-back check;
588 : // that's why it is OK to not use a base rather than
589 : // trying to play nice and escaping the quotes. See bug
590 : // 358128.
591 :
592 0 : if (baseUri.FindChar('"') == kNotFound)
593 : {
594 : // Great, the baseUri does not contain a char that
595 : // will prematurely close the string. Go ahead an
596 : // add a base href.
597 0 : buffer.AppendLiteral("<base href=\"");
598 0 : NS_ConvertUTF8toUTF16 utf16BaseURI(baseUri);
599 0 : nsString htmlEscapedUri;
600 0 : htmlEscapedUri.Adopt(nsEscapeHTML2(utf16BaseURI.get(), utf16BaseURI.Length()));
601 0 : buffer.Append(htmlEscapedUri);
602 0 : buffer.AppendLiteral("\" />\n");
603 : }
604 : else
605 : {
606 0 : NS_ERROR("broken protocol handler didn't escape double-quote.");
607 : }
608 :
609 0 : nsAutoString direction(NS_LITERAL_STRING("ltr"));
610 : nsCOMPtr<nsIXULChromeRegistry> reg =
611 0 : mozilla::services::GetXULChromeRegistryService();
612 0 : if (reg) {
613 0 : bool isRTL = false;
614 0 : reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL);
615 0 : if (isRTL) {
616 0 : direction.AssignLiteral("rtl");
617 : }
618 : }
619 :
620 0 : buffer.AppendLiteral("</head>\n<body dir=\"");
621 0 : buffer.Append(direction);
622 0 : buffer.AppendLiteral("\">\n<h1>");
623 :
624 : const PRUnichar* formatHeading[] = {
625 0 : htmlEscSpec.get()
626 0 : };
627 :
628 0 : rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("DirTitle").get(),
629 : formatHeading,
630 : sizeof(formatHeading)/sizeof(PRUnichar*),
631 0 : getter_Copies(title));
632 0 : if (NS_FAILED(rv)) return rv;
633 :
634 0 : AppendNonAsciiToNCR(title, buffer);
635 0 : buffer.AppendLiteral("</h1>\n");
636 :
637 0 : if (!parentStr.IsEmpty()) {
638 0 : nsXPIDLString parentText;
639 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirGoUp").get(),
640 0 : getter_Copies(parentText));
641 0 : if (NS_FAILED(rv)) return rv;
642 :
643 0 : buffer.AppendLiteral("<p id=\"UI_goUp\"><a class=\"up\" href=\"");
644 :
645 0 : NS_ConvertUTF8toUTF16 utf16ParentStr(parentStr);
646 0 : nsString htmlParentStr;
647 0 : htmlParentStr.Adopt(nsEscapeHTML2(utf16ParentStr.get(), utf16ParentStr.Length()));
648 0 : buffer.Append(htmlParentStr);
649 0 : buffer.AppendLiteral("\">");
650 0 : AppendNonAsciiToNCR(parentText, buffer);
651 0 : buffer.AppendLiteral("</a></p>\n");
652 : }
653 :
654 0 : if (isSchemeFile) {
655 0 : nsXPIDLString showHiddenText;
656 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("ShowHidden").get(),
657 0 : getter_Copies(showHiddenText));
658 0 : if (NS_FAILED(rv)) return rv;
659 :
660 0 : buffer.AppendLiteral("<p id=\"UI_showHidden\" style=\"display:none\"><label><input type=\"checkbox\" checked onchange=\"updateHidden()\">");
661 0 : AppendNonAsciiToNCR(showHiddenText, buffer);
662 0 : buffer.AppendLiteral("</label></p>\n");
663 : }
664 :
665 0 : buffer.AppendLiteral("<table>\n");
666 :
667 0 : nsXPIDLString columnText;
668 :
669 : buffer.AppendLiteral(" <thead>\n"
670 : " <tr>\n"
671 0 : " <th>");
672 :
673 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirColName").get(),
674 0 : getter_Copies(columnText));
675 0 : if (NS_FAILED(rv)) return rv;
676 0 : AppendNonAsciiToNCR(columnText, buffer);
677 : buffer.AppendLiteral("</th>\n"
678 0 : " <th>");
679 :
680 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirColSize").get(),
681 0 : getter_Copies(columnText));
682 0 : if (NS_FAILED(rv)) return rv;
683 0 : AppendNonAsciiToNCR(columnText, buffer);
684 : buffer.AppendLiteral("</th>\n"
685 0 : " <th colspan=\"2\">");
686 :
687 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirColMTime").get(),
688 0 : getter_Copies(columnText));
689 0 : if (NS_FAILED(rv)) return rv;
690 0 : AppendNonAsciiToNCR(columnText, buffer);
691 : buffer.AppendLiteral("</th>\n"
692 : " </tr>\n"
693 0 : " </thead>\n");
694 0 : buffer.AppendLiteral(" <tbody>\n");
695 :
696 0 : aBuffer = buffer;
697 0 : return rv;
698 : }
699 :
700 : NS_IMETHODIMP
701 0 : nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext,
702 : nsresult aStatus) {
703 0 : if (NS_SUCCEEDED(aStatus)) {
704 0 : nsString buffer;
705 0 : buffer.AssignLiteral("</tbody></table></body></html>\n");
706 :
707 0 : aStatus = FormatInputStream(request, aContext, buffer);
708 : }
709 :
710 0 : mParser->OnStopRequest(request, aContext, aStatus);
711 0 : mParser = 0;
712 :
713 0 : return mListener->OnStopRequest(request, aContext, aStatus);
714 : }
715 :
716 : nsresult
717 0 : nsIndexedToHTML::FormatInputStream(nsIRequest* aRequest, nsISupports *aContext, const nsAString &aBuffer)
718 : {
719 0 : nsresult rv = NS_OK;
720 :
721 : // set up unicode encoder
722 0 : if (!mUnicodeEncoder) {
723 0 : nsXPIDLCString encoding;
724 0 : rv = mParser->GetEncoding(getter_Copies(encoding));
725 0 : if (NS_SUCCEEDED(rv)) {
726 0 : nsCOMPtr<nsICharsetConverterManager> charsetConverterManager;
727 0 : charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
728 0 : rv = charsetConverterManager->GetUnicodeEncoder(encoding.get(),
729 0 : getter_AddRefs(mUnicodeEncoder));
730 0 : if (NS_SUCCEEDED(rv))
731 0 : rv = mUnicodeEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
732 0 : nsnull, (PRUnichar)'?');
733 : }
734 : }
735 :
736 : // convert the data with unicode encoder
737 0 : char *buffer = nsnull;
738 : PRInt32 dstLength;
739 0 : if (NS_SUCCEEDED(rv)) {
740 0 : PRInt32 unicharLength = aBuffer.Length();
741 0 : rv = mUnicodeEncoder->GetMaxLength(PromiseFlatString(aBuffer).get(),
742 0 : unicharLength, &dstLength);
743 0 : if (NS_SUCCEEDED(rv)) {
744 0 : buffer = (char *) nsMemory::Alloc(dstLength);
745 0 : NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
746 :
747 0 : rv = mUnicodeEncoder->Convert(PromiseFlatString(aBuffer).get(), &unicharLength,
748 0 : buffer, &dstLength);
749 0 : if (NS_SUCCEEDED(rv)) {
750 0 : PRInt32 finLen = 0;
751 0 : rv = mUnicodeEncoder->Finish(buffer + dstLength, &finLen);
752 0 : if (NS_SUCCEEDED(rv))
753 0 : dstLength += finLen;
754 : }
755 : }
756 : }
757 :
758 : // if conversion error then fallback to UTF-8
759 0 : if (NS_FAILED(rv)) {
760 0 : rv = NS_OK;
761 0 : if (buffer) {
762 0 : nsMemory::Free(buffer);
763 0 : buffer = nsnull;
764 : }
765 : }
766 :
767 0 : nsCOMPtr<nsIInputStream> inputData;
768 0 : if (buffer) {
769 0 : rv = NS_NewCStringInputStream(getter_AddRefs(inputData), Substring(buffer, dstLength));
770 0 : nsMemory::Free(buffer);
771 0 : NS_ENSURE_SUCCESS(rv, rv);
772 0 : rv = mListener->OnDataAvailable(aRequest, aContext,
773 0 : inputData, 0, dstLength);
774 : }
775 : else {
776 0 : NS_ConvertUTF16toUTF8 utf8Buffer(aBuffer);
777 0 : rv = NS_NewCStringInputStream(getter_AddRefs(inputData), utf8Buffer);
778 0 : NS_ENSURE_SUCCESS(rv, rv);
779 0 : rv = mListener->OnDataAvailable(aRequest, aContext,
780 0 : inputData, 0, utf8Buffer.Length());
781 : }
782 0 : return (rv);
783 : }
784 :
785 : NS_IMETHODIMP
786 0 : nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest,
787 : nsISupports *aCtxt,
788 : nsIInputStream* aInput,
789 : PRUint32 aOffset,
790 : PRUint32 aCount) {
791 0 : return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount);
792 : }
793 :
794 : NS_IMETHODIMP
795 0 : nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
796 : nsISupports *aCtxt,
797 : nsIDirIndex *aIndex) {
798 : nsresult rv;
799 0 : if (!aIndex)
800 0 : return NS_ERROR_NULL_POINTER;
801 :
802 0 : nsString pushBuffer;
803 0 : pushBuffer.AppendLiteral("<tr");
804 :
805 0 : nsXPIDLString description;
806 0 : aIndex->GetDescription(getter_Copies(description));
807 0 : if (description.First() == PRUnichar('.'))
808 0 : pushBuffer.AppendLiteral(" class=\"hidden-object\"");
809 :
810 0 : pushBuffer.AppendLiteral(">\n <td sortable-data=\"");
811 :
812 : // The sort key is the name of the item, prepended by either 0, 1 or 2
813 : // in order to group items.
814 : PRUint32 type;
815 0 : aIndex->GetType(&type);
816 0 : switch (type) {
817 : case nsIDirIndex::TYPE_SYMLINK:
818 0 : pushBuffer.AppendInt(0);
819 0 : break;
820 : case nsIDirIndex::TYPE_DIRECTORY:
821 0 : pushBuffer.AppendInt(1);
822 0 : break;
823 : case nsIDirIndex::TYPE_FILE:
824 : case nsIDirIndex::TYPE_UNKNOWN:
825 0 : pushBuffer.AppendInt(2);
826 0 : break;
827 : }
828 0 : PRUnichar* escaped = nsEscapeHTML2(description.get(), description.Length());
829 0 : pushBuffer.Append(escaped);
830 :
831 0 : pushBuffer.AppendLiteral("\"><a class=\"");
832 0 : switch (type) {
833 : case nsIDirIndex::TYPE_DIRECTORY:
834 0 : pushBuffer.AppendLiteral("dir");
835 0 : break;
836 : case nsIDirIndex::TYPE_SYMLINK:
837 0 : pushBuffer.AppendLiteral("symlink");
838 0 : break;
839 : case nsIDirIndex::TYPE_FILE:
840 : case nsIDirIndex::TYPE_UNKNOWN:
841 0 : pushBuffer.AppendLiteral("file");
842 0 : break;
843 : }
844 0 : pushBuffer.AppendLiteral("\"");
845 :
846 : // Truncate long names to not stretch the table
847 : //XXX this should be left to the stylesheet (bug 391471)
848 0 : nsString escapedShort;
849 0 : if (description.Length() > 71) {
850 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
851 0 : nsCOMPtr<nsIURI> uri;
852 0 : rv = channel->GetURI(getter_AddRefs(uri));
853 0 : if (NS_FAILED(rv)) return rv;
854 :
855 : //XXX this potentially truncates after a combining char (bug 391472)
856 0 : nsXPIDLString descriptionAffix;
857 0 : descriptionAffix.Assign(description);
858 0 : descriptionAffix.Cut(0, descriptionAffix.Length() - 25);
859 0 : if (NS_IS_LOW_SURROGATE(descriptionAffix.First()))
860 0 : descriptionAffix.Cut(0, 1);
861 0 : description.Truncate(NS_MIN<PRUint32>(71, description.Length() - 28));
862 0 : if (NS_IS_HIGH_SURROGATE(description.Last()))
863 0 : description.Truncate(description.Length() - 1);
864 :
865 0 : escapedShort.Adopt(nsEscapeHTML2(description.get(), description.Length()));
866 :
867 0 : escapedShort.Append(mEscapedEllipsis);
868 : // add ZERO WIDTH SPACE (U+200B) for wrapping
869 0 : escapedShort.AppendLiteral("​");
870 0 : nsString tmp;
871 0 : tmp.Adopt(nsEscapeHTML2(descriptionAffix.get(), descriptionAffix.Length()));
872 0 : escapedShort.Append(tmp);
873 :
874 0 : pushBuffer.AppendLiteral(" title=\"");
875 0 : pushBuffer.Append(escaped);
876 0 : pushBuffer.AppendLiteral("\"");
877 : }
878 0 : if (escapedShort.IsEmpty())
879 0 : escapedShort.Assign(escaped);
880 0 : nsMemory::Free(escaped);
881 :
882 0 : pushBuffer.AppendLiteral(" href=\"");
883 0 : nsXPIDLCString loc;
884 0 : aIndex->GetLocation(getter_Copies(loc));
885 :
886 0 : if (!mTextToSubURI) {
887 0 : mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
888 0 : if (NS_FAILED(rv)) return rv;
889 : }
890 :
891 0 : nsXPIDLCString encoding;
892 0 : rv = mParser->GetEncoding(getter_Copies(encoding));
893 0 : if (NS_FAILED(rv)) return rv;
894 :
895 0 : nsXPIDLString unEscapeSpec;
896 0 : rv = mTextToSubURI->UnEscapeAndConvert(encoding, loc,
897 0 : getter_Copies(unEscapeSpec));
898 0 : if (NS_FAILED(rv)) return rv;
899 :
900 : // need to escape links
901 0 : nsCAutoString escapeBuf;
902 :
903 0 : NS_ConvertUTF16toUTF8 utf8UnEscapeSpec(unEscapeSpec);
904 :
905 : // Adding trailing slash helps to recognize whether the URL points to a file
906 : // or a directory (bug #214405).
907 0 : if ((type == nsIDirIndex::TYPE_DIRECTORY) &&
908 0 : (utf8UnEscapeSpec.Last() != '/')) {
909 0 : utf8UnEscapeSpec.Append('/');
910 : }
911 :
912 : // now minimally re-escape the location...
913 : PRUint32 escFlags;
914 : // for some protocols, we expect the location to be absolute.
915 : // if so, and if the location indeed appears to be a valid URI, then go
916 : // ahead and treat it like one.
917 0 : if (mExpectAbsLoc &&
918 0 : NS_SUCCEEDED(net_ExtractURLScheme(utf8UnEscapeSpec, nsnull, nsnull, nsnull))) {
919 : // escape as absolute
920 0 : escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal;
921 : }
922 : else {
923 : // escape as relative
924 : // esc_Directory is needed because directories have a trailing slash.
925 : // Without it, the trailing '/' will be escaped, and links from within
926 : // that directory will be incorrect
927 0 : escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
928 : }
929 0 : NS_EscapeURL(utf8UnEscapeSpec.get(), utf8UnEscapeSpec.Length(), escFlags, escapeBuf);
930 : // esc_Directory does not escape the semicolons, so if a filename
931 : // contains semicolons we need to manually escape them.
932 : // This replacement should be removed in bug #473280
933 0 : escapeBuf.ReplaceSubstring(";", "%3b");
934 0 : NS_ConvertUTF8toUTF16 utf16URI(escapeBuf);
935 0 : nsString htmlEscapedURL;
936 0 : htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length()));
937 0 : pushBuffer.Append(htmlEscapedURL);
938 :
939 0 : pushBuffer.AppendLiteral("\">");
940 :
941 0 : if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) {
942 0 : pushBuffer.AppendLiteral("<img src=\"moz-icon://");
943 0 : PRInt32 lastDot = escapeBuf.RFindChar('.');
944 0 : if (lastDot != kNotFound) {
945 0 : escapeBuf.Cut(0, lastDot);
946 0 : NS_ConvertUTF8toUTF16 utf16EscapeBuf(escapeBuf);
947 0 : nsString htmlFileExt;
948 0 : htmlFileExt.Adopt(nsEscapeHTML2(utf16EscapeBuf.get(), utf16EscapeBuf.Length()));
949 0 : pushBuffer.Append(htmlFileExt);
950 : } else {
951 0 : pushBuffer.AppendLiteral("unknown");
952 : }
953 0 : pushBuffer.AppendLiteral("?size=16\" alt=\"");
954 :
955 0 : nsXPIDLString altText;
956 0 : rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirFileLabel").get(),
957 0 : getter_Copies(altText));
958 0 : if (NS_FAILED(rv)) return rv;
959 0 : AppendNonAsciiToNCR(altText, pushBuffer);
960 0 : pushBuffer.AppendLiteral("\">");
961 : }
962 :
963 0 : pushBuffer.Append(escapedShort);
964 0 : pushBuffer.AppendLiteral("</a></td>\n <td");
965 :
966 0 : if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) {
967 0 : pushBuffer.AppendLiteral(">");
968 : } else {
969 : PRInt64 size;
970 0 : aIndex->GetSize(&size);
971 :
972 0 : if (PRUint64(size) != LL_MAXUINT) {
973 0 : pushBuffer.AppendLiteral(" sortable-data=\"");
974 0 : pushBuffer.AppendInt(size);
975 0 : pushBuffer.AppendLiteral("\">");
976 0 : nsAutoString sizeString;
977 0 : FormatSizeString(size, sizeString);
978 0 : pushBuffer.Append(sizeString);
979 : } else {
980 0 : pushBuffer.AppendLiteral(">");
981 : }
982 : }
983 0 : pushBuffer.AppendLiteral("</td>\n <td");
984 :
985 : PRTime t;
986 0 : aIndex->GetLastModified(&t);
987 :
988 0 : if (t == -1) {
989 0 : pushBuffer.AppendLiteral("></td>\n <td>");
990 : } else {
991 0 : pushBuffer.AppendLiteral(" sortable-data=\"");
992 0 : pushBuffer.AppendInt(t);
993 0 : pushBuffer.AppendLiteral("\">");
994 0 : nsAutoString formatted;
995 0 : mDateTime->FormatPRTime(nsnull,
996 : kDateFormatShort,
997 : kTimeFormatNone,
998 : t,
999 0 : formatted);
1000 0 : AppendNonAsciiToNCR(formatted, pushBuffer);
1001 0 : pushBuffer.AppendLiteral("</td>\n <td>");
1002 0 : mDateTime->FormatPRTime(nsnull,
1003 : kDateFormatNone,
1004 : kTimeFormatSeconds,
1005 : t,
1006 0 : formatted);
1007 : // use NCR to show date in any doc charset
1008 0 : AppendNonAsciiToNCR(formatted, pushBuffer);
1009 : }
1010 :
1011 0 : pushBuffer.AppendLiteral("</td>\n</tr>");
1012 :
1013 0 : return FormatInputStream(aRequest, aCtxt, pushBuffer);
1014 : }
1015 :
1016 : NS_IMETHODIMP
1017 0 : nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest,
1018 : nsISupports *aCtxt,
1019 : const nsAString& aInfo) {
1020 0 : nsAutoString pushBuffer;
1021 0 : PRUnichar* escaped = nsEscapeHTML2(PromiseFlatString(aInfo).get());
1022 0 : if (!escaped)
1023 0 : return NS_ERROR_OUT_OF_MEMORY;
1024 0 : pushBuffer.AppendLiteral("<tr>\n <td>");
1025 0 : pushBuffer.Append(escaped);
1026 0 : nsMemory::Free(escaped);
1027 0 : pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n");
1028 :
1029 0 : return FormatInputStream(aRequest, aCtxt, pushBuffer);
1030 : }
1031 :
1032 0 : void nsIndexedToHTML::FormatSizeString(PRInt64 inSize, nsString& outSizeString)
1033 : {
1034 0 : outSizeString.Truncate();
1035 0 : if (inSize > PRInt64(0)) {
1036 : // round up to the nearest Kilobyte
1037 0 : PRInt64 upperSize = (inSize + PRInt64(1023)) / PRInt64(1024);
1038 0 : outSizeString.AppendInt(upperSize);
1039 0 : outSizeString.AppendLiteral(" KB");
1040 : }
1041 0 : }
1042 :
1043 0 : nsIndexedToHTML::nsIndexedToHTML() {
1044 0 : }
1045 :
1046 0 : nsIndexedToHTML::~nsIndexedToHTML() {
1047 0 : }
|