nsIndexedToHTML.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsIndexedToHTML.h"
  6. #include "mozilla/dom/EncodingUtils.h"
  7. #include "nsNetUtil.h"
  8. #include "netCore.h"
  9. #include "nsStringStream.h"
  10. #include "nsIFile.h"
  11. #include "nsIFileURL.h"
  12. #include "nsEscape.h"
  13. #include "nsIDirIndex.h"
  14. #include "nsURLHelper.h"
  15. #include "nsIPlatformCharset.h"
  16. #include "nsIPrefService.h"
  17. #include "nsIPrefBranch.h"
  18. #include "nsIPrefLocalizedString.h"
  19. #include "nsIChromeRegistry.h"
  20. #include "nsIDateTimeFormat.h"
  21. #include "nsIStringBundle.h"
  22. #include "nsITextToSubURI.h"
  23. #include "nsXPIDLString.h"
  24. #include <algorithm>
  25. #include "nsIChannel.h"
  26. NS_IMPL_ISUPPORTS(nsIndexedToHTML,
  27. nsIDirIndexListener,
  28. nsIStreamConverter,
  29. nsIRequestObserver,
  30. nsIStreamListener)
  31. static void AppendNonAsciiToNCR(const nsAString& in, nsCString& out)
  32. {
  33. nsAString::const_iterator start, end;
  34. in.BeginReading(start);
  35. in.EndReading(end);
  36. while (start != end) {
  37. if (*start < 128) {
  38. out.Append(*start++);
  39. } else {
  40. out.AppendLiteral("&#x");
  41. out.AppendInt(*start++, 16);
  42. out.Append(';');
  43. }
  44. }
  45. }
  46. nsresult
  47. nsIndexedToHTML::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
  48. nsresult rv;
  49. if (aOuter)
  50. return NS_ERROR_NO_AGGREGATION;
  51. nsIndexedToHTML* _s = new nsIndexedToHTML();
  52. if (_s == nullptr)
  53. return NS_ERROR_OUT_OF_MEMORY;
  54. rv = _s->QueryInterface(aIID, aResult);
  55. return rv;
  56. }
  57. nsresult
  58. nsIndexedToHTML::Init(nsIStreamListener* aListener) {
  59. nsresult rv = NS_OK;
  60. mListener = aListener;
  61. mDateTime = nsIDateTimeFormat::Create();
  62. if (!mDateTime)
  63. return NS_ERROR_FAILURE;
  64. nsCOMPtr<nsIStringBundleService> sbs =
  65. do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
  66. if (NS_FAILED(rv)) return rv;
  67. rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle));
  68. mExpectAbsLoc = false;
  69. return rv;
  70. }
  71. NS_IMETHODIMP
  72. nsIndexedToHTML::Convert(nsIInputStream* aFromStream,
  73. const char* aFromType,
  74. const char* aToType,
  75. nsISupports* aCtxt,
  76. nsIInputStream** res) {
  77. return NS_ERROR_NOT_IMPLEMENTED;
  78. }
  79. NS_IMETHODIMP
  80. nsIndexedToHTML::AsyncConvertData(const char *aFromType,
  81. const char *aToType,
  82. nsIStreamListener *aListener,
  83. nsISupports *aCtxt) {
  84. return Init(aListener);
  85. }
  86. NS_IMETHODIMP
  87. nsIndexedToHTML::OnStartRequest(nsIRequest* request, nsISupports *aContext) {
  88. nsCString buffer;
  89. nsresult rv = DoOnStartRequest(request, aContext, buffer);
  90. if (NS_FAILED(rv)) {
  91. request->Cancel(rv);
  92. }
  93. rv = mListener->OnStartRequest(request, aContext);
  94. if (NS_FAILED(rv)) return rv;
  95. // The request may have been canceled, and if that happens, we want to
  96. // suppress calls to OnDataAvailable.
  97. request->GetStatus(&rv);
  98. if (NS_FAILED(rv)) return rv;
  99. // Push our buffer to the listener.
  100. rv = SendToListener(request, aContext, buffer);
  101. return rv;
  102. }
  103. nsresult
  104. nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
  105. nsCString& aBuffer) {
  106. nsresult rv;
  107. nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  108. nsCOMPtr<nsIURI> uri;
  109. rv = channel->GetURI(getter_AddRefs(uri));
  110. if (NS_FAILED(rv)) return rv;
  111. channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
  112. mParser = do_CreateInstance("@mozilla.org/dirIndexParser;1",&rv);
  113. if (NS_FAILED(rv)) return rv;
  114. rv = mParser->SetListener(this);
  115. if (NS_FAILED(rv)) return rv;
  116. rv = mParser->OnStartRequest(request, aContext);
  117. if (NS_FAILED(rv)) return rv;
  118. nsAutoCString baseUri, titleUri;
  119. rv = uri->GetAsciiSpec(baseUri);
  120. if (NS_FAILED(rv)) return rv;
  121. nsCOMPtr<nsIURI> titleURL;
  122. rv = uri->Clone(getter_AddRefs(titleURL));
  123. if (NS_FAILED(rv)) titleURL = uri;
  124. rv = titleURL->SetQuery(EmptyCString());
  125. if (NS_FAILED(rv)) titleURL = uri;
  126. rv = titleURL->SetRef(EmptyCString());
  127. if (NS_FAILED(rv)) titleURL = uri;
  128. nsCString parentStr;
  129. nsCString buffer;
  130. buffer.AppendLiteral("<!DOCTYPE html>\n<html>\n<head>\n");
  131. // XXX - should be using the 300: line from the parser.
  132. // We can't guarantee that that comes before any entry, so we'd have to
  133. // buffer, and do other painful stuff.
  134. // I'll deal with this when I make the changes to handle welcome messages
  135. // The .. stuff should also come from the lower level protocols, but that
  136. // would muck up the XUL display
  137. // - bbaetz
  138. bool isScheme = false;
  139. bool isSchemeFile = false;
  140. if (NS_SUCCEEDED(uri->SchemeIs("ftp", &isScheme)) && isScheme) {
  141. // strip out the password here, so it doesn't show in the page title
  142. // This is done by the 300: line generation in ftp, but we don't use
  143. // that - see above
  144. nsAutoCString pw;
  145. rv = titleURL->GetPassword(pw);
  146. if (NS_FAILED(rv)) return rv;
  147. if (!pw.IsEmpty()) {
  148. nsCOMPtr<nsIURI> newUri;
  149. rv = titleURL->Clone(getter_AddRefs(newUri));
  150. if (NS_FAILED(rv)) return rv;
  151. rv = newUri->SetPassword(EmptyCString());
  152. if (NS_FAILED(rv)) return rv;
  153. }
  154. nsAutoCString path;
  155. rv = uri->GetPath(path);
  156. if (NS_FAILED(rv)) return rv;
  157. if (!path.EqualsLiteral("//") && !path.LowerCaseEqualsLiteral("/%2f")) {
  158. rv = uri->Resolve(NS_LITERAL_CSTRING(".."),parentStr);
  159. if (NS_FAILED(rv)) return rv;
  160. }
  161. } else if (NS_SUCCEEDED(uri->SchemeIs("file", &isSchemeFile)) && isSchemeFile) {
  162. nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
  163. nsCOMPtr<nsIFile> file;
  164. rv = fileUrl->GetFile(getter_AddRefs(file));
  165. if (NS_FAILED(rv)) return rv;
  166. file->SetFollowLinks(true);
  167. nsAutoCString url;
  168. rv = net_GetURLSpecFromFile(file, url);
  169. if (NS_FAILED(rv)) return rv;
  170. baseUri.Assign(url);
  171. nsCOMPtr<nsIFile> parent;
  172. rv = file->GetParent(getter_AddRefs(parent));
  173. if (parent && NS_SUCCEEDED(rv)) {
  174. net_GetURLSpecFromDir(parent, url);
  175. if (NS_FAILED(rv)) return rv;
  176. parentStr.Assign(url);
  177. }
  178. // Directory index will be always encoded in UTF-8 if this is file url
  179. buffer.AppendLiteral("<meta charset=\"UTF-8\">\n");
  180. } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &isScheme)) && isScheme) {
  181. nsAutoCString path;
  182. rv = uri->GetPath(path);
  183. if (NS_FAILED(rv)) return rv;
  184. // a top-level jar directory URL is of the form jar:foo.zip!/
  185. // path will be of the form foo.zip!/, and its last two characters
  186. // will be "!/"
  187. //XXX this won't work correctly when the name of the directory being
  188. //XXX displayed ends with "!", but then again, jar: URIs don't deal
  189. //XXX particularly well with such directories anyway
  190. if (!StringEndsWith(path, NS_LITERAL_CSTRING("!/"))) {
  191. rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
  192. if (NS_FAILED(rv)) return rv;
  193. }
  194. }
  195. else {
  196. // default behavior for other protocols is to assume the channel's
  197. // URL references a directory ending in '/' -- fixup if necessary.
  198. nsAutoCString path;
  199. rv = uri->GetPath(path);
  200. if (NS_FAILED(rv)) return rv;
  201. if (baseUri.Last() != '/') {
  202. baseUri.Append('/');
  203. path.Append('/');
  204. uri->SetPath(path);
  205. }
  206. if (!path.EqualsLiteral("/")) {
  207. rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
  208. if (NS_FAILED(rv)) return rv;
  209. }
  210. }
  211. rv = titleURL->GetAsciiSpec(titleUri);
  212. if (NS_FAILED(rv)) {
  213. return rv;
  214. }
  215. buffer.AppendLiteral("<style type=\"text/css\">\n"
  216. ":root {\n"
  217. " font-family: sans-serif;\n"
  218. "}\n"
  219. "img {\n"
  220. " border: 0;\n"
  221. "}\n"
  222. "th {\n"
  223. " text-align: start;\n"
  224. " white-space: nowrap;\n"
  225. "}\n"
  226. "th > a {\n"
  227. " color: inherit;\n"
  228. "}\n"
  229. "table[order] > thead > tr > th {\n"
  230. " cursor: pointer;\n"
  231. "}\n"
  232. "table[order] > thead > tr > th::after {\n"
  233. " display: none;\n"
  234. " width: .8em;\n"
  235. " margin-inline-end: -.8em;\n"
  236. " text-align: end;\n"
  237. "}\n"
  238. "table[order=\"asc\"] > thead > tr > th::after {\n"
  239. " content: \"\\2193\"; /* DOWNWARDS ARROW (U+2193) */\n"
  240. "}\n"
  241. "table[order=\"desc\"] > thead > tr > th::after {\n"
  242. " content: \"\\2191\"; /* UPWARDS ARROW (U+2191) */\n"
  243. "}\n"
  244. "table[order][order-by=\"0\"] > thead > tr > th:first-child > a ,\n"
  245. "table[order][order-by=\"1\"] > thead > tr > th:first-child + th > a ,\n"
  246. "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th > a {\n"
  247. " text-decoration: underline;\n"
  248. "}\n"
  249. "table[order][order-by=\"0\"] > thead > tr > th:first-child::after ,\n"
  250. "table[order][order-by=\"1\"] > thead > tr > th:first-child + th::after ,\n"
  251. "table[order][order-by=\"2\"] > thead > tr > th:first-child + th + th::after {\n"
  252. " display: inline-block;\n"
  253. "}\n"
  254. "table.remove-hidden > tbody > tr.hidden-object {\n"
  255. " display: none;\n"
  256. "}\n"
  257. "td {\n"
  258. " white-space: nowrap;\n"
  259. "}\n"
  260. "table.ellipsis {\n"
  261. " width: 100%;\n"
  262. " table-layout: fixed;\n"
  263. " border-spacing: 0;\n"
  264. "}\n"
  265. "table.ellipsis > tbody > tr > td {\n"
  266. " padding: 0;\n"
  267. " overflow: hidden;\n"
  268. " text-overflow: ellipsis;\n"
  269. "}\n"
  270. "/* name */\n"
  271. "/* name */\n"
  272. "th:first-child {\n"
  273. " padding-inline-end: 2em;\n"
  274. "}\n"
  275. "/* size */\n"
  276. "th:first-child + th {\n"
  277. " padding-inline-end: 1em;\n"
  278. "}\n"
  279. "td:first-child + td {\n"
  280. " text-align: end;\n"
  281. " padding-inline-end: 1em;\n"
  282. "}\n"
  283. "/* date */\n"
  284. "td:first-child + td + td {\n"
  285. " padding-inline-start: 1em;\n"
  286. " padding-inline-end: .5em;\n"
  287. "}\n"
  288. "/* time */\n"
  289. "td:first-child + td + td + td {\n"
  290. " padding-inline-start: .5em;\n"
  291. "}\n"
  292. ".symlink {\n"
  293. " font-style: italic;\n"
  294. "}\n"
  295. ".dir ,\n"
  296. ".symlink ,\n"
  297. ".file {\n"
  298. " margin-inline-start: 20px;\n"
  299. "}\n"
  300. ".dir::before ,\n"
  301. ".file > img {\n"
  302. " margin-inline-end: 4px;\n"
  303. " margin-inline-start: -20px;\n"
  304. " max-width: 16px;\n"
  305. " max-height: 16px;\n"
  306. " vertical-align: middle;\n"
  307. "}\n"
  308. ".dir::before {\n"
  309. " content: url(resource://gre/res/html/folder.png);\n"
  310. "}\n"
  311. "</style>\n"
  312. "<link rel=\"stylesheet\" media=\"screen, projection\" type=\"text/css\""
  313. " href=\"chrome://global/skin/dirListing/dirListing.css\">\n"
  314. "<script type=\"application/javascript\">\n"
  315. "'use strict';\n"
  316. "var gTable, gOrderBy, gTBody, gRows, gUI_showHidden;\n"
  317. "document.addEventListener(\"DOMContentLoaded\", function() {\n"
  318. " gTable = document.getElementsByTagName(\"table\")[0];\n"
  319. " gTBody = gTable.tBodies[0];\n"
  320. " if (gTBody.rows.length < 2)\n"
  321. " return;\n"
  322. " gUI_showHidden = document.getElementById(\"UI_showHidden\");\n"
  323. " var headCells = gTable.tHead.rows[0].cells,\n"
  324. " hiddenObjects = false;\n"
  325. " function rowAction(i) {\n"
  326. " return function(event) {\n"
  327. " event.preventDefault();\n"
  328. " orderBy(i);\n"
  329. " }\n"
  330. " }\n"
  331. " for (var i = headCells.length - 1; i >= 0; i--) {\n"
  332. " var anchor = document.createElement(\"a\");\n"
  333. " anchor.href = \"\";\n"
  334. " anchor.appendChild(headCells[i].firstChild);\n"
  335. " headCells[i].appendChild(anchor);\n"
  336. " headCells[i].addEventListener(\"click\", rowAction(i), true);\n"
  337. " }\n"
  338. " if (gUI_showHidden) {\n"
  339. " gRows = Array.slice(gTBody.rows);\n"
  340. " hiddenObjects = gRows.some(row => row.className == \"hidden-object\");\n"
  341. " }\n"
  342. " gTable.setAttribute(\"order\", \"\");\n"
  343. " if (hiddenObjects) {\n"
  344. " gUI_showHidden.style.display = \"block\";\n"
  345. " updateHidden();\n"
  346. " }\n"
  347. "}, \"false\");\n"
  348. "function compareRows(rowA, rowB) {\n"
  349. " var a = rowA.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
  350. " var b = rowB.cells[gOrderBy].getAttribute(\"sortable-data\") || \"\";\n"
  351. " var intA = +a;\n"
  352. " var intB = +b;\n"
  353. " if (a == intA && b == intB) {\n"
  354. " a = intA;\n"
  355. " b = intB;\n"
  356. " } else {\n"
  357. " a = a.toLowerCase();\n"
  358. " b = b.toLowerCase();\n"
  359. " }\n"
  360. " if (a < b)\n"
  361. " return -1;\n"
  362. " if (a > b)\n"
  363. " return 1;\n"
  364. " return 0;\n"
  365. "}\n"
  366. "function orderBy(column) {\n"
  367. " if (!gRows)\n"
  368. " gRows = Array.slice(gTBody.rows);\n"
  369. " var order;\n"
  370. " if (gOrderBy == column) {\n"
  371. " order = gTable.getAttribute(\"order\") == \"asc\" ? \"desc\" : \"asc\";\n"
  372. " } else {\n"
  373. " order = \"asc\";\n"
  374. " gOrderBy = column;\n"
  375. " gTable.setAttribute(\"order-by\", column);\n"
  376. " gRows.sort(compareRows);\n"
  377. " }\n"
  378. " gTable.removeChild(gTBody);\n"
  379. " gTable.setAttribute(\"order\", order);\n"
  380. " if (order == \"asc\")\n"
  381. " for (var i = 0; i < gRows.length; i++)\n"
  382. " gTBody.appendChild(gRows[i]);\n"
  383. " else\n"
  384. " for (var i = gRows.length - 1; i >= 0; i--)\n"
  385. " gTBody.appendChild(gRows[i]);\n"
  386. " gTable.appendChild(gTBody);\n"
  387. "}\n"
  388. "function updateHidden() {\n"
  389. " gTable.className = gUI_showHidden.getElementsByTagName(\"input\")[0].checked ?\n"
  390. " \"\" :\n"
  391. " \"remove-hidden\";\n"
  392. "}\n"
  393. "</script>\n");
  394. buffer.AppendLiteral("<link rel=\"icon\" type=\"image/png\" href=\"");
  395. nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
  396. if (!innerUri)
  397. return NS_ERROR_UNEXPECTED;
  398. nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri));
  399. //XXX bug 388553: can't use skinnable icons here due to security restrictions
  400. if (fileURL) {
  401. //buffer.AppendLiteral("chrome://global/skin/dirListing/local.png");
  402. buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
  403. "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
  404. "ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR"
  405. "3XQ3ONASdBJCSBxHos5%2B3Bg3rvkCv8PElS78gPkO%2FATj"
  406. "QoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12O"
  407. "YIDhcPiu2Wx%2B%2FHF5CW1Z6Jyegt%2FTNEWSJIjjGFEUIQ"
  408. "xDrFYrWFSzXC4%2FdLvd95pRKpXKy%2BpRFZ7nwaWo1%2BsG"
  409. "nQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5"
  410. "obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM%2FS"
  411. "KK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9c"
  412. "lkYoDo3SZJzzSdp0VSKYmfV1co%2Bz580kw5KDIM8RbRfEnU"
  413. "f1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd%2BzGQQB5llunF"
  414. "%2FM52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxi"
  415. "xfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18l"
  416. "yWh5FFbrOSxbl3V4G%2BVB7T4ajYYxTyuLtO%2BCvWGgJE1M"
  417. "c7JNsJEhvgw%2FQV4fo%2F24nbEsX2u1d5sVyn8sJO0ZAQiI"
  418. "YnFh%2BxrfLz%2Fj29cBS%2FO14zg3i8XigW3ZkErDtmKoeM"
  419. "%2BAJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ%2B"
  420. "4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8"
  421. "y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D");
  422. } else {
  423. //buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png");
  424. buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
  425. "AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
  426. "ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft"
  427. "Z2AgQSYQRqL1UPVG2hAUQkxLEStz4DrXpLpD5Drz31Cajax%"
  428. "2Bghhx6qHIJURBTxIwQRwopCBbZjHMcOTrzermPipsSt1Iw0"
  429. "3p3ZmW%2B%2B2R0TxhgOD34wjCHZlQ0iDYz9yvEfhxMTCYhE"
  430. "QDIZhkxKd2sqzX2TOD2vBQCQhpPefng1ZP2dVPlLLdpL8SEM"
  431. "cxng%2Fbs0RIHhtgs4twxOh%2BHjZxvzDx%2F3GQQiDFISiR"
  432. "BLFMPKTRMollzcWECrDVhtxtdRVsL9youPxGj%2FbdfFlUZh"
  433. "tDyYbYqWRUdai1oQRZ5oHeHl2gNM%2B01Uqio8RlH%2Bnsaz"
  434. "JzNwXcq1B%2BiXPHprlEEymeBfXs1w8XxxihfyuXqoHqpoGj"
  435. "ZM04bddgG%2F9%2B8WGj87qDdsrK9m%2BoA%2BpbhQTDh2l1"
  436. "%2Bi2weNbSHMZyjvNXmVbqh9Fj5Oz27uEoP%2BSTxANruJs9"
  437. "L%2FT6P0ewqPx5nmiAG5f6AoCtN1PbJzuRyJAyDBzzSQYvEr"
  438. "f06yYxhGXlEa8H2KVGoasjwLx3Ewk858opQWXm%2B%2Fib9E"
  439. "QrBzclLLLy89xYvlpchvtixcX6uo1y%2FzsiwHrkIsgKbp%2"
  440. "BYWFOWicuqppoNTnStHzPFCPQhBEBOyGAX4JMADFetubi4BS"
  441. "YAAAAABJRU5ErkJggg%3D%3D");
  442. }
  443. buffer.AppendLiteral("\">\n<title>");
  444. // Everything needs to end in a /,
  445. // otherwise we end up linking to file:///foo/dirfile
  446. if (!mTextToSubURI) {
  447. mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
  448. if (NS_FAILED(rv)) return rv;
  449. }
  450. nsXPIDLCString encoding;
  451. rv = uri->GetOriginCharset(encoding);
  452. if (NS_FAILED(rv)) return rv;
  453. if (encoding.IsEmpty()) {
  454. encoding.AssignLiteral("UTF-8");
  455. }
  456. nsXPIDLString unEscapeSpec;
  457. rv = mTextToSubURI->UnEscapeAndConvert(encoding, titleUri.get(),
  458. getter_Copies(unEscapeSpec));
  459. // unescape may fail because
  460. // 1. file URL may be encoded in platform charset for backward compatibility
  461. // 2. query part may not be encoded in UTF-8 (see bug 261929)
  462. // so try the platform's default if this is file url
  463. if (NS_FAILED(rv) && isSchemeFile) {
  464. nsCOMPtr<nsIPlatformCharset> platformCharset(do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv));
  465. NS_ENSURE_SUCCESS(rv, rv);
  466. nsAutoCString charset;
  467. rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, charset);
  468. NS_ENSURE_SUCCESS(rv, rv);
  469. rv = mTextToSubURI->UnEscapeAndConvert(charset.get(), titleUri.get(),
  470. getter_Copies(unEscapeSpec));
  471. }
  472. if (NS_FAILED(rv)) return rv;
  473. nsXPIDLString htmlEscSpec;
  474. htmlEscSpec.Adopt(nsEscapeHTML2(unEscapeSpec.get(),
  475. unEscapeSpec.Length()));
  476. nsXPIDLString title;
  477. const char16_t* formatTitle[] = {
  478. htmlEscSpec.get()
  479. };
  480. rv = mBundle->FormatStringFromName(u"DirTitle",
  481. formatTitle,
  482. sizeof(formatTitle)/sizeof(char16_t*),
  483. getter_Copies(title));
  484. if (NS_FAILED(rv)) return rv;
  485. // we want to convert string bundle to NCR
  486. // to ensure they're shown in any charsets
  487. AppendNonAsciiToNCR(title, buffer);
  488. buffer.AppendLiteral("</title>\n");
  489. // If there is a quote character in the baseUri, then
  490. // lets not add a base URL. The reason for this is that
  491. // if we stick baseUri containing a quote into a quoted
  492. // string, the quote character will prematurely close
  493. // the base href string. This is a fall-back check;
  494. // that's why it is OK to not use a base rather than
  495. // trying to play nice and escaping the quotes. See bug
  496. // 358128.
  497. if (!baseUri.Contains('"'))
  498. {
  499. // Great, the baseUri does not contain a char that
  500. // will prematurely close the string. Go ahead an
  501. // add a base href, but only do so if we're not
  502. // dealing with a resource URI.
  503. nsCOMPtr<nsIURI> originalUri;
  504. rv = channel->GetOriginalURI(getter_AddRefs(originalUri));
  505. bool wasResource = false;
  506. if (NS_FAILED(rv) ||
  507. NS_FAILED(originalUri->SchemeIs("resource", &wasResource)) ||
  508. !wasResource) {
  509. buffer.AppendLiteral("<base href=\"");
  510. nsAdoptingCString htmlEscapedUri(nsEscapeHTML(baseUri.get()));
  511. buffer.Append(htmlEscapedUri);
  512. buffer.AppendLiteral("\" />\n");
  513. }
  514. }
  515. else
  516. {
  517. NS_ERROR("broken protocol handler didn't escape double-quote.");
  518. }
  519. nsCString direction(NS_LITERAL_CSTRING("ltr"));
  520. nsCOMPtr<nsIXULChromeRegistry> reg =
  521. mozilla::services::GetXULChromeRegistryService();
  522. if (reg) {
  523. bool isRTL = false;
  524. reg->IsLocaleRTL(NS_LITERAL_CSTRING("global"), &isRTL);
  525. if (isRTL) {
  526. direction.AssignLiteral("rtl");
  527. }
  528. }
  529. buffer.AppendLiteral("</head>\n<body dir=\"");
  530. buffer.Append(direction);
  531. buffer.AppendLiteral("\">\n<h1>");
  532. const char16_t* formatHeading[] = {
  533. htmlEscSpec.get()
  534. };
  535. rv = mBundle->FormatStringFromName(u"DirTitle",
  536. formatHeading,
  537. sizeof(formatHeading)/sizeof(char16_t*),
  538. getter_Copies(title));
  539. if (NS_FAILED(rv)) return rv;
  540. AppendNonAsciiToNCR(title, buffer);
  541. buffer.AppendLiteral("</h1>\n");
  542. if (!parentStr.IsEmpty()) {
  543. nsXPIDLString parentText;
  544. rv = mBundle->GetStringFromName(u"DirGoUp",
  545. getter_Copies(parentText));
  546. if (NS_FAILED(rv)) return rv;
  547. buffer.AppendLiteral("<p id=\"UI_goUp\"><a class=\"up\" href=\"");
  548. nsAdoptingCString htmlParentStr(nsEscapeHTML(parentStr.get()));
  549. buffer.Append(htmlParentStr);
  550. buffer.AppendLiteral("\">");
  551. AppendNonAsciiToNCR(parentText, buffer);
  552. buffer.AppendLiteral("</a></p>\n");
  553. }
  554. if (isSchemeFile) {
  555. nsXPIDLString showHiddenText;
  556. rv = mBundle->GetStringFromName(u"ShowHidden",
  557. getter_Copies(showHiddenText));
  558. if (NS_FAILED(rv)) return rv;
  559. buffer.AppendLiteral("<p id=\"UI_showHidden\" style=\"display:none\"><label><input type=\"checkbox\" checked onchange=\"updateHidden()\">");
  560. AppendNonAsciiToNCR(showHiddenText, buffer);
  561. buffer.AppendLiteral("</label></p>\n");
  562. }
  563. buffer.AppendLiteral("<table>\n");
  564. nsXPIDLString columnText;
  565. buffer.AppendLiteral(" <thead>\n"
  566. " <tr>\n"
  567. " <th>");
  568. rv = mBundle->GetStringFromName(u"DirColName",
  569. getter_Copies(columnText));
  570. if (NS_FAILED(rv)) return rv;
  571. AppendNonAsciiToNCR(columnText, buffer);
  572. buffer.AppendLiteral("</th>\n"
  573. " <th>");
  574. rv = mBundle->GetStringFromName(u"DirColSize",
  575. getter_Copies(columnText));
  576. if (NS_FAILED(rv)) return rv;
  577. AppendNonAsciiToNCR(columnText, buffer);
  578. buffer.AppendLiteral("</th>\n"
  579. " <th colspan=\"2\">");
  580. rv = mBundle->GetStringFromName(u"DirColMTime",
  581. getter_Copies(columnText));
  582. if (NS_FAILED(rv)) return rv;
  583. AppendNonAsciiToNCR(columnText, buffer);
  584. buffer.AppendLiteral("</th>\n"
  585. " </tr>\n"
  586. " </thead>\n");
  587. buffer.AppendLiteral(" <tbody>\n");
  588. aBuffer = buffer;
  589. return rv;
  590. }
  591. NS_IMETHODIMP
  592. nsIndexedToHTML::OnStopRequest(nsIRequest* request, nsISupports *aContext,
  593. nsresult aStatus) {
  594. if (NS_SUCCEEDED(aStatus)) {
  595. nsCString buffer;
  596. buffer.AssignLiteral("</tbody></table></body></html>\n");
  597. aStatus = SendToListener(request, aContext, buffer);
  598. }
  599. mParser->OnStopRequest(request, aContext, aStatus);
  600. mParser = nullptr;
  601. return mListener->OnStopRequest(request, aContext, aStatus);
  602. }
  603. nsresult
  604. nsIndexedToHTML::SendToListener(nsIRequest* aRequest, nsISupports *aContext, const nsACString &aBuffer)
  605. {
  606. nsCOMPtr<nsIInputStream> inputData;
  607. nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputData), aBuffer);
  608. NS_ENSURE_SUCCESS(rv, rv);
  609. return mListener->OnDataAvailable(aRequest, aContext,
  610. inputData, 0, aBuffer.Length());
  611. }
  612. NS_IMETHODIMP
  613. nsIndexedToHTML::OnDataAvailable(nsIRequest *aRequest,
  614. nsISupports *aCtxt,
  615. nsIInputStream* aInput,
  616. uint64_t aOffset,
  617. uint32_t aCount) {
  618. return mParser->OnDataAvailable(aRequest, aCtxt, aInput, aOffset, aCount);
  619. }
  620. NS_IMETHODIMP
  621. nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
  622. nsISupports *aCtxt,
  623. nsIDirIndex *aIndex) {
  624. nsresult rv;
  625. if (!aIndex)
  626. return NS_ERROR_NULL_POINTER;
  627. nsCString pushBuffer;
  628. pushBuffer.AppendLiteral("<tr");
  629. // We don't know the file's character set yet, so retrieve the raw bytes
  630. // which will be decoded by the HTML parser.
  631. nsXPIDLCString loc;
  632. aIndex->GetLocation(getter_Copies(loc));
  633. // Adjust the length in case unescaping shortened the string.
  634. loc.Truncate(nsUnescapeCount(loc.BeginWriting()));
  635. if (loc.IsEmpty()) {
  636. return NS_ERROR_ILLEGAL_VALUE;
  637. }
  638. if (loc.First() == char16_t('.'))
  639. pushBuffer.AppendLiteral(" class=\"hidden-object\"");
  640. pushBuffer.AppendLiteral(">\n <td sortable-data=\"");
  641. // The sort key is the name of the item, prepended by either 0, 1 or 2
  642. // in order to group items.
  643. uint32_t type;
  644. aIndex->GetType(&type);
  645. switch (type) {
  646. case nsIDirIndex::TYPE_SYMLINK:
  647. pushBuffer.Append('0');
  648. break;
  649. case nsIDirIndex::TYPE_DIRECTORY:
  650. pushBuffer.Append('1');
  651. break;
  652. default:
  653. pushBuffer.Append('2');
  654. break;
  655. }
  656. nsAdoptingCString escaped(nsEscapeHTML(loc));
  657. pushBuffer.Append(escaped);
  658. pushBuffer.AppendLiteral("\"><table class=\"ellipsis\"><tbody><tr><td><a class=\"");
  659. switch (type) {
  660. case nsIDirIndex::TYPE_DIRECTORY:
  661. pushBuffer.AppendLiteral("dir");
  662. break;
  663. case nsIDirIndex::TYPE_SYMLINK:
  664. pushBuffer.AppendLiteral("symlink");
  665. break;
  666. default:
  667. pushBuffer.AppendLiteral("file");
  668. break;
  669. }
  670. pushBuffer.AppendLiteral("\" href=\"");
  671. // need to escape links
  672. nsAutoCString locEscaped;
  673. // Adding trailing slash helps to recognize whether the URL points to a file
  674. // or a directory (bug #214405).
  675. if ((type == nsIDirIndex::TYPE_DIRECTORY) && (loc.Last() != '/')) {
  676. loc.Append('/');
  677. }
  678. // now minimally re-escape the location...
  679. uint32_t escFlags;
  680. // for some protocols, we expect the location to be absolute.
  681. // if so, and if the location indeed appears to be a valid URI, then go
  682. // ahead and treat it like one.
  683. nsAutoCString scheme;
  684. if (mExpectAbsLoc &&
  685. NS_SUCCEEDED(net_ExtractURLScheme(loc, scheme))) {
  686. // escape as absolute
  687. escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal;
  688. }
  689. else {
  690. // escape as relative
  691. // esc_Directory is needed because directories have a trailing slash.
  692. // Without it, the trailing '/' will be escaped, and links from within
  693. // that directory will be incorrect
  694. escFlags = esc_Forced | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
  695. }
  696. NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped);
  697. // esc_Directory does not escape the semicolons, so if a filename
  698. // contains semicolons we need to manually escape them.
  699. // This replacement should be removed in bug #473280
  700. locEscaped.ReplaceSubstring(";", "%3b");
  701. nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get()));
  702. pushBuffer.Append(htmlEscapedURL);
  703. pushBuffer.AppendLiteral("\">");
  704. if (type == nsIDirIndex::TYPE_FILE || type == nsIDirIndex::TYPE_UNKNOWN) {
  705. pushBuffer.AppendLiteral("<img src=\"moz-icon://");
  706. int32_t lastDot = locEscaped.RFindChar('.');
  707. if (lastDot != kNotFound) {
  708. locEscaped.Cut(0, lastDot);
  709. nsAdoptingCString htmlFileExt(nsEscapeHTML(locEscaped.get()));
  710. pushBuffer.Append(htmlFileExt);
  711. } else {
  712. pushBuffer.AppendLiteral("unknown");
  713. }
  714. pushBuffer.AppendLiteral("?size=16\" alt=\"");
  715. nsXPIDLString altText;
  716. rv = mBundle->GetStringFromName(u"DirFileLabel",
  717. getter_Copies(altText));
  718. if (NS_FAILED(rv)) return rv;
  719. AppendNonAsciiToNCR(altText, pushBuffer);
  720. pushBuffer.AppendLiteral("\">");
  721. }
  722. pushBuffer.Append(escaped);
  723. pushBuffer.AppendLiteral("</a></td></tr></tbody></table></td>\n <td");
  724. if (type == nsIDirIndex::TYPE_DIRECTORY || type == nsIDirIndex::TYPE_SYMLINK) {
  725. pushBuffer.Append('>');
  726. } else {
  727. int64_t size;
  728. aIndex->GetSize(&size);
  729. if (uint64_t(size) != UINT64_MAX) {
  730. pushBuffer.AppendLiteral(" sortable-data=\"");
  731. pushBuffer.AppendInt(size);
  732. pushBuffer.AppendLiteral("\">");
  733. nsAutoCString sizeString;
  734. FormatSizeString(size, sizeString);
  735. pushBuffer.Append(sizeString);
  736. } else {
  737. pushBuffer.Append('>');
  738. }
  739. }
  740. pushBuffer.AppendLiteral("</td>\n <td");
  741. PRTime t;
  742. aIndex->GetLastModified(&t);
  743. if (t == -1LL) {
  744. pushBuffer.AppendLiteral("></td>\n <td>");
  745. } else {
  746. pushBuffer.AppendLiteral(" sortable-data=\"");
  747. pushBuffer.AppendInt(static_cast<int64_t>(t));
  748. pushBuffer.AppendLiteral("\">");
  749. nsAutoString formatted;
  750. mDateTime->FormatPRTime(nullptr,
  751. kDateFormatShort,
  752. kTimeFormatNone,
  753. t,
  754. formatted);
  755. AppendNonAsciiToNCR(formatted, pushBuffer);
  756. pushBuffer.AppendLiteral("</td>\n <td>");
  757. mDateTime->FormatPRTime(nullptr,
  758. kDateFormatNone,
  759. kTimeFormatSeconds,
  760. t,
  761. formatted);
  762. // use NCR to show date in any doc charset
  763. AppendNonAsciiToNCR(formatted, pushBuffer);
  764. }
  765. pushBuffer.AppendLiteral("</td>\n</tr>");
  766. return SendToListener(aRequest, aCtxt, pushBuffer);
  767. }
  768. NS_IMETHODIMP
  769. nsIndexedToHTML::OnInformationAvailable(nsIRequest *aRequest,
  770. nsISupports *aCtxt,
  771. const nsAString& aInfo) {
  772. nsAutoCString pushBuffer;
  773. nsAdoptingString escaped(nsEscapeHTML2(PromiseFlatString(aInfo).get()));
  774. if (!escaped)
  775. return NS_ERROR_OUT_OF_MEMORY;
  776. pushBuffer.AppendLiteral("<tr>\n <td>");
  777. // escaped is provided in Unicode, so write hex NCRs as necessary
  778. // to prevent the HTML parser from applying a character set.
  779. AppendNonAsciiToNCR(escaped, pushBuffer);
  780. pushBuffer.AppendLiteral("</td>\n <td></td>\n <td></td>\n <td></td>\n</tr>\n");
  781. return SendToListener(aRequest, aCtxt, pushBuffer);
  782. }
  783. void nsIndexedToHTML::FormatSizeString(int64_t inSize, nsCString& outSizeString)
  784. {
  785. outSizeString.Truncate();
  786. if (inSize > int64_t(0)) {
  787. // round up to the nearest Kilobyte
  788. int64_t upperSize = (inSize + int64_t(1023)) / int64_t(1024);
  789. outSizeString.AppendInt(upperSize);
  790. outSizeString.AppendLiteral(" KB");
  791. }
  792. }
  793. nsIndexedToHTML::nsIndexedToHTML() {
  794. }
  795. nsIndexedToHTML::~nsIndexedToHTML() {
  796. }