123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsIndexedToHTML.h"
- #include "mozilla/dom/EncodingUtils.h"
- #include "nsNetUtil.h"
- #include "netCore.h"
- #include "nsStringStream.h"
- #include "nsIFile.h"
- #include "nsIFileURL.h"
- #include "nsEscape.h"
- #include "nsIDirIndex.h"
- #include "nsURLHelper.h"
- #include "nsIPlatformCharset.h"
- #include "nsIPrefService.h"
- #include "nsIPrefBranch.h"
- #include "nsIPrefLocalizedString.h"
- #include "nsIChromeRegistry.h"
- #include "nsIDateTimeFormat.h"
- #include "nsIStringBundle.h"
- #include "nsITextToSubURI.h"
- #include "nsXPIDLString.h"
- #include <algorithm>
- #include "nsIChannel.h"
- NS_IMPL_ISUPPORTS(nsIndexedToHTML,
- nsIDirIndexListener,
- nsIStreamConverter,
- nsIRequestObserver,
- nsIStreamListener)
- static void AppendNonAsciiToNCR(const nsAString& in, nsCString& out)
- {
- nsAString::const_iterator start, end;
- in.BeginReading(start);
- in.EndReading(end);
- while (start != end) {
- if (*start < 128) {
- out.Append(*start++);
- } else {
- out.AppendLiteral("&#x");
- out.AppendInt(*start++, 16);
- out.Append(';');
- }
- }
- }
- nsresult
- nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
- nsresult rv;
- if (aOuter)
- return NS_ERROR_NO_AGGREGATION;
-
- nsIndexedToHTML* _s = new nsIndexedToHTML();
- if (_s == nullptr)
- return NS_ERROR_OUT_OF_MEMORY;
-
- rv = _s->QueryInterface(aIID, aResult);
- return rv;
- }
- nsresult
- nsIndexedToHTML::Init(nsIStreamListener* aListener) {
- nsresult rv = NS_OK;
- mListener = aListener;
- mDateTime = nsIDateTimeFormat::Create();
- if (!mDateTime)
- return NS_ERROR_FAILURE;
- nsCOMPtr<nsIStringBundleService> sbs =
- do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
- if (NS_FAILED(rv)) return rv;
- rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle));
- mExpectAbsLoc = false;
- return rv;
- }
- NS_IMETHODIMP
- nsIndexedToHTML::Convert(nsIInputStream* aFromStream,
- const char* aFromType,
- const char* aToType,
- nsISupports* aCtxt,
- nsIInputStream** res) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- nsIndexedToHTML::AsyncConvertData(const char *aFromType,
- const char *aToType,
- nsIStreamListener *aListener,
- nsISupports *aCtxt) {
- return Init(aListener);
- }
- NS_IMETHODIMP
- nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) {
- nsCString 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 = SendToListener(request, aContext, buffer);
- return rv;
- }
- nsresult
- nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
- nsCString& aBuffer) {
- nsresult rv;
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
- nsCOMPtr<nsIURI> uri;
- rv = channel->GetURI(getter_AddRefs(uri));
- if (NS_FAILED(rv)) return rv;
- channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
- mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv);
- if (NS_FAILED(rv)) return rv;
- rv = mParser->SetListener(this);
- if (NS_FAILED(rv)) return rv;
-
- rv = mParser->OnStartRequest(request, aContext);
- if (NS_FAILED(rv)) return rv;
- nsAutoCString baseUri, titleUri;
- rv = uri->GetAsciiSpec(baseUri);
- if (NS_FAILED(rv)) return rv;
- nsCOMPtr<nsIURI> titleURL;
- rv = uri->Clone(getter_AddRefs(titleURL));
- if (NS_FAILED(rv)) titleURL = uri;
- rv = titleURL->SetQuery(EmptyCString());
- if (NS_FAILED(rv)) titleURL = uri;
- rv = titleURL->SetRef(EmptyCString());
- if (NS_FAILED(rv)) titleURL = uri;
- nsCString parentStr;
- nsCString buffer;
- buffer.AppendLiteral("<!DOCTYPE html>\n<html>\n<head>\n");
- // XXX - should be using the 300: line from the parser.
- // We can't guarantee that that comes before any entry, so we'd have to
- // buffer, and do other painful stuff.
- // I'll deal with this when I make the changes to handle welcome messages
- // The .. stuff should also come from the lower level protocols, but that
- // would muck up the XUL display
- // - bbaetz
- bool isScheme = false;
- bool isSchemeFile = false;
- if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) {
- // strip out the password here, so it doesn't show in the page title
- // This is done by the 300: line generation in ftp, but we don't use
- // that - see above
-
- nsAutoCString pw;
- rv = titleURL->GetPassword(pw);
- if (NS_FAILED(rv)) return rv;
- if (!pw.IsEmpty()) {
- nsCOMPtr<nsIURI> newUri;
- rv = titleURL->Clone(getter_AddRefs(newUri));
- if (NS_FAILED(rv)) return rv;
- rv = newUri->SetPassword(EmptyCString());
- if (NS_FAILED(rv)) return rv;
- }
- nsAutoCString path;
- rv = uri->GetPath(path);
- if (NS_FAILED(rv)) return rv;
- if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) {
- rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr);
- if (NS_FAILED(rv)) return rv;
- }
- } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) {
- nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
- nsCOMPtr<nsIFile> file;
- rv = fileUrl->GetFile(getter_AddRefs(file));
- if (NS_FAILED(rv)) return rv;
- file->SetFollowLinks(true);
-
- nsAutoCString url;
- rv = net_GetURLSpecFromFile(file, url);
- if (NS_FAILED(rv)) return rv;
- baseUri.Assign(url);
-
- nsCOMPtr<nsIFile> parent;
- rv = file->GetParent(getter_AddRefs(parent));
-
- if (parent && NS_SUCCEEDED(rv)) {
- net_GetURLSpecFromDir(parent, url);
- if (NS_FAILED(rv)) return rv;
- parentStr.Assign(url);
- }
- // Directory index will be always encoded in UTF-8 if this is file url
- buffer.AppendLiteral("<meta charset=\"UTF-8\">\n");
- } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) {
- nsAutoCString path;
- rv = uri->GetPath(path);
- if (NS_FAILED(rv)) return rv;
- // a top-level jar directory URL is of the form jar:foo.zip!/
- // path will be of the form foo.zip!/, and its last two characters
- // will be "!/"
- //XXX this won't work correctly when the name of the directory being
- //XXX displayed ends with "!", but then again, jar: URIs don't deal
- //XXX particularly well with such directories anyway
- if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) {
- rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
- if (NS_FAILED(rv)) return rv;
- }
- }
- else {
- // default behavior for other protocols is to assume the channel's
- // URL references a directory ending in '/' -- fixup if necessary.
- nsAutoCString path;
- rv = uri->GetPath(path);
- if (NS_FAILED(rv)) return rv;
- if (baseUri.Last() != '/') {
- baseUri.Append('/');
- path.Append('/');
- uri->SetPath(path);
- }
- if (!path.EqualsLiteral("/")) {
- rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
- if (NS_FAILED(rv)) return rv;
- }
- }
- rv = titleURL->GetAsciiSpec(titleUri);
- if (NS_FAILED(rv)) {
- return rv;
- }
- buffer.AppendLiteral("<style type=\"text/css\">\n"
- ":root {\n"
- " font-family: sans-serif;\n"
- "}\n"
- "img {\n"
- " border: 0;\n"
- "}\n"
- "th {\n"
- " text-align: start;\n"
- " white-space: nowrap;\n"
- "}\n"
- "th > a {\n"
- " color: inherit;\n"
- "}\n"
- "table[order] > thead > tr > th {\n"
- " cursor: pointer;\n"
- "}\n"
- "table[order] > thead > tr > th::after {\n"
- " display: none;\n"
- " width: .8em;\n"
- " margin-inline-end: -.8em;\n"
- " text-align: end;\n"
- "}\n"
- "table[order=\"asc\"] > thead > tr > th::after {\n"
- " content: \"\\2193\"; /* DOWNWARDS ARROW (U+2193) */\n"
- "}\n"
- "table[order=\"desc\"] > thead > tr > th::after {\n"
- " content: \"\\2191\"; /* UPWARDS ARROW (U+2191) */\n"
- "}\n"
- "table[order][order-by=\"0\"] > thead > tr > th:first-child > a ,\n"
- "table[order][order-by=\"1\"] > thead > tr > th:first-child + th > a ,\n"
- "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th > a {\n"
- " text-decoration: underline;\n"
- "}\n"
- "table[order][order-by=\"0\"] > thead > tr > th:first-child::after ,\n"
- "table[order][order-by=\"1\"] > thead > tr > th:first-child + th::after ,\n"
- "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th::after {\n"
- " display: inline-block;\n"
- "}\n"
- "table.remove-hidden > tbody > tr.hidden-object {\n"
- " display: none;\n"
- "}\n"
- "td {\n"
- " white-space: nowrap;\n"
- "}\n"
- "table.ellipsis {\n"
- " width: 100%;\n"
- " table-layout: fixed;\n"
- " border-spacing: 0;\n"
- "}\n"
- "table.ellipsis > tbody > tr > td {\n"
- " padding: 0;\n"
- " overflow: hidden;\n"
- " text-overflow: ellipsis;\n"
- "}\n"
- "/* name */\n"
- "/* name */\n"
- "th:first-child {\n"
- " padding-inline-end: 2em;\n"
- "}\n"
- "/* size */\n"
- "th:first-child + th {\n"
- " padding-inline-end: 1em;\n"
- "}\n"
- "td:first-child + td {\n"
- " text-align: end;\n"
- " padding-inline-end: 1em;\n"
- "}\n"
- "/* date */\n"
- "td:first-child + td + td {\n"
- " padding-inline-start: 1em;\n"
- " padding-inline-end: .5em;\n"
- "}\n"
- "/* time */\n"
- "td:first-child + td + td + td {\n"
- " padding-inline-start: .5em;\n"
- "}\n"
- ".symlink {\n"
- " font-style: italic;\n"
- "}\n"
- ".dir ,\n"
- ".symlink ,\n"
- ".file {\n"
- " margin-inline-start: 20px;\n"
- "}\n"
- ".dir::before ,\n"
- ".file > img {\n"
- " margin-inline-end: 4px;\n"
- " margin-inline-start: -20px;\n"
- " max-width: 16px;\n"
- " max-height: 16px;\n"
- " vertical-align: middle;\n"
- "}\n"
- ".dir::before {\n"
- " content: url(resource://gre/res/html/folder.png);\n"
- "}\n"
- "</style>\n"
- "<link rel=\"stylesheet\" media=\"screen, projection\" type=\"text/css\""
- " href=\"chrome://global/skin/dirListing/dirListing.css\">\n"
- "<script type=\"application/javascript\">\n"
- "'use strict';\n"
- "var gTable, gOrderBy, gTBody, gRows, gUI_showHidden;\n"
- "document.addEventListener(\"DOMContentLoaded\", function() {\n"
- " gTable = document.getElementsByTagName(\"table\")[0];\n"
- " gTBody = gTable.tBodies[0];\n"
- " if (gTBody.rows.length < 2)\n"
- " return;\n"
- " gUI_showHidden = document.getElementById(\"UI_showHidden\");\n"
- " var headCells = gTable.tHead.rows[0].cells,\n"
- " hiddenObjects = false;\n"
- " function rowAction(i) {\n"
- " return function(event) {\n"
- " event.preventDefault();\n"
- " orderBy(i);\n"
- " }\n"
- " }\n"
- " for (var i = headCells.length - 1; i >= 0; i--) {\n"
- " var anchor = document.createElement(\"a\");\n"
- " anchor.href = \"\";\n"
- " anchor.appendChild(headCells[i].firstChild);\n"
- " headCells[i].appendChild(anchor);\n"
- " headCells[i].addEventListener(\"click\", rowAction(i), true);\n"
- " }\n"
- " if (gUI_showHidden) {\n"
- " gRows = Array.slice(gTBody.rows);\n"
- " hiddenObjects = gRows.some(row => row.className == \"hidden-object\");\n"
- " }\n"
- " gTable.setAttribute(\"order\", \"\");\n"
- " if (hiddenObjects) {\n"
- " gUI_showHidden.style.display = \"block\";\n"
- " updateHidden();\n"
- " }\n"
- "}, \"false\");\n"
- "function compareRows(rowA, rowB) {\n"
- " var a = rowA.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
- " var b = rowB.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
- " var intA = +a;\n"
- " var intB = +b;\n"
- " if (a == intA && b == intB) {\n"
- " a = intA;\n"
- " b = intB;\n"
- " } else {\n"
- " a = a.toLowerCase();\n"
- " b = b.toLowerCase();\n"
- " }\n"
- " if (a < b)\n"
- " return -1;\n"
- " if (a > b)\n"
- " return 1;\n"
- " return 0;\n"
- "}\n"
- "function orderBy(column) {\n"
- " if (!gRows)\n"
- " gRows = Array.slice(gTBody.rows);\n"
- " var order;\n"
- " if (gOrderBy == column) {\n"
- " order = gTable.getAttribute(\"order\") == \"asc\" ? \"desc\" : \"asc\";\n"
- " } else {\n"
- " order = \"asc\";\n"
- " gOrderBy = column;\n"
- " gTable.setAttribute(\"order-by\", column);\n"
- " gRows.sort(compareRows);\n"
- " }\n"
- " gTable.removeChild(gTBody);\n"
- " gTable.setAttribute(\"order\", order);\n"
- " if (order == \"asc\")\n"
- " for (var i = 0; i < gRows.length; i++)\n"
- " gTBody.appendChild(gRows[i]);\n"
- " else\n"
- " for (var i = gRows.length - 1; i >= 0; i--)\n"
- " gTBody.appendChild(gRows[i]);\n"
- " gTable.appendChild(gTBody);\n"
- "}\n"
- "function updateHidden() {\n"
- " gTable.className = gUI_showHidden.getElementsByTagName(\"input\")[0].checked ?\n"
- " \"\" :\n"
- " \"remove-hidden\";\n"
- "}\n"
- "</script>\n");
- buffer.AppendLiteral("<link rel=\"icon\" type=\"image/png\" href=\"");
- nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
- if (!innerUri)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri));
- //XXX bug 388553: can't use skinnable icons here due to security restrictions
- if (fileURL) {
- //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png");
- buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
- "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
- "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR"
- "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj"
- "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O"
- "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ"
- "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG"
- "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5"
- "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS"
- "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c"
- "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU"
- "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF"
- "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi"
- "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l"
- "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M"
- "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI"
- "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM"
- "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B"
- "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8"
- "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D");
- } else {
- //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png");
- buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
- "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
- "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft"
- "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%"
- "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0"
- "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE"
- "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM"
- "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR"
- "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh"
- "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz"
- "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj"
- "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1"
- "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9"
- "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr"
- "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E"
- "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2"
- "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS"
- "YAAAAABJRU5ErkJggg%3D%3D");
- }
- buffer.AppendLiteral("\">\n<title>");
- // Everything needs to end in a /,
- // otherwise we end up linking to file:///foo/dirfile
- if (!mTextToSubURI) {
- mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
- if (NS_FAILED(rv)) return rv;
- }
- nsXPIDLCString encoding;
- rv = uri->GetOriginCharset(encoding);
- if (NS_FAILED(rv)) return rv;
- if (encoding.IsEmpty()) {
- encoding.AssignLiteral("UTF-8");
- }
- nsXPIDLString unEscapeSpec;
- rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(),
- getter_Copies(unEscapeSpec));
- // unescape may fail because
- // 1. file URL may be encoded in platform charset for backward compatibility
- // 2. query part may not be encoded in UTF-8 (see bug 261929)
- // so try the platform's default if this is file url
- if (NS_FAILED(rv) && isSchemeFile) {
- nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv));
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString charset;
- rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(),
- getter_Copies(unEscapeSpec));
- }
- if (NS_FAILED(rv)) return rv;
- nsXPIDLString htmlEscSpec;
- htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
- unEscapeSpec.Length()));
- nsXPIDLString title;
- const char16_t* formatTitle[] = {
- htmlEscSpec.get()
- };
- rv = mBundle->FormatStringFromName(u"DirTitle",
- formatTitle,
- sizeof(formatTitle)/sizeof(char16_t*),
- getter_Copies(title));
- if (NS_FAILED(rv)) return rv;
- // we want to convert string bundle to NCR
- // to ensure they're shown in any charsets
- AppendNonAsciiToNCR(title, buffer);
- buffer.AppendLiteral("</title>\n");
- // If there is a quote character in the baseUri, then
- // lets not add a base URL. The reason for this is that
- // if we stick baseUri containing a quote into a quoted
- // string, the quote character will prematurely close
- // the base href string. This is a fall-back check;
- // that's why it is OK to not use a base rather than
- // trying to play nice and escaping the quotes. See bug
- // 358128.
- if (!baseUri.Contains('"'))
- {
- // Great, the baseUri does not contain a char that
- // will prematurely close the string. Go ahead an
- // add a base href, but only do so if we're not
- // dealing with a resource URI.
- nsCOMPtr<nsIURI> originalUri;
- rv = channel->GetOriginalURI(getter_AddRefs(originalUri));
- bool wasResource = false;
- if (NS_FAILED(rv) ||
- NS_FAILED(originalUri->SchemeIs("resource", &wasResource)) ||
- !wasResource) {
- buffer.AppendLiteral("<base href=\"");
- nsAdoptingCString htmlEscapedUri(nsEscapeHTML(baseUri.get()));
- buffer.Append(htmlEscapedUri);
- buffer.AppendLiteral("\" />\n");
- }
- }
- else
- {
- NS_ERROR("broken protocol handler didn't escape double-quote.");
- }
- nsCString direction(NS_LITERAL_CSTRING("ltr"));
- nsCOMPtr<nsIXULChromeRegistry> reg =
- mozilla::services::GetXULChromeRegistryService();
- if (reg) {
- bool isRTL = false;
- reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL);
- if (isRTL) {
- direction.AssignLiteral("rtl");
- }
- }
- buffer.AppendLiteral("</head>\n<body dir=\"");
- buffer.Append(direction);
- buffer.AppendLiteral("\">\n<h1>");
-
- const char16_t* formatHeading[] = {
- htmlEscSpec.get()
- };
- rv = mBundle->FormatStringFromName(u"DirTitle",
- formatHeading,
- sizeof(formatHeading)/sizeof(char16_t*),
- getter_Copies(title));
- if (NS_FAILED(rv)) return rv;
-
- AppendNonAsciiToNCR(title, buffer);
- buffer.AppendLiteral("</h1>\n");
- if (!parentStr.IsEmpty()) {
- nsXPIDLString parentText;
- rv = mBundle->GetStringFromName(u"DirGoUp",
- getter_Copies(parentText));
- if (NS_FAILED(rv)) return rv;
- buffer.AppendLiteral("<p id=\"UI_goUp\"><a class=\"up\" href=\"");
- nsAdoptingCString htmlParentStr(nsEscapeHTML(parentStr.get()));
- buffer.Append(htmlParentStr);
- buffer.AppendLiteral("\">");
- AppendNonAsciiToNCR(parentText, buffer);
- buffer.AppendLiteral("</a></p>\n");
- }
- if (isSchemeFile) {
- nsXPIDLString showHiddenText;
- rv = mBundle->GetStringFromName(u"ShowHidden",
- getter_Copies(showHiddenText));
- if (NS_FAILED(rv)) return rv;
- buffer.AppendLiteral("<p id=\"UI_showHidden\" style=\"display:none\"><label><input type=\"checkbox\" checked onchange=\"updateHidden()\">");
- AppendNonAsciiToNCR(showHiddenText, buffer);
- buffer.AppendLiteral("</label></p>\n");
- }
- buffer.AppendLiteral("<table>\n");
- nsXPIDLString columnText;
- buffer.AppendLiteral(" <thead>\n"
- " <tr>\n"
- " <th>");
- rv = mBundle->GetStringFromName(u"DirColName",
- getter_Copies(columnText));
- if (NS_FAILED(rv)) return rv;
- AppendNonAsciiToNCR(columnText, buffer);
- buffer.AppendLiteral("</th>\n"
- " <th>");
- rv = mBundle->GetStringFromName(u"DirColSize",
- getter_Copies(columnText));
- if (NS_FAILED(rv)) return rv;
- AppendNonAsciiToNCR(columnText, buffer);
- buffer.AppendLiteral("</th>\n"
- " <th colspan=\"2\">");
- rv = mBundle->GetStringFromName(u"DirColMTime",
- getter_Copies(columnText));
- if (NS_FAILED(rv)) return rv;
- AppendNonAsciiToNCR(columnText, buffer);
- buffer.AppendLiteral("</th>\n"
- " </tr>\n"
- " </thead>\n");
- buffer.AppendLiteral(" <tbody>\n");
- aBuffer = buffer;
- return rv;
- }
- NS_IMETHODIMP
- nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext,
- nsresult aStatus) {
- if (NS_SUCCEEDED(aStatus)) {
- nsCString buffer;
- buffer.AssignLiteral("</tbody></table></body></html>\n");
- aStatus = SendToListener(request, aContext, buffer);
- }
- mParser->OnStopRequest(request, aContext, aStatus);
- mParser = nullptr;
-
- return mListener->OnStopRequest(request, aContext, aStatus);
- }
- nsresult
- nsIndexedToHTML::SendToListener(nsIRequest* aRequest, nsISupports *aContext, const nsACString &aBuffer)
- {
- nsCOMPtr<nsIInputStream> inputData;
- nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputData), aBuffer);
- NS_ENSURE_SUCCESS(rv, rv);
- return mListener->OnDataAvailable(aRequest, aContext,
- inputData, 0, aBuffer.Length());
- }
- NS_IMETHODIMP
- nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest,
- nsISupports *aCtxt,
- nsIInputStream* aInput,
- uint64_t aOffset,
- uint32_t aCount) {
- return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount);
- }
- NS_IMETHODIMP
- nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
- nsISupports *aCtxt,
- nsIDirIndex *aIndex) {
- nsresult rv;
- if (!aIndex)
- return NS_ERROR_NULL_POINTER;
- nsCString pushBuffer;
- pushBuffer.AppendLiteral("<tr");
- // We don't know the file's character set yet, so retrieve the raw bytes
- // which will be decoded by the HTML parser.
- nsXPIDLCString loc;
- aIndex->GetLocation(getter_Copies(loc));
- // Adjust the length in case unescaping shortened the string.
- loc.Truncate(nsUnescapeCount(loc.BeginWriting()));
- if (loc.IsEmpty()) {
- return NS_ERROR_ILLEGAL_VALUE;
- }
- if (loc.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.Append('0');
- break;
- case nsIDirIndex::TYPE_DIRECTORY:
- pushBuffer.Append('1');
- break;
- default:
- pushBuffer.Append('2');
- break;
- }
- nsAdoptingCString escaped(nsEscapeHTML(loc));
- pushBuffer.Append(escaped);
- pushBuffer.AppendLiteral("\"><table class=\"ellipsis\"><tbody><tr><td><a class=\"");
- switch (type) {
- case nsIDirIndex::TYPE_DIRECTORY:
- pushBuffer.AppendLiteral("dir");
- break;
- case nsIDirIndex::TYPE_SYMLINK:
- pushBuffer.AppendLiteral("symlink");
- break;
- default:
- pushBuffer.AppendLiteral("file");
- break;
- }
- pushBuffer.AppendLiteral("\" href=\"");
- // 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.
- nsAutoCString scheme;
- if (mExpectAbsLoc &&
- NS_SUCCEEDED(net_ExtractURLScheme(loc, scheme))) {
- // escape as absolute
- escFlags = esc_Forced | 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_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");
- nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get()));
- 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);
- nsAdoptingCString htmlFileExt(nsEscapeHTML(locEscaped.get()));
- pushBuffer.Append(htmlFileExt);
- } else {
- pushBuffer.AppendLiteral("unknown");
- }
- pushBuffer.AppendLiteral("?size=16\" alt=\"");
- nsXPIDLString altText;
- rv = mBundle->GetStringFromName(u"DirFileLabel",
- getter_Copies(altText));
- if (NS_FAILED(rv)) return rv;
- AppendNonAsciiToNCR(altText, pushBuffer);
- pushBuffer.AppendLiteral("\">");
- }
- pushBuffer.Append(escaped);
- pushBuffer.AppendLiteral("</a></td></tr></tbody></table></td>\n <td");
- if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) {
- pushBuffer.Append('>');
- } else {
- int64_t size;
- aIndex->GetSize(&size);
- if (uint64_t(size) != UINT64_MAX) {
- pushBuffer.AppendLiteral(" sortable-data=\"");
- pushBuffer.AppendInt(size);
- pushBuffer.AppendLiteral("\">");
- nsAutoCString sizeString;
- FormatSizeString(size, sizeString);
- pushBuffer.Append(sizeString);
- } else {
- pushBuffer.Append('>');
- }
- }
- pushBuffer.AppendLiteral("</td>\n <td");
- PRTime t;
- aIndex->GetLastModified(&t);
- if (t == -1LL) {
- 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 SendToListener(aRequest, aCtxt, pushBuffer);
- }
- NS_IMETHODIMP
- nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest,
- nsISupports *aCtxt,
- const nsAString& aInfo) {
- nsAutoCString pushBuffer;
- nsAdoptingString escaped(nsEscapeHTML2(PromiseFlatString(aInfo).get()));
- if (!escaped)
- return NS_ERROR_OUT_OF_MEMORY;
- pushBuffer.AppendLiteral("<tr>\n <td>");
- // escaped is provided in Unicode, so write hex NCRs as necessary
- // to prevent the HTML parser from applying a character set.
- AppendNonAsciiToNCR(escaped, pushBuffer);
- pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n");
-
- return SendToListener(aRequest, aCtxt, pushBuffer);
- }
- void nsIndexedToHTML::FormatSizeString(int64_t inSize, nsCString& outSizeString)
- {
- outSizeString.Truncate();
- if (inSize > int64_t(0)) {
- // round up to the nearest Kilobyte
- int64_t upperSize = (inSize + int64_t(1023)) / int64_t(1024);
- outSizeString.AppendInt(upperSize);
- outSizeString.AppendLiteral(" KB");
- }
- }
- nsIndexedToHTML::nsIndexedToHTML() {
- }
- nsIndexedToHTML::~nsIndexedToHTML() {
- }
|