michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIndexedToHTML.h" michael@0: #include "mozilla/dom/EncodingUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "netCore.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsEscape.h" michael@0: #include "nsIDirIndex.h" michael@0: #include "nsDateTimeFormatCID.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsIPlatformCharset.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefLocalizedString.h" michael@0: #include "nsIChromeRegistry.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsIDateTimeFormat.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsITextToSubURI.h" michael@0: #include "nsXPIDLString.h" michael@0: #include michael@0: michael@0: NS_IMPL_ISUPPORTS(nsIndexedToHTML, michael@0: nsIDirIndexListener, michael@0: nsIStreamConverter, michael@0: nsIRequestObserver, michael@0: nsIStreamListener) michael@0: michael@0: static void AppendNonAsciiToNCR(const nsAString& in, nsCString& out) michael@0: { michael@0: nsAString::const_iterator start, end; michael@0: michael@0: in.BeginReading(start); michael@0: in.EndReading(end); michael@0: michael@0: while (start != end) { michael@0: if (*start < 128) { michael@0: out.Append(*start++); michael@0: } else { michael@0: out.AppendLiteral("&#x"); michael@0: out.AppendInt(*start++, 16); michael@0: out.Append(';'); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { michael@0: nsresult rv; michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsIndexedToHTML* _s = new nsIndexedToHTML(); michael@0: if (_s == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = _s->QueryInterface(aIID, aResult); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsIndexedToHTML::Init(nsIStreamListener* aListener) { michael@0: nsresult rv = NS_OK; michael@0: michael@0: mListener = aListener; michael@0: michael@0: mDateTime = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr sbs = michael@0: do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle)); michael@0: michael@0: mExpectAbsLoc = false; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::Convert(nsIInputStream* aFromStream, michael@0: const char* aFromType, michael@0: const char* aToType, michael@0: nsISupports* aCtxt, michael@0: nsIInputStream** res) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::AsyncConvertData(const char *aFromType, michael@0: const char *aToType, michael@0: nsIStreamListener *aListener, michael@0: nsISupports *aCtxt) { michael@0: return Init(aListener); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) { michael@0: nsCString buffer; michael@0: nsresult rv = DoOnStartRequest(request, aContext, buffer); michael@0: if (NS_FAILED(rv)) { michael@0: request->Cancel(rv); michael@0: } michael@0: michael@0: rv = mListener->OnStartRequest(request, aContext); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // The request may have been canceled, and if that happens, we want to michael@0: // suppress calls to OnDataAvailable. michael@0: request->GetStatus(&rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Push our buffer to the listener. michael@0: michael@0: rv = SendToListener(request, aContext, buffer); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext, michael@0: nsCString& aBuffer) { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr channel = do_QueryInterface(request); michael@0: nsCOMPtr uri; michael@0: rv = channel->GetURI(getter_AddRefs(uri)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: channel->SetContentType(NS_LITERAL_CSTRING("text/html")); michael@0: michael@0: mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mParser->SetListener(this); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mParser->OnStartRequest(request, aContext); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString baseUri, titleUri; michael@0: rv = uri->GetAsciiSpec(baseUri); michael@0: if (NS_FAILED(rv)) return rv; michael@0: titleUri = baseUri; michael@0: michael@0: nsCString parentStr; michael@0: michael@0: nsCString buffer; michael@0: buffer.AppendLiteral("\n\n\n"); michael@0: michael@0: // XXX - should be using the 300: line from the parser. michael@0: // We can't guarantee that that comes before any entry, so we'd have to michael@0: // buffer, and do other painful stuff. michael@0: // I'll deal with this when I make the changes to handle welcome messages michael@0: // The .. stuff should also come from the lower level protocols, but that michael@0: // would muck up the XUL display michael@0: // - bbaetz michael@0: michael@0: bool isScheme = false; michael@0: bool isSchemeFile = false; michael@0: if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) { michael@0: michael@0: // strip out the password here, so it doesn't show in the page title michael@0: // This is done by the 300: line generation in ftp, but we don't use michael@0: // that - see above michael@0: michael@0: nsAutoCString pw; michael@0: rv = uri->GetPassword(pw); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (!pw.IsEmpty()) { michael@0: nsCOMPtr newUri; michael@0: rv = uri->Clone(getter_AddRefs(newUri)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: rv = newUri->SetPassword(EmptyCString()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: rv = newUri->GetAsciiSpec(titleUri); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: nsAutoCString path; michael@0: rv = uri->GetPath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) { michael@0: rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) { michael@0: nsCOMPtr fileUrl = do_QueryInterface(uri); michael@0: nsCOMPtr file; michael@0: rv = fileUrl->GetFile(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: file->SetFollowLinks(true); michael@0: michael@0: nsAutoCString url; michael@0: rv = net_GetURLSpecFromFile(file, url); michael@0: if (NS_FAILED(rv)) return rv; michael@0: baseUri.Assign(url); michael@0: michael@0: nsCOMPtr parent; michael@0: rv = file->GetParent(getter_AddRefs(parent)); michael@0: michael@0: if (parent && NS_SUCCEEDED(rv)) { michael@0: net_GetURLSpecFromDir(parent, url); michael@0: if (NS_FAILED(rv)) return rv; michael@0: parentStr.Assign(url); michael@0: } michael@0: michael@0: // Directory index will be always encoded in UTF-8 if this is file url michael@0: buffer.AppendLiteral("\n"); michael@0: michael@0: } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) { michael@0: nsAutoCString path; michael@0: rv = uri->GetPath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // a top-level jar directory URL is of the form jar:foo.zip!/ michael@0: // path will be of the form foo.zip!/, and its last two characters michael@0: // will be "!/" michael@0: //XXX this won't work correctly when the name of the directory being michael@0: //XXX displayed ends with "!", but then again, jar: URIs don't deal michael@0: //XXX particularly well with such directories anyway michael@0: if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) { michael@0: rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: else { michael@0: // default behavior for other protocols is to assume the channel's michael@0: // URL references a directory ending in '/' -- fixup if necessary. michael@0: nsAutoCString path; michael@0: rv = uri->GetPath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (baseUri.Last() != '/') { michael@0: baseUri.Append('/'); michael@0: path.Append('/'); michael@0: uri->SetPath(path); michael@0: } michael@0: if (!path.EqualsLiteral("/")) { michael@0: rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: michael@0: buffer.AppendLiteral("\n" michael@0: "\n" michael@0: "\n"); michael@0: michael@0: buffer.AppendLiteral(" innerUri = NS_GetInnermostURI(uri); michael@0: if (!innerUri) michael@0: return NS_ERROR_UNEXPECTED; michael@0: nsCOMPtr fileURL(do_QueryInterface(innerUri)); michael@0: //XXX bug 388553: can't use skinnable icons here due to security restrictions michael@0: if (fileURL) { michael@0: //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png"); michael@0: buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB" michael@0: "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i" michael@0: "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR" michael@0: "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj" michael@0: "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O" michael@0: "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ" michael@0: "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG" michael@0: "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5" michael@0: "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS" michael@0: "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c" michael@0: "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU" michael@0: "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF" michael@0: "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi" michael@0: "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l" michael@0: "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M" michael@0: "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI" michael@0: "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM" michael@0: "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B" michael@0: "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8" michael@0: "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D"); michael@0: } else { michael@0: //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png"); michael@0: buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB" michael@0: "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i" michael@0: "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft" michael@0: "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%" michael@0: "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0" michael@0: "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE" michael@0: "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM" michael@0: "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR" michael@0: "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh" michael@0: "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz" michael@0: "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj" michael@0: "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1" michael@0: "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9" michael@0: "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr" michael@0: "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E" michael@0: "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2" michael@0: "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS" michael@0: "YAAAAABJRU5ErkJggg%3D%3D"); michael@0: } michael@0: buffer.AppendLiteral("\">\n"); michael@0: michael@0: // Everything needs to end in a /, michael@0: // otherwise we end up linking to file:///foo/dirfile michael@0: michael@0: if (!mTextToSubURI) { michael@0: mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: nsXPIDLCString encoding; michael@0: rv = uri->GetOriginCharset(encoding); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (encoding.IsEmpty()) { michael@0: encoding.AssignLiteral("UTF-8"); michael@0: } michael@0: michael@0: nsXPIDLString unEscapeSpec; michael@0: rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(), michael@0: getter_Copies(unEscapeSpec)); michael@0: // unescape may fail because michael@0: // 1. file URL may be encoded in platform charset for backward compatibility michael@0: // 2. query part may not be encoded in UTF-8 (see bug 261929) michael@0: // so try the platform's default if this is file url michael@0: if (NS_FAILED(rv) && isSchemeFile) { michael@0: nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsAutoCString charset; michael@0: rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(), michael@0: getter_Copies(unEscapeSpec)); michael@0: } michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsXPIDLString htmlEscSpec; michael@0: htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(), michael@0: unEscapeSpec.Length())); michael@0: michael@0: nsXPIDLString title; michael@0: const char16_t* formatTitle[] = { michael@0: htmlEscSpec.get() michael@0: }; michael@0: michael@0: rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"), michael@0: formatTitle, michael@0: sizeof(formatTitle)/sizeof(char16_t*), michael@0: getter_Copies(title)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // we want to convert string bundle to NCR michael@0: // to ensure they're shown in any charsets michael@0: AppendNonAsciiToNCR(title, buffer); michael@0: michael@0: buffer.AppendLiteral("\n"); michael@0: michael@0: // If there is a quote character in the baseUri, then michael@0: // lets not add a base URL. The reason for this is that michael@0: // if we stick baseUri containing a quote into a quoted michael@0: // string, the quote character will prematurely close michael@0: // the base href string. This is a fall-back check; michael@0: // that's why it is OK to not use a base rather than michael@0: // trying to play nice and escaping the quotes. See bug michael@0: // 358128. michael@0: michael@0: if (baseUri.FindChar('"') == kNotFound) michael@0: { michael@0: // Great, the baseUri does not contain a char that michael@0: // will prematurely close the string. Go ahead an michael@0: // add a base href. michael@0: buffer.AppendLiteral("\n"); michael@0: } michael@0: else michael@0: { michael@0: NS_ERROR("broken protocol handler didn't escape double-quote."); michael@0: } michael@0: michael@0: nsCString direction(NS_LITERAL_CSTRING("ltr")); michael@0: nsCOMPtr reg = michael@0: mozilla::services::GetXULChromeRegistryService(); michael@0: if (reg) { michael@0: bool isRTL = false; michael@0: reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL); michael@0: if (isRTL) { michael@0: direction.AssignLiteral("rtl"); michael@0: } michael@0: } michael@0: michael@0: buffer.AppendLiteral("\n\n

