NS_IMETHODIMP nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext, nsresult aStatus) { if (NS_SUCCEEDED(aStatus)) { nsString buffer; buffer.AssignLiteral("</tbody></table></body></html>\n"); aStatus = FormatInputStream(request, aContext, buffer); } mParser->OnStopRequest(request, aContext, aStatus); mParser = 0; return mListener->OnStopRequest(request, aContext, aStatus); }
NS_IMETHODIMP nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest, nsISupports *aCtxt, const nsAString& aInfo) { nsAutoString pushBuffer; PRUnichar* escaped = nsEscapeHTML2(PromiseFlatString(aInfo).get()); if (!escaped) return NS_ERROR_OUT_OF_MEMORY; pushBuffer.AppendLiteral("<tr>\n <td>"); pushBuffer.Append(escaped); nsMemory::Free(escaped); pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n"); return FormatInputStream(aRequest, aCtxt, pushBuffer); }
NS_IMETHODIMP nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) { nsString buffer; nsresult rv = DoOnStartRequest(request, aContext, buffer); if (NS_FAILED(rv)) { request->Cancel(rv); } rv = mListener->OnStartRequest(request, aContext); if (NS_FAILED(rv)) return rv; // The request may have been canceled, and if that happens, we want to // suppress calls to OnDataAvailable. request->GetStatus(&rv); if (NS_FAILED(rv)) return rv; // Push our buffer to the listener. rv = FormatInputStream(request, aContext, buffer); return rv; }
NS_IMETHODIMP nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, nsISupports *aCtxt, nsIDirIndex *aIndex) { nsresult rv; if (!aIndex) return NS_ERROR_NULL_POINTER; nsString pushBuffer; pushBuffer.AppendLiteral("<tr"); nsXPIDLString description; aIndex->GetDescription(getter_Copies(description)); if (description.First() == PRUnichar('.')) pushBuffer.AppendLiteral(" class=\"hidden-object\""); pushBuffer.AppendLiteral(">\n <td sortable-data=\""); // The sort key is the name of the item, prepended by either 0, 1 or 2 // in order to group items. PRUint32 type; aIndex->GetType(&type); switch (type) { case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendInt(0); break; case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendInt(1); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendInt(2); break; } PRUnichar* escaped = nsEscapeHTML2(description.get(), description.Length()); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\"><a class=\""); switch (type) { case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendLiteral("dir"); break; case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendLiteral("symlink"); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendLiteral("file"); break; } pushBuffer.AppendLiteral("\""); // Truncate long names to not stretch the table //XXX this should be left to the stylesheet (bug 391471) nsString escapedShort; if (description.Length() > 71) { nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); nsCOMPtr<nsIURI> uri; rv = channel->GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; //XXX this potentially truncates after a combining char (bug 391472) nsXPIDLString descriptionAffix; descriptionAffix.Assign(description); descriptionAffix.Cut(0, descriptionAffix.Length() - 25); if (NS_IS_LOW_SURROGATE(descriptionAffix.First())) descriptionAffix.Cut(0, 1); description.Truncate(NS_MIN<PRUint32>(71, description.Length() - 28)); if (NS_IS_HIGH_SURROGATE(description.Last())) description.Truncate(description.Length() - 1); escapedShort.Adopt(nsEscapeHTML2(description.get(), description.Length())); escapedShort.Append(mEscapedEllipsis); // add ZERO WIDTH SPACE (U+200B) for wrapping escapedShort.AppendLiteral("​"); nsString tmp; tmp.Adopt(nsEscapeHTML2(descriptionAffix.get(), descriptionAffix.Length())); escapedShort.Append(tmp); pushBuffer.AppendLiteral(" title=\""); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\""); } if (escapedShort.IsEmpty()) escapedShort.Assign(escaped); nsMemory::Free(escaped); pushBuffer.AppendLiteral(" href=\""); nsXPIDLCString loc; aIndex->GetLocation(getter_Copies(loc)); if (!mTextToSubURI) { mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; } nsXPIDLCString encoding; rv = mParser->GetEncoding(getter_Copies(encoding)); if (NS_FAILED(rv)) return rv; nsXPIDLString unEscapeSpec; rv = mTextToSubURI->UnEscapeAndConvert(encoding, loc, getter_Copies(unEscapeSpec)); if (NS_FAILED(rv)) return rv; // need to escape links nsCAutoString escapeBuf; NS_ConvertUTF16toUTF8 utf8UnEscapeSpec(unEscapeSpec); // Adding trailing slash helps to recognize whether the URL points to a file // or a directory (bug #214405). if ((type == nsIDirIndex::TYPE_DIRECTORY) && (utf8UnEscapeSpec.Last() != '/')) { utf8UnEscapeSpec.Append('/'); } // now minimally re-escape the location... PRUint32 escFlags; // for some protocols, we expect the location to be absolute. // if so, and if the location indeed appears to be a valid URI, then go // ahead and treat it like one. if (mExpectAbsLoc && NS_SUCCEEDED(net_ExtractURLScheme(utf8UnEscapeSpec, nsnull, nsnull, nsnull))) { // escape as absolute escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal; } else { // escape as relative // esc_Directory is needed because directories have a trailing slash. // Without it, the trailing '/' will be escaped, and links from within // that directory will be incorrect escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory; } NS_EscapeURL(utf8UnEscapeSpec.get(), utf8UnEscapeSpec.Length(), escFlags, escapeBuf); // esc_Directory does not escape the semicolons, so if a filename // contains semicolons we need to manually escape them. // This replacement should be removed in bug #473280 escapeBuf.ReplaceSubstring(";", "%3b"); NS_ConvertUTF8toUTF16 utf16URI(escapeBuf); nsString htmlEscapedURL; htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length())); pushBuffer.Append(htmlEscapedURL); pushBuffer.AppendLiteral("\">"); if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { pushBuffer.AppendLiteral("<img src=\"moz-icon://"); PRInt32 lastDot = escapeBuf.RFindChar('.'); if (lastDot != kNotFound) { escapeBuf.Cut(0, lastDot); NS_ConvertUTF8toUTF16 utf16EscapeBuf(escapeBuf); nsString htmlFileExt; htmlFileExt.Adopt(nsEscapeHTML2(utf16EscapeBuf.get(), utf16EscapeBuf.Length())); pushBuffer.Append(htmlFileExt); } else { pushBuffer.AppendLiteral("unknown"); } pushBuffer.AppendLiteral("?size=16\" alt=\""); nsXPIDLString altText; rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DirFileLabel").get(), getter_Copies(altText)); if (NS_FAILED(rv)) return rv; AppendNonAsciiToNCR(altText, pushBuffer); pushBuffer.AppendLiteral("\">"); } pushBuffer.Append(escapedShort); pushBuffer.AppendLiteral("</a></td>\n <td"); if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) { pushBuffer.AppendLiteral(">"); } else { PRInt64 size; aIndex->GetSize(&size); if (PRUint64(size) != LL_MAXUINT) { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(size); pushBuffer.AppendLiteral("\">"); nsAutoString sizeString; FormatSizeString(size, sizeString); pushBuffer.Append(sizeString); } else { pushBuffer.AppendLiteral(">"); } } pushBuffer.AppendLiteral("</td>\n <td"); PRTime t; aIndex->GetLastModified(&t); if (t == -1) { pushBuffer.AppendLiteral("></td>\n <td>"); } else { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(t); pushBuffer.AppendLiteral("\">"); nsAutoString formatted; mDateTime->FormatPRTime(nsnull, kDateFormatShort, kTimeFormatNone, t, formatted); AppendNonAsciiToNCR(formatted, pushBuffer); pushBuffer.AppendLiteral("</td>\n <td>"); mDateTime->FormatPRTime(nsnull, kDateFormatNone, kTimeFormatSeconds, t, formatted); // use NCR to show date in any doc charset AppendNonAsciiToNCR(formatted, pushBuffer); } pushBuffer.AppendLiteral("</td>\n</tr>"); return FormatInputStream(aRequest, aCtxt, pushBuffer); }
NS_IMETHODIMP nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest, nsISupports *aCtxt, nsIDirIndex *aIndex) { nsresult rv; if (!aIndex) return NS_ERROR_NULL_POINTER; nsString pushBuffer; pushBuffer.AppendLiteral("<tr"); nsXPIDLString description; aIndex->GetDescription(getter_Copies(description)); if (description.First() == char16_t('.')) pushBuffer.AppendLiteral(" class=\"hidden-object\""); pushBuffer.AppendLiteral(">\n <td sortable-data=\""); // The sort key is the name of the item, prepended by either 0, 1 or 2 // in order to group items. uint32_t type; aIndex->GetType(&type); switch (type) { case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendInt(0); break; case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendInt(1); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendInt(2); break; } char16_t* escaped = nsEscapeHTML2(description.get(), description.Length()); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\"><a class=\""); switch (type) { case nsIDirIndex::TYPE_DIRECTORY: pushBuffer.AppendLiteral("dir"); break; case nsIDirIndex::TYPE_SYMLINK: pushBuffer.AppendLiteral("symlink"); break; case nsIDirIndex::TYPE_FILE: case nsIDirIndex::TYPE_UNKNOWN: pushBuffer.AppendLiteral("file"); break; } pushBuffer.AppendLiteral("\""); // Truncate long names to not stretch the table //XXX this should be left to the stylesheet (bug 391471) nsString escapedShort; if (description.Length() > 71) { nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); nsCOMPtr<nsIURI> uri; rv = channel->GetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; //XXX this potentially truncates after a combining char (bug 391472) nsXPIDLString descriptionAffix; descriptionAffix.Assign(description); descriptionAffix.Cut(0, descriptionAffix.Length() - 25); if (NS_IS_LOW_SURROGATE(descriptionAffix.First())) descriptionAffix.Cut(0, 1); description.Truncate(std::min<uint32_t>(71, description.Length() - 28)); if (NS_IS_HIGH_SURROGATE(description.Last())) description.Truncate(description.Length() - 1); escapedShort.Adopt(nsEscapeHTML2(description.get(), description.Length())); escapedShort.Append(mEscapedEllipsis); // add ZERO WIDTH SPACE (U+200B) for wrapping escapedShort.AppendLiteral("​"); nsString tmp; tmp.Adopt(nsEscapeHTML2(descriptionAffix.get(), descriptionAffix.Length())); escapedShort.Append(tmp); pushBuffer.AppendLiteral(" title=\""); pushBuffer.Append(escaped); pushBuffer.AppendLiteral("\""); } if (escapedShort.IsEmpty()) escapedShort.Assign(escaped); nsMemory::Free(escaped); pushBuffer.AppendLiteral(" href=\""); nsXPIDLCString loc; aIndex->GetLocation(getter_Copies(loc)); nsXPIDLCString encoding; rv = mParser->GetEncoding(getter_Copies(encoding)); if (NS_FAILED(rv)) return rv; // Don't byte-to-Unicode conversion here, it is lossy. loc.SetLength(nsUnescapeCount(loc.BeginWriting())); // need to escape links nsAutoCString locEscaped; // Adding trailing slash helps to recognize whether the URL points to a file // or a directory (bug #214405). if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) { loc.Append('/'); } // now minimally re-escape the location... uint32_t escFlags; // for some protocols, we expect the location to be absolute. // if so, and if the location indeed appears to be a valid URI, then go // ahead and treat it like one. if (mExpectAbsLoc && NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) { // escape as absolute escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal; } else { // escape as relative // esc_Directory is needed because directories have a trailing slash. // Without it, the trailing '/' will be escaped, and links from within // that directory will be incorrect escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory; } NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped); // esc_Directory does not escape the semicolons, so if a filename // contains semicolons we need to manually escape them. // This replacement should be removed in bug #473280 locEscaped.ReplaceSubstring(";", "%3b"); nsAutoString utf16URI; if (encoding.EqualsLiteral("UTF-8")) { // Try to convert non-ASCII bytes to Unicode using UTF-8 decoder. nsCOMPtr<nsIUnicodeDecoder> decoder = mozilla::dom::EncodingUtils::DecoderForEncoding("UTF-8"); decoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal); int32_t len = locEscaped.Length(); int32_t outlen = 0; rv = decoder->GetMaxLength(locEscaped.get(), len, &outlen); if (NS_FAILED(rv)) { return rv; } nsAutoArrayPtr<char16_t> outbuf(new char16_t[outlen]); rv = decoder->Convert(locEscaped.get(), &len, outbuf, &outlen); // Use the result only if the sequence is valid as UTF-8. if (rv == NS_OK) { utf16URI.Append(outbuf, outlen); } } if (utf16URI.IsEmpty()) { // Escape all non-ASCII bytes to preserve the raw value. nsAutoCString outstr; NS_EscapeURL(locEscaped, esc_AlwaysCopy | esc_OnlyNonASCII, outstr); CopyASCIItoUTF16(outstr, utf16URI); } nsString htmlEscapedURL; htmlEscapedURL.Adopt(nsEscapeHTML2(utf16URI.get(), utf16URI.Length())); pushBuffer.Append(htmlEscapedURL); pushBuffer.AppendLiteral("\">"); if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) { pushBuffer.AppendLiteral("<img src=\"moz-icon://"); int32_t lastDot = locEscaped.RFindChar('.'); if (lastDot != kNotFound) { locEscaped.Cut(0, lastDot); NS_ConvertUTF8toUTF16 utf16LocEscaped(locEscaped); nsString htmlFileExt; htmlFileExt.Adopt(nsEscapeHTML2(utf16LocEscaped.get(), utf16LocEscaped.Length())); pushBuffer.Append(htmlFileExt); } else { pushBuffer.AppendLiteral("unknown"); } pushBuffer.AppendLiteral("?size=16\" alt=\""); nsXPIDLString altText; rv = mBundle->GetStringFromName(MOZ_UTF16("DirFileLabel"), getter_Copies(altText)); if (NS_FAILED(rv)) return rv; AppendNonAsciiToNCR(altText, pushBuffer); pushBuffer.AppendLiteral("\">"); } pushBuffer.Append(escapedShort); pushBuffer.AppendLiteral("</a></td>\n <td"); if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) { pushBuffer.AppendLiteral(">"); } else { int64_t size; aIndex->GetSize(&size); if (uint64_t(size) != UINT64_MAX) { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(size); pushBuffer.AppendLiteral("\">"); nsAutoString sizeString; FormatSizeString(size, sizeString); pushBuffer.Append(sizeString); } else { pushBuffer.AppendLiteral(">"); } } pushBuffer.AppendLiteral("</td>\n <td"); PRTime t; aIndex->GetLastModified(&t); if (t == -1) { pushBuffer.AppendLiteral("></td>\n <td>"); } else { pushBuffer.AppendLiteral(" sortable-data=\""); pushBuffer.AppendInt(static_cast<int64_t>(t)); pushBuffer.AppendLiteral("\">"); nsAutoString formatted; mDateTime->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatNone, t, formatted); AppendNonAsciiToNCR(formatted, pushBuffer); pushBuffer.AppendLiteral("</td>\n <td>"); mDateTime->FormatPRTime(nullptr, kDateFormatNone, kTimeFormatSeconds, t, formatted); // use NCR to show date in any doc charset AppendNonAsciiToNCR(formatted, pushBuffer); } pushBuffer.AppendLiteral("</td>\n</tr>"); return FormatInputStream(aRequest, aCtxt, pushBuffer); }