Back to index

lightning-sunbird  0.9+nobinonly
nsIndexedToHTML.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Bradley Baetz <bbaetz@cs.mcgill.ca>
00024  *   Christopher A. Aillon <christopher@aillon.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsIndexedToHTML.h"
00041 #include "nsNetUtil.h"
00042 #include "nsIStringStream.h"
00043 #include "nsIFileURL.h"
00044 #include "nsEscape.h"
00045 #include "nsIDirIndex.h"
00046 #include "prtime.h"
00047 #include "nsDateTimeFormatCID.h"
00048 #include "nsURLHelper.h"
00049 #include "nsCRT.h"
00050 #include "nsIPlatformCharset.h"
00051 #include "nsInt64.h"
00052 
00053 NS_IMPL_THREADSAFE_ISUPPORTS4(nsIndexedToHTML,
00054                               nsIDirIndexListener,
00055                               nsIStreamConverter,
00056                               nsIRequestObserver,
00057                               nsIStreamListener)
00058 
00059 static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
00060 
00061 static void ConvertNonAsciiToNCR(const nsAString& in, nsAFlatString& out)
00062 {
00063   nsAString::const_iterator start, end;
00064 
00065   in.BeginReading(start);
00066   in.EndReading(end);
00067 
00068   out.Truncate();
00069 
00070   while (start != end) {
00071     if (*start < 128) {
00072       out.Append(*start++);
00073     } else {
00074       out.AppendLiteral("&#x");
00075       nsAutoString hex;
00076       hex.AppendInt(*start++, 16);
00077       out.Append(hex);
00078       out.Append((PRUnichar)';');
00079     }
00080   }
00081 }
00082 
00083 NS_METHOD
00084 nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
00085     nsresult rv;
00086     if (aOuter)
00087         return NS_ERROR_NO_AGGREGATION;
00088     
00089     nsIndexedToHTML* _s = new nsIndexedToHTML();
00090     if (_s == nsnull)
00091         return NS_ERROR_OUT_OF_MEMORY;
00092     
00093     rv = _s->QueryInterface(aIID, aResult);
00094     return rv;
00095 }
00096 
00097 nsresult
00098 nsIndexedToHTML::Init(nsIStreamListener* aListener) {
00099     nsresult rv = NS_OK;
00100 
00101     mListener = aListener;
00102 
00103     mDateTime = do_CreateInstance(kDateTimeFormatCID, &rv);
00104 
00105     nsCOMPtr<nsIStringBundleService> sbs =
00106         do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00107     if (NS_FAILED(rv)) return rv;
00108     rv = sbs->CreateBundle("chrome://necko/locale/necko.properties",
00109                            getter_AddRefs(mBundle));
00110 
00111     mRowCount = 0;
00112     mExpectAbsLoc = PR_FALSE;
00113 
00114     return rv;
00115 }
00116 
00117 NS_IMETHODIMP
00118 nsIndexedToHTML::Convert(nsIInputStream* aFromStream,
00119                          const char* aFromType,
00120                          const char* aToType,
00121                          nsISupports* aCtxt,
00122                          nsIInputStream** res) {
00123     return NS_ERROR_NOT_IMPLEMENTED;
00124 }
00125 
00126 NS_IMETHODIMP
00127 nsIndexedToHTML::AsyncConvertData(const char *aFromType,
00128                                   const char *aToType,
00129                                   nsIStreamListener *aListener,
00130                                   nsISupports *aCtxt) {
00131     return Init(aListener);
00132 }
00133 
00134 NS_IMETHODIMP
00135 nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) {
00136     nsresult rv;
00137 
00138     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00139     nsCOMPtr<nsIURI> uri;
00140     rv = channel->GetURI(getter_AddRefs(uri));
00141     if (NS_FAILED(rv)) return rv;
00142 
00143     channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
00144 
00145     mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv);
00146     if (NS_FAILED(rv)) return rv;
00147 
00148     rv = mParser->SetListener(this);
00149     if (NS_FAILED(rv)) return rv;
00150     
00151     rv = mParser->OnStartRequest(request, aContext);
00152     if (NS_FAILED(rv)) return rv;
00153 
00154     nsCAutoString baseUri,titleUri;
00155     rv = uri->GetAsciiSpec(baseUri);
00156     if (NS_FAILED(rv)) return rv;
00157     titleUri = baseUri;
00158 
00159     nsCString parentStr;
00160 
00161     // XXX - should be using the 300: line from the parser.
00162     // We can't guarantee that that comes before any entry, so we'd have to
00163     // buffer, and do other painful stuff.
00164     // I'll deal with this when I make the changes to handle welcome messages
00165     // The .. stuff should also come from the lower level protocols, but that
00166     // would muck up the XUL display
00167     // - bbaetz
00168 
00169     PRBool isScheme = PR_FALSE;
00170     PRBool isSchemeFile = PR_FALSE;
00171     if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) {
00172 
00173         // ftp urls don't always end in a /
00174         // make sure they do
00175         // but look out for /%2F as path
00176         nsCAutoString path;
00177         rv = uri->GetPath(path);
00178         if (NS_FAILED(rv)) return rv;
00179         if (baseUri.Last() != '/' && !path.LowerCaseEqualsLiteral("/%2f")) {
00180             baseUri.Append('/');
00181             path.Append('/');
00182             uri->SetPath(path);
00183         }
00184 
00185         // strip out the password here, so it doesn't show in the page title
00186         // This is done by the 300: line generation in ftp, but we don't use
00187         // that - see above
00188         
00189         nsCAutoString pw;
00190         rv = uri->GetPassword(pw);
00191         if (NS_FAILED(rv)) return rv;
00192         if (!pw.IsEmpty()) {
00193              nsCOMPtr<nsIURI> newUri;
00194              rv = uri->Clone(getter_AddRefs(newUri));
00195              if (NS_FAILED(rv)) return rv;
00196              rv = newUri->SetPassword(EmptyCString());
00197              if (NS_FAILED(rv)) return rv;
00198              rv = newUri->GetAsciiSpec(titleUri);
00199              if (NS_FAILED(rv)) return rv;
00200              if (titleUri.Last() != '/' && !path.LowerCaseEqualsLiteral("/%2f"))
00201                  titleUri.Append('/');
00202         }
00203 
00204         if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) {
00205             rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr);
00206             if (NS_FAILED(rv)) return rv;
00207         }
00208     } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) {
00209         nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
00210         nsCOMPtr<nsIFile> file;
00211         rv = fileUrl->GetFile(getter_AddRefs(file));
00212         if (NS_FAILED(rv)) return rv;
00213         nsCOMPtr<nsILocalFile> lfile = do_QueryInterface(file, &rv);
00214         if (NS_FAILED(rv)) return rv;
00215         lfile->SetFollowLinks(PR_TRUE);
00216         
00217         nsCAutoString url;
00218         rv = net_GetURLSpecFromFile(file, url);
00219         if (NS_FAILED(rv)) return rv;
00220         baseUri.Assign(url);
00221         
00222         nsCOMPtr<nsIFile> parent;
00223         rv = file->GetParent(getter_AddRefs(parent));
00224         
00225         if (parent && NS_SUCCEEDED(rv)) {
00226             net_GetURLSpecFromFile(parent, url);
00227             if (NS_FAILED(rv)) return rv;
00228             parentStr.Assign(url);
00229         }
00230 
00231         // Directory index will be always encoded in UTF-8 if this is file url
00232         rv = mParser->SetEncoding("UTF-8");
00233         NS_ENSURE_SUCCESS(rv, rv);
00234 
00235     } else if (NS_SUCCEEDED(uri->SchemeIs("gopher", &isScheme)) && isScheme) {
00236         mExpectAbsLoc = PR_TRUE;
00237     }
00238     else {
00239         // default behavior for other protocols is to assume the channel's
00240         // URL references a directory ending in '/' -- fixup if necessary.
00241         nsCAutoString path;
00242         rv = uri->GetPath(path);
00243         if (NS_FAILED(rv)) return rv;
00244         if (baseUri.Last() != '/') {
00245             baseUri.Append('/');
00246             path.Append('/');
00247             uri->SetPath(path);
00248         }
00249         if (!path.EqualsLiteral("/")) {
00250             rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
00251             if (NS_FAILED(rv)) return rv;
00252         }
00253     }
00254 
00255     nsString buffer;
00256     buffer.AssignLiteral("<?xml version=\"1.0\" encoding=\"");
00257     
00258     // Get the encoding from the parser
00259     // XXX - this won't work for any encoding set via a 301: line in the
00260     // format - this output stuff would need to move to OnDataAvailable
00261     // for that. However, currently only file:// sends that, and file's HTML
00262     // index mode isn't enabled yet (bug 102812)
00263 
00264     nsXPIDLCString encoding;
00265     rv = mParser->GetEncoding(getter_Copies(encoding));
00266     if (NS_FAILED(rv)) return rv;
00267 
00268     AppendASCIItoUTF16(encoding, buffer);
00269 
00270     buffer.AppendLiteral("\"?>\n"
00271                          "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
00272                          "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
00273 
00274     // Anything but a gopher url needs to end in a /,
00275     // otherwise we end up linking to file:///foo/dirfile
00276 
00277     buffer.AppendLiteral("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><title>");
00278 
00279     nsXPIDLString title;
00280 
00281     if (!mTextToSubURI) {
00282         mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
00283         if (NS_FAILED(rv)) return rv;
00284     }
00285 
00286     nsXPIDLString unEscapeSpec;
00287     rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(),
00288                                            getter_Copies(unEscapeSpec));
00289     // unescape may fail because
00290     // 1. file URL may be encoded in platform charset for backward compatibility
00291     // 2. query part may not be encoded in UTF-8 (see bug 261929)
00292     // so try the platform's default if this is file url
00293     if (NS_FAILED(rv) && isSchemeFile) {
00294         nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv));
00295         NS_ENSURE_SUCCESS(rv, rv);
00296         nsCAutoString charset;
00297         rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
00298         NS_ENSURE_SUCCESS(rv, rv);
00299 
00300         rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(),
00301                                                getter_Copies(unEscapeSpec));
00302     }
00303     if (NS_FAILED(rv)) return rv;
00304 
00305     nsXPIDLString htmlEscSpec;
00306     htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
00307                                     unEscapeSpec.Length()));
00308 
00309     const PRUnichar* formatTitle[] = {
00310         htmlEscSpec.get()
00311     };
00312 
00313     rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("DirTitle").get(),
00314                                        formatTitle,
00315                                        sizeof(formatTitle)/sizeof(PRUnichar*),
00316                                        getter_Copies(title));
00317     if (NS_FAILED(rv)) return rv;
00318 
00319     // we want to convert string bundle to NCR
00320     // to ensure they're shown in any charsets
00321     nsAutoString strNCR;
00322     ConvertNonAsciiToNCR(title, strNCR);
00323     buffer.Append(strNCR);
00324 
00325     buffer.AppendLiteral("</title>");    
00326 
00327     // If there is a quote character in the baseUri, then
00328     // lets not add a base URL.  The reason for this is that
00329     // if we stick baseUri containing a quote into a quoted
00330     // string, the quote character will prematurely close
00331     // the base href string.  This is a fall-back check;
00332     // that's why it is OK to not use a base rather than
00333     // trying to play nice and escaping the quotes.  See bug
00334     // 358128.
00335 
00336     if (baseUri.FindChar('"') == kNotFound)
00337     {
00338         // Great, the baseUri does not contain a char that
00339         // will prematurely close the string.  Go ahead an
00340         // add a base href.
00341         buffer.AppendLiteral("<base href=\"");
00342         NS_ConvertUTF8toUTF16 utf16BaseURI(baseUri);
00343         nsString htmlEscapedUri;
00344         htmlEscapedUri.Adopt(nsEscapeHTML2(utf16BaseURI.get(), utf16BaseURI.Length()));
00345         buffer.Append(htmlEscapedUri);
00346         buffer.AppendLiteral("\"/>\n");
00347     }
00348     else
00349     {
00350         NS_ERROR("broken protocol handler didn't escape double-quote.");
00351     }
00352 
00353     buffer.AppendLiteral("<style type=\"text/css\">\n"
00354                          "img { border: 0; padding: 0 2px; vertical-align: text-bottom; }\n"
00355                          "td  { font-family: monospace; padding: 2px 3px; text-align: right; vertical-align: bottom; white-space: pre; }\n"
00356                          "td:first-child { text-align: left; padding: 2px 10px 2px 3px; }\n"
00357                          "table { border: 0; }\n"
00358                          "a.symlink { font-style: italic; }\n"
00359                          "</style>\n"
00360                          "</head>\n<body>\n<h1>");
00361     
00362     const PRUnichar* formatHeading[] = {
00363         htmlEscSpec.get()
00364     };
00365 
00366     rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("DirTitle").get(),
00367                                        formatHeading,
00368                                        sizeof(formatHeading)/sizeof(PRUnichar*),
00369                                        getter_Copies(title));
00370     if (NS_FAILED(rv)) return rv;
00371     
00372     ConvertNonAsciiToNCR(title, strNCR);
00373     buffer.Append(strNCR);
00374     buffer.AppendLiteral("</h1>\n<hr/><table>\n");
00375 
00376     //buffer.AppendLiteral("<tr><th>Name</th><th>Size</th><th>Last modified</th></tr>\n");
00377 
00378     if (!parentStr.IsEmpty()) {
00379         nsXPIDLString parentText;
00380         rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirGoUp").get(),
00381                                         getter_Copies(parentText));
00382         if (NS_FAILED(rv)) return rv;
00383 
00384         ConvertNonAsciiToNCR(parentText, strNCR);
00385         buffer.AppendLiteral("<tr><td colspan=\"3\"><a href=\"");
00386 
00387         NS_ConvertUTF8toUTF16 utf16ParentStr(parentStr);
00388         nsString htmlParentStr;
00389         htmlParentStr.Adopt(nsEscapeHTML2(utf16ParentStr.get(), utf16ParentStr.Length()));
00390         buffer.Append(htmlParentStr);
00391         buffer.AppendLiteral("\">");
00392         buffer.Append(strNCR);
00393         buffer.AppendLiteral("</a></td></tr>\n");
00394     }
00395 
00396     // Push buffer to the listener now, so the initial HTML will not
00397     // be parsed in OnDataAvailable().
00398 
00399     rv = mListener->OnStartRequest(request, aContext);
00400     if (NS_FAILED(rv)) return rv;
00401 
00402     // The request may have been canceled, and if that happens, we want to
00403     // suppress calls to OnDataAvailable.
00404     request->GetStatus(&rv);
00405     if (NS_FAILED(rv)) return rv;
00406 
00407     rv = FormatInputStream(request, aContext, buffer);
00408     return rv;
00409 }
00410 
00411 NS_IMETHODIMP
00412 nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext,
00413                                nsresult aStatus) {
00414     if (NS_SUCCEEDED(aStatus)) {
00415         nsString buffer;
00416         buffer.AssignLiteral("</table><hr/></body></html>\n");
00417 
00418         aStatus = FormatInputStream(request, aContext, buffer);
00419     }
00420 
00421     mParser->OnStopRequest(request, aContext, aStatus);
00422     mParser = 0;
00423     
00424     return mListener->OnStopRequest(request, aContext, aStatus);
00425 }
00426 
00427 nsresult
00428 nsIndexedToHTML::FormatInputStream(nsIRequest* aRequest, nsISupports *aContext, const nsAString &aBuffer) 
00429 {
00430     nsresult rv = NS_OK;
00431 
00432     // set up unicode encoder
00433     if (!mUnicodeEncoder) {
00434       nsXPIDLCString encoding;
00435       rv = mParser->GetEncoding(getter_Copies(encoding));
00436       if (NS_SUCCEEDED(rv)) {
00437         nsCOMPtr<nsICharsetConverterManager> charsetConverterManager;
00438         charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00439         rv = charsetConverterManager->GetUnicodeEncoder(encoding.get(), 
00440                                                           getter_AddRefs(mUnicodeEncoder));
00441         if (NS_SUCCEEDED(rv))
00442             rv = mUnicodeEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, 
00443                                                        nsnull, (PRUnichar)'?');
00444       }
00445     }
00446 
00447     // convert the data with unicode encoder
00448     char *buffer = nsnull;
00449     PRInt32 dstLength;
00450     if (NS_SUCCEEDED(rv)) {
00451       PRInt32 unicharLength = aBuffer.Length();
00452       rv = mUnicodeEncoder->GetMaxLength(PromiseFlatString(aBuffer).get(), 
00453                                          unicharLength, &dstLength);
00454       if (NS_SUCCEEDED(rv)) {
00455         buffer = (char *) nsMemory::Alloc(dstLength);
00456         NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
00457 
00458         rv = mUnicodeEncoder->Convert(PromiseFlatString(aBuffer).get(), &unicharLength, 
00459                                       buffer, &dstLength);
00460         if (NS_SUCCEEDED(rv)) {
00461           PRInt32 finLen = 0;
00462           rv = mUnicodeEncoder->Finish(buffer + dstLength, &finLen);
00463           if (NS_SUCCEEDED(rv))
00464             dstLength += finLen;
00465         }
00466       }
00467     }
00468 
00469     // if conversion error then fallback to UTF-8
00470     if (NS_FAILED(rv)) {
00471       rv = NS_OK;
00472       if (buffer) {
00473         nsMemory::Free(buffer);
00474         buffer = nsnull;
00475       }
00476     }
00477 
00478     nsCOMPtr<nsIInputStream> inputData;
00479     if (buffer) {
00480       rv = NS_NewCStringInputStream(getter_AddRefs(inputData), Substring(buffer, buffer + dstLength));
00481       nsMemory::Free(buffer);
00482       NS_ENSURE_SUCCESS(rv, rv);
00483       rv = mListener->OnDataAvailable(aRequest, aContext,
00484                                       inputData, 0, dstLength);
00485     }
00486     else {
00487       NS_ConvertUCS2toUTF8 utf8Buffer(aBuffer);
00488       rv = NS_NewCStringInputStream(getter_AddRefs(inputData), utf8Buffer);
00489       NS_ENSURE_SUCCESS(rv, rv);
00490       rv = mListener->OnDataAvailable(aRequest, aContext,
00491                                       inputData, 0, utf8Buffer.Length());
00492     }
00493     return (rv);
00494 }
00495 
00496 NS_IMETHODIMP
00497 nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest,
00498                                  nsISupports *aCtxt,
00499                                  nsIInputStream* aInput,
00500                                  PRUint32 aOffset,
00501                                  PRUint32 aCount) {
00502     return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount);
00503 }
00504 
00505 // This defines the number of rows we are going to have per table
00506 // splitting this up makes things faster, by helping layout
00507 #define ROWS_PER_TABLE 250
00508 
00509 NS_IMETHODIMP
00510 nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
00511                                   nsISupports *aCtxt,
00512                                   nsIDirIndex *aIndex) {
00513     nsresult rv;
00514     if (!aIndex)
00515         return NS_ERROR_NULL_POINTER;
00516 
00517     nsString pushBuffer;
00518     pushBuffer.AppendLiteral("<tr>\n <td><a");
00519 
00520     PRUint32 type;
00521     aIndex->GetType(&type);
00522     if (type == nsIDirIndex::TYPE_SYMLINK) {
00523         pushBuffer.AppendLiteral(" class=\"symlink\"");
00524     }
00525 
00526     pushBuffer.AppendLiteral(" href=\"");
00527 
00528     nsXPIDLCString loc;
00529     aIndex->GetLocation(getter_Copies(loc));
00530 
00531     if (!mTextToSubURI) {
00532         mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
00533         if (NS_FAILED(rv)) return rv;
00534     }
00535 
00536     nsXPIDLCString encoding;
00537     rv = mParser->GetEncoding(getter_Copies(encoding));
00538     if (NS_FAILED(rv)) return rv;
00539 
00540     nsXPIDLString unEscapeSpec;
00541     rv = mTextToSubURI->UnEscapeAndConvert(encoding, loc,
00542                                            getter_Copies(unEscapeSpec));
00543     if (NS_FAILED(rv)) return rv;
00544 
00545     // need to escape links
00546     nsCAutoString escapeBuf;
00547 
00548     NS_ConvertUCS2toUTF8 utf8UnEscapeSpec(unEscapeSpec);
00549 
00550     // now minimally re-escape the location...
00551     PRUint32 escFlags;
00552     // for some protocols, like gopher, we expect the location to be absolute.
00553     // if so, and if the location indeed appears to be a valid URI, then go
00554     // ahead and treat it like one.
00555     if (mExpectAbsLoc &&
00556         NS_SUCCEEDED(net_ExtractURLScheme(utf8UnEscapeSpec, nsnull, nsnull, nsnull))) {
00557         // escape as absolute 
00558         escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal;
00559     }
00560     else {
00561         // escape as relative
00562         escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon;
00563     }
00564     NS_EscapeURL(utf8UnEscapeSpec.get(), utf8UnEscapeSpec.Length(), escFlags, escapeBuf);
00565     NS_ConvertUTF8toUTF16 utf16URI(escapeBuf);
00566     nsString htmlEscapedURL;
00567     htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length()));
00568     pushBuffer.Append(htmlEscapedURL);
00569   
00570     pushBuffer.AppendLiteral("\"><img src=\"");
00571 
00572     switch (type) {
00573     case nsIDirIndex::TYPE_DIRECTORY:
00574     case nsIDirIndex::TYPE_SYMLINK:
00575         pushBuffer.AppendLiteral("resource://gre/res/html/gopher-menu.gif\" alt=\"Directory: ");
00576         break;
00577     case nsIDirIndex::TYPE_FILE:
00578     case nsIDirIndex::TYPE_UNKNOWN:
00579         pushBuffer.AppendLiteral("resource://gre/res/html/gopher-unknown.gif\" alt=\"File: ");
00580         break;
00581     }
00582     pushBuffer.AppendLiteral("\"/>");
00583 
00584     nsXPIDLString tmp;
00585     aIndex->GetDescription(getter_Copies(tmp));
00586     PRUnichar* escaped = nsEscapeHTML2(tmp.get(), tmp.Length());
00587     pushBuffer.Append(escaped);
00588     nsMemory::Free(escaped);
00589 
00590     pushBuffer.AppendLiteral("</a></td>\n <td>");
00591 
00592     PRInt64 size;
00593     aIndex->GetSize(&size);
00594     
00595     if (nsUint64(PRUint64(size)) != nsUint64(LL_MAXUINT) &&
00596         type != nsIDirIndex::TYPE_DIRECTORY &&
00597         type != nsIDirIndex::TYPE_SYMLINK) {
00598         nsAutoString  sizeString;
00599         FormatSizeString(size, sizeString);
00600         pushBuffer.Append(sizeString);
00601     }
00602 
00603     pushBuffer.AppendLiteral("</td>\n <td>");
00604 
00605     PRTime t;
00606     aIndex->GetLastModified(&t);
00607 
00608     if (t == -1) {
00609         pushBuffer.AppendLiteral("</td>\n <td>");
00610     } else {
00611         nsAutoString formatted;
00612         nsAutoString strNCR;    // use NCR to show date in any doc charset
00613         mDateTime->FormatPRTime(nsnull,
00614                                 kDateFormatShort,
00615                                 kTimeFormatNone,
00616                                 t,
00617                                 formatted);
00618         ConvertNonAsciiToNCR(formatted, strNCR);
00619         pushBuffer.Append(strNCR);
00620         pushBuffer.AppendLiteral("</td>\n <td>");
00621         mDateTime->FormatPRTime(nsnull,
00622                                 kDateFormatNone,
00623                                 kTimeFormatSeconds,
00624                                 t,
00625                                 formatted);
00626         ConvertNonAsciiToNCR(formatted, strNCR);
00627         pushBuffer.Append(strNCR);
00628     }
00629 
00630     pushBuffer.AppendLiteral("</td>\n</tr>\n");
00631 
00632     // Split this up to avoid slow layout performance with large tables
00633     // - bug 85381
00634     if (++mRowCount > ROWS_PER_TABLE) {
00635         pushBuffer.AppendLiteral("</table>\n<table>\n");
00636         mRowCount = 0;
00637     }
00638     
00639     return FormatInputStream(aRequest, aCtxt, pushBuffer);
00640 }
00641 
00642 NS_IMETHODIMP
00643 nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest,
00644                                         nsISupports *aCtxt,
00645                                         const nsAString& aInfo) {
00646     nsAutoString pushBuffer;
00647     PRUnichar* escaped = nsEscapeHTML2(PromiseFlatString(aInfo).get());
00648     if (!escaped)
00649         return NS_ERROR_OUT_OF_MEMORY;
00650     pushBuffer.AppendLiteral("<tr>\n <td>");
00651     pushBuffer.Append(escaped);
00652     nsMemory::Free(escaped);
00653     pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n");
00654     
00655     // Split this up to avoid slow layout performance with large tables
00656     // - bug 85381
00657     if (++mRowCount > ROWS_PER_TABLE) {
00658         pushBuffer.AppendLiteral("</table>\n<table>\n");
00659         mRowCount = 0;
00660     }   
00661     return FormatInputStream(aRequest, aCtxt, pushBuffer);
00662 }
00663 
00664 void nsIndexedToHTML::FormatSizeString(PRInt64 inSize, nsString& outSizeString)
00665 {
00666     nsInt64 size(inSize);
00667     outSizeString.Truncate();
00668     if (size > nsInt64(0)) {
00669         // round up to the nearest Kilobyte
00670         PRInt64  upperSize = (size + nsInt64(1023)) / nsInt64(1024);
00671         outSizeString.AppendInt(upperSize);
00672         outSizeString.AppendLiteral(" KB");
00673     }
00674 }
00675 
00676 nsIndexedToHTML::nsIndexedToHTML() {
00677 }
00678 
00679 nsIndexedToHTML::~nsIndexedToHTML() {
00680 }