"); michael@0: michael@0: const char16_t* formatHeading[] = { michael@0: htmlEscSpec.get() michael@0: }; michael@0: michael@0: rv = mBundle->FormatStringFromName(MOZ_UTF16("DirTitle"), michael@0: formatHeading, michael@0: sizeof(formatHeading)/sizeof(char16_t*), michael@0: getter_Copies(title)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: AppendNonAsciiToNCR(title, buffer); michael@0: buffer.AppendLiteral("

\n"); michael@0: michael@0: if (!parentStr.IsEmpty()) { michael@0: nsXPIDLString parentText; michael@0: rv = mBundle->GetStringFromName(MOZ_UTF16("DirGoUp"), michael@0: getter_Copies(parentText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: buffer.AppendLiteral("

"); michael@0: AppendNonAsciiToNCR(parentText, buffer); michael@0: buffer.AppendLiteral("

\n"); michael@0: } michael@0: michael@0: if (isSchemeFile) { michael@0: nsXPIDLString showHiddenText; michael@0: rv = mBundle->GetStringFromName(MOZ_UTF16("ShowHidden"), michael@0: getter_Copies(showHiddenText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: buffer.AppendLiteral("

\n"); michael@0: } michael@0: michael@0: buffer.AppendLiteral("\n"); michael@0: michael@0: nsXPIDLString columnText; michael@0: michael@0: buffer.AppendLiteral(" \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n"); michael@0: buffer.AppendLiteral(" \n"); michael@0: michael@0: aBuffer = buffer; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext, michael@0: nsresult aStatus) { michael@0: if (NS_SUCCEEDED(aStatus)) { michael@0: nsCString buffer; michael@0: buffer.AssignLiteral("
"); michael@0: michael@0: rv = mBundle->GetStringFromName(MOZ_UTF16("DirColName"), michael@0: getter_Copies(columnText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: AppendNonAsciiToNCR(columnText, buffer); michael@0: buffer.AppendLiteral(""); michael@0: michael@0: rv = mBundle->GetStringFromName(MOZ_UTF16("DirColSize"), michael@0: getter_Copies(columnText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: AppendNonAsciiToNCR(columnText, buffer); michael@0: buffer.AppendLiteral(""); michael@0: michael@0: rv = mBundle->GetStringFromName(MOZ_UTF16("DirColMTime"), michael@0: getter_Copies(columnText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: AppendNonAsciiToNCR(columnText, buffer); michael@0: buffer.AppendLiteral("
\n"); michael@0: michael@0: aStatus = SendToListener(request, aContext, buffer); michael@0: } michael@0: michael@0: mParser->OnStopRequest(request, aContext, aStatus); michael@0: mParser = 0; michael@0: michael@0: return mListener->OnStopRequest(request, aContext, aStatus); michael@0: } michael@0: michael@0: nsresult michael@0: nsIndexedToHTML::SendToListener(nsIRequest* aRequest, nsISupports *aContext, const nsACString &aBuffer) michael@0: { michael@0: nsCOMPtr inputData; michael@0: nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputData), aBuffer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return mListener->OnDataAvailable(aRequest, aContext, michael@0: inputData, 0, aBuffer.Length()); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aCtxt, michael@0: nsIInputStream* aInput, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) { michael@0: return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, michael@0: nsISupports *aCtxt, michael@0: nsIDirIndex *aIndex) { michael@0: nsresult rv; michael@0: if (!aIndex) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsCString pushBuffer; michael@0: pushBuffer.AppendLiteral("GetLocation(getter_Copies(loc)); michael@0: michael@0: // Adjust the length in case unescaping shortened the string. michael@0: loc.Truncate(nsUnescapeCount(loc.BeginWriting())); michael@0: if (loc.First() == PRUnichar('.')) michael@0: pushBuffer.AppendLiteral(" class=\"hidden-object\""); michael@0: michael@0: pushBuffer.AppendLiteral(">\n GetType(&type); michael@0: switch (type) { michael@0: case nsIDirIndex::TYPE_SYMLINK: michael@0: pushBuffer.Append('0'); michael@0: break; michael@0: case nsIDirIndex::TYPE_DIRECTORY: michael@0: pushBuffer.Append('1'); michael@0: break; michael@0: default: michael@0: pushBuffer.Append('2'); michael@0: break; michael@0: } michael@0: nsAdoptingCString escaped(nsEscapeHTML(loc)); michael@0: pushBuffer.Append(escaped); michael@0: michael@0: pushBuffer.AppendLiteral("\">
"); michael@0: michael@0: if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { michael@0: pushBuffer.AppendLiteral("\"");GetStringFromName(MOZ_UTF16("DirFileLabel"), michael@0: getter_Copies(altText)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: AppendNonAsciiToNCR(altText, pushBuffer); michael@0: pushBuffer.AppendLiteral("\">"); michael@0: } michael@0: michael@0: pushBuffer.Append(escaped); michael@0: pushBuffer.AppendLiteral("
\n "); michael@0: } else { michael@0: int64_t size; michael@0: aIndex->GetSize(&size); michael@0: michael@0: if (uint64_t(size) != UINT64_MAX) { michael@0: pushBuffer.AppendLiteral(" sortable-data=\""); michael@0: pushBuffer.AppendInt(size); michael@0: pushBuffer.AppendLiteral("\">"); michael@0: nsAutoCString sizeString; michael@0: FormatSizeString(size, sizeString); michael@0: pushBuffer.Append(sizeString); michael@0: } else { michael@0: pushBuffer.AppendLiteral(">"); michael@0: } michael@0: } michael@0: pushBuffer.AppendLiteral("\n GetLastModified(&t); michael@0: michael@0: if (t == -1) { michael@0: pushBuffer.AppendLiteral(">\n "); michael@0: } else { michael@0: pushBuffer.AppendLiteral(" sortable-data=\""); michael@0: pushBuffer.AppendInt(static_cast(t)); michael@0: pushBuffer.AppendLiteral("\">"); michael@0: nsAutoString formatted; michael@0: mDateTime->FormatPRTime(nullptr, michael@0: kDateFormatShort, michael@0: kTimeFormatNone, michael@0: t, michael@0: formatted); michael@0: AppendNonAsciiToNCR(formatted, pushBuffer); michael@0: pushBuffer.AppendLiteral("\n "); michael@0: mDateTime->FormatPRTime(nullptr, michael@0: kDateFormatNone, michael@0: kTimeFormatSeconds, michael@0: t, michael@0: formatted); michael@0: // use NCR to show date in any doc charset michael@0: AppendNonAsciiToNCR(formatted, pushBuffer); michael@0: } michael@0: michael@0: pushBuffer.AppendLiteral("\n"); michael@0: michael@0: return SendToListener(aRequest, aCtxt, pushBuffer); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest, michael@0: nsISupports *aCtxt, michael@0: const nsAString& aInfo) { michael@0: nsAutoCString pushBuffer; michael@0: nsAdoptingString escaped(nsEscapeHTML2(PromiseFlatString(aInfo).get())); michael@0: if (!escaped) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: pushBuffer.AppendLiteral("\n "); michael@0: // escaped is provided in Unicode, so write hex NCRs as necessary michael@0: // to prevent the HTML parser from applying a character set. michael@0: AppendNonAsciiToNCR(escaped, pushBuffer); michael@0: pushBuffer.AppendLiteral("\n \n \n \n\n"); michael@0: michael@0: return SendToListener(aRequest, aCtxt, pushBuffer); michael@0: } michael@0: michael@0: void nsIndexedToHTML::FormatSizeString(int64_t inSize, nsCString& outSizeString) michael@0: { michael@0: outSizeString.Truncate(); michael@0: if (inSize > int64_t(0)) { michael@0: // round up to the nearest Kilobyte michael@0: int64_t upperSize = (inSize + int64_t(1023)) / int64_t(1024); michael@0: outSizeString.AppendInt(upperSize); michael@0: outSizeString.AppendLiteral(" KB"); michael@0: } michael@0: } michael@0: michael@0: nsIndexedToHTML::nsIndexedToHTML() { michael@0: } michael@0: michael@0: nsIndexedToHTML::~nsIndexedToHTML() { michael@0: }