nsAboutCache.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /* -*- Mode: C++; tab-width: 2; 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 "nsAboutCache.h"
  6. #include "nsIInputStream.h"
  7. #include "nsIStorageStream.h"
  8. #include "nsIURI.h"
  9. #include "nsCOMPtr.h"
  10. #include "nsNetUtil.h"
  11. #include "nsIPipe.h"
  12. #include "nsContentUtils.h"
  13. #include "nsEscape.h"
  14. #include "nsAboutProtocolUtils.h"
  15. #include "nsPrintfCString.h"
  16. #include "nsICacheStorageService.h"
  17. #include "nsICacheStorage.h"
  18. #include "CacheFileUtils.h"
  19. #include "CacheObserver.h"
  20. #include "nsThreadUtils.h"
  21. using namespace mozilla::net;
  22. NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule)
  23. NS_IMPL_ISUPPORTS(nsAboutCache::Channel, nsIChannel, nsIRequest, nsICacheStorageVisitor)
  24. NS_IMETHODIMP
  25. nsAboutCache::NewChannel(nsIURI* aURI,
  26. nsILoadInfo* aLoadInfo,
  27. nsIChannel** result)
  28. {
  29. nsresult rv;
  30. NS_ENSURE_ARG_POINTER(aURI);
  31. RefPtr<Channel> channel = new Channel();
  32. rv = channel->Init(aURI, aLoadInfo);
  33. if (NS_FAILED(rv)) return rv;
  34. channel.forget(result);
  35. return NS_OK;
  36. }
  37. nsresult
  38. nsAboutCache::Channel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo)
  39. {
  40. nsresult rv;
  41. mCancel = false;
  42. nsCOMPtr<nsIInputStream> inputStream;
  43. rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(mStream),
  44. 16384, (uint32_t)-1,
  45. true, // non-blocking input
  46. false // blocking output
  47. );
  48. if (NS_FAILED(rv)) return rv;
  49. nsAutoCString storageName;
  50. rv = ParseURI(aURI, storageName);
  51. if (NS_FAILED(rv)) return rv;
  52. mOverview = storageName.IsEmpty();
  53. if (mOverview) {
  54. // ...and visit all we can
  55. mStorageList.AppendElement(NS_LITERAL_CSTRING("memory"));
  56. mStorageList.AppendElement(NS_LITERAL_CSTRING("disk"));
  57. mStorageList.AppendElement(NS_LITERAL_CSTRING("appcache"));
  58. } else {
  59. // ...and visit just the specified storage, entries will output too
  60. mStorageList.AppendElement(storageName);
  61. }
  62. // The entries header is added on encounter of the first entry
  63. mEntriesHeaderAdded = false;
  64. rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
  65. aURI,
  66. inputStream,
  67. NS_LITERAL_CSTRING("text/html"),
  68. NS_LITERAL_CSTRING("utf-8"),
  69. aLoadInfo);
  70. if (NS_FAILED(rv)) return rv;
  71. mBuffer.AssignLiteral(
  72. "<!DOCTYPE html>\n"
  73. "<html>\n"
  74. "<head>\n"
  75. " <title>Network Cache Storage Information</title>\n"
  76. " <meta charset=\"utf-8\">\n"
  77. " <link rel=\"stylesheet\" href=\"chrome://global/skin/about.css\"/>\n"
  78. " <link rel=\"stylesheet\" href=\"chrome://global/skin/aboutCache.css\"/>\n"
  79. " <script src=\"chrome://global/content/aboutCache.js\"></script>"
  80. "</head>\n"
  81. "<body class=\"aboutPageWideContainer\">\n"
  82. "<h1>Information about the Network Cache Storage Service</h1>\n");
  83. // Add the context switch controls
  84. mBuffer.AppendLiteral(
  85. "<label><input id='priv' type='checkbox'/> Private</label>\n"
  86. "<label><input id='anon' type='checkbox'/> Anonymous</label>\n"
  87. );
  88. if (CacheObserver::UseNewCache()) {
  89. // Visit scoping by browser and appid is not implemented for
  90. // the old cache, simply don't add these controls.
  91. // The appid/inbrowser entries are already mixed in the default
  92. // view anyway.
  93. mBuffer.AppendLiteral(
  94. "<label><input id='appid' type='text' size='6'/> AppID</label>\n"
  95. "<label><input id='inbrowser' type='checkbox'/> In Browser Element</label>\n"
  96. );
  97. }
  98. mBuffer.AppendLiteral(
  99. "<label><input id='submit' type='button' value='Update' onclick='navigate()'/></label>\n"
  100. );
  101. if (!mOverview) {
  102. mBuffer.AppendLiteral("<a href=\"about:cache?storage=&amp;context=");
  103. char* escapedContext = nsEscapeHTML(mContextString.get());
  104. mBuffer.Append(escapedContext);
  105. free(escapedContext);
  106. mBuffer.AppendLiteral("\">Back to overview</a>");
  107. }
  108. rv = FlushBuffer();
  109. if (NS_FAILED(rv)) {
  110. NS_WARNING("Failed to flush buffer");
  111. }
  112. return NS_OK;
  113. }
  114. NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
  115. {
  116. nsresult rv;
  117. if (!mChannel) {
  118. return NS_ERROR_UNEXPECTED;
  119. }
  120. // Kick the walk loop.
  121. rv = VisitNextStorage();
  122. if (NS_FAILED(rv)) return rv;
  123. MOZ_ASSERT(!aContext, "asyncOpen2() does not take a context argument");
  124. rv = NS_MaybeOpenChannelUsingAsyncOpen2(mChannel, aListener);
  125. if (NS_FAILED(rv)) return rv;
  126. return NS_OK;
  127. }
  128. NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen2(nsIStreamListener *aListener)
  129. {
  130. return AsyncOpen(aListener, nullptr);
  131. }
  132. NS_IMETHODIMP nsAboutCache::Channel::Open(nsIInputStream * *_retval)
  133. {
  134. return NS_ERROR_NOT_IMPLEMENTED;
  135. }
  136. NS_IMETHODIMP nsAboutCache::Channel::Open2(nsIInputStream * *_retval)
  137. {
  138. return NS_ERROR_NOT_IMPLEMENTED;
  139. }
  140. nsresult
  141. nsAboutCache::Channel::ParseURI(nsIURI * uri, nsACString & storage)
  142. {
  143. //
  144. // about:cache[?storage=<storage-name>[&context=<context-key>]]
  145. //
  146. nsresult rv;
  147. nsAutoCString path;
  148. rv = uri->GetPath(path);
  149. if (NS_FAILED(rv)) return rv;
  150. mContextString.Truncate();
  151. mLoadInfo = CacheFileUtils::ParseKey(NS_LITERAL_CSTRING(""));
  152. storage.Truncate();
  153. nsACString::const_iterator start, valueStart, end;
  154. path.BeginReading(start);
  155. path.EndReading(end);
  156. valueStart = end;
  157. if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), start, valueStart)) {
  158. return NS_OK;
  159. }
  160. nsACString::const_iterator storageNameBegin = valueStart;
  161. start = valueStart;
  162. valueStart = end;
  163. if (!FindInReadable(NS_LITERAL_CSTRING("&context="), start, valueStart))
  164. start = end;
  165. nsACString::const_iterator storageNameEnd = start;
  166. mContextString = Substring(valueStart, end);
  167. mLoadInfo = CacheFileUtils::ParseKey(mContextString);
  168. storage.Assign(Substring(storageNameBegin, storageNameEnd));
  169. return NS_OK;
  170. }
  171. nsresult
  172. nsAboutCache::Channel::VisitNextStorage()
  173. {
  174. if (!mStorageList.Length())
  175. return NS_ERROR_NOT_AVAILABLE;
  176. mStorageName = mStorageList[0];
  177. mStorageList.RemoveElementAt(0);
  178. // Must re-dispatch since we cannot start another visit cycle
  179. // from visitor callback. The cache v1 service doesn't like it.
  180. // TODO - mayhemer, bug 913828, remove this dispatch and call
  181. // directly.
  182. return NS_DispatchToMainThread(mozilla::NewRunnableMethod(this, &nsAboutCache::Channel::FireVisitStorage));
  183. }
  184. void
  185. nsAboutCache::Channel::FireVisitStorage()
  186. {
  187. nsresult rv;
  188. rv = VisitStorage(mStorageName);
  189. if (NS_FAILED(rv)) {
  190. if (mLoadInfo) {
  191. char* escaped = nsEscapeHTML(mStorageName.get());
  192. mBuffer.Append(
  193. nsPrintfCString("<p>Unrecognized storage name '%s' in about:cache URL</p>",
  194. escaped));
  195. free(escaped);
  196. } else {
  197. char* escaped = nsEscapeHTML(mContextString.get());
  198. mBuffer.Append(
  199. nsPrintfCString("<p>Unrecognized context key '%s' in about:cache URL</p>",
  200. escaped));
  201. free(escaped);
  202. }
  203. rv = FlushBuffer();
  204. if (NS_FAILED(rv)) {
  205. NS_WARNING("Failed to flush buffer");
  206. }
  207. // Simulate finish of a visit cycle, this tries the next storage
  208. // or closes the output stream (i.e. the UI loader will stop spinning)
  209. OnCacheEntryVisitCompleted();
  210. }
  211. }
  212. nsresult
  213. nsAboutCache::Channel::VisitStorage(nsACString const & storageName)
  214. {
  215. nsresult rv;
  216. rv = GetStorage(storageName, mLoadInfo, getter_AddRefs(mStorage));
  217. if (NS_FAILED(rv)) return rv;
  218. rv = mStorage->AsyncVisitStorage(this, !mOverview);
  219. if (NS_FAILED(rv)) return rv;
  220. return NS_OK;
  221. }
  222. //static
  223. nsresult
  224. nsAboutCache::GetStorage(nsACString const & storageName,
  225. nsILoadContextInfo* loadInfo,
  226. nsICacheStorage **storage)
  227. {
  228. nsresult rv;
  229. nsCOMPtr<nsICacheStorageService> cacheService =
  230. do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
  231. if (NS_FAILED(rv)) return rv;
  232. nsCOMPtr<nsICacheStorage> cacheStorage;
  233. if (storageName == "disk") {
  234. rv = cacheService->DiskCacheStorage(
  235. loadInfo, false, getter_AddRefs(cacheStorage));
  236. } else if (storageName == "memory") {
  237. rv = cacheService->MemoryCacheStorage(
  238. loadInfo, getter_AddRefs(cacheStorage));
  239. } else if (storageName == "appcache") {
  240. rv = cacheService->AppCacheStorage(
  241. loadInfo, nullptr, getter_AddRefs(cacheStorage));
  242. } else {
  243. rv = NS_ERROR_UNEXPECTED;
  244. }
  245. if (NS_FAILED(rv)) return rv;
  246. cacheStorage.forget(storage);
  247. return NS_OK;
  248. }
  249. NS_IMETHODIMP
  250. nsAboutCache::Channel::OnCacheStorageInfo(uint32_t aEntryCount, uint64_t aConsumption,
  251. uint64_t aCapacity, nsIFile * aDirectory)
  252. {
  253. // We need mStream for this
  254. if (!mStream) {
  255. return NS_ERROR_FAILURE;
  256. }
  257. mBuffer.AssignLiteral("<h2>");
  258. mBuffer.Append(mStorageName);
  259. mBuffer.AppendLiteral("</h2>\n"
  260. "<table id=\"");
  261. mBuffer.AppendLiteral("\">\n");
  262. // Write out cache info
  263. // Number of entries
  264. mBuffer.AppendLiteral(" <tr>\n"
  265. " <th>Number of entries:</th>\n"
  266. " <td>");
  267. mBuffer.AppendInt(aEntryCount);
  268. mBuffer.AppendLiteral("</td>\n"
  269. " </tr>\n");
  270. // Maximum storage size
  271. mBuffer.AppendLiteral(" <tr>\n"
  272. " <th>Maximum storage size:</th>\n"
  273. " <td>");
  274. mBuffer.AppendInt(aCapacity / 1024);
  275. mBuffer.AppendLiteral(" KiB</td>\n"
  276. " </tr>\n");
  277. // Storage in use
  278. mBuffer.AppendLiteral(" <tr>\n"
  279. " <th>Storage in use:</th>\n"
  280. " <td>");
  281. mBuffer.AppendInt(aConsumption / 1024);
  282. mBuffer.AppendLiteral(" KiB</td>\n"
  283. " </tr>\n");
  284. // Storage disk location
  285. mBuffer.AppendLiteral(" <tr>\n"
  286. " <th>Storage disk location:</th>\n"
  287. " <td>");
  288. if (aDirectory) {
  289. nsAutoString path;
  290. aDirectory->GetPath(path);
  291. mBuffer.Append(NS_ConvertUTF16toUTF8(path));
  292. } else {
  293. mBuffer.AppendLiteral("none, only stored in memory");
  294. }
  295. mBuffer.AppendLiteral(" </td>\n"
  296. " </tr>\n");
  297. if (mOverview) { // The about:cache case
  298. if (aEntryCount != 0) { // Add the "List Cache Entries" link
  299. mBuffer.AppendLiteral(" <tr>\n"
  300. " <th><a href=\"about:cache?storage=");
  301. mBuffer.Append(mStorageName);
  302. mBuffer.AppendLiteral("&amp;context=");
  303. char* escapedContext = nsEscapeHTML(mContextString.get());
  304. mBuffer.Append(escapedContext);
  305. free(escapedContext);
  306. mBuffer.AppendLiteral("\">List Cache Entries</a></th>\n"
  307. " </tr>\n");
  308. }
  309. }
  310. mBuffer.AppendLiteral("</table>\n");
  311. // The entries header is added on encounter of the first entry
  312. mEntriesHeaderAdded = false;
  313. nsresult rv = FlushBuffer();
  314. if (NS_FAILED(rv)) {
  315. NS_WARNING("Failed to flush buffer");
  316. }
  317. if (mOverview) {
  318. // OnCacheEntryVisitCompleted() is not called when we do not iterate
  319. // cache entries. Since this moves forward to the next storage in
  320. // the list we want to visit, artificially call it here.
  321. OnCacheEntryVisitCompleted();
  322. }
  323. return NS_OK;
  324. }
  325. NS_IMETHODIMP
  326. nsAboutCache::Channel::OnCacheEntryInfo(nsIURI *aURI, const nsACString & aIdEnhance,
  327. int64_t aDataSize, int32_t aFetchCount,
  328. uint32_t aLastModified, uint32_t aExpirationTime,
  329. bool aPinned)
  330. {
  331. // We need mStream for this
  332. if (!mStream || mCancel) {
  333. // Returning a failure from this callback stops the iteration
  334. return NS_ERROR_FAILURE;
  335. }
  336. if (!mEntriesHeaderAdded) {
  337. mBuffer.AppendLiteral("<hr/>\n"
  338. "<table id=\"entries\">\n"
  339. " <colgroup>\n"
  340. " <col id=\"col-key\">\n"
  341. " <col id=\"col-dataSize\">\n"
  342. " <col id=\"col-fetchCount\">\n"
  343. " <col id=\"col-lastModified\">\n"
  344. " <col id=\"col-expires\">\n"
  345. " <col id=\"col-pinned\">\n"
  346. " </colgroup>\n"
  347. " <thead>\n"
  348. " <tr>\n"
  349. " <th>Key</th>\n"
  350. " <th>Data size</th>\n"
  351. " <th>Fetch count</th>\n"
  352. " <th>Last Modifed</th>\n"
  353. " <th>Expires</th>\n"
  354. " <th>Pinning</th>\n"
  355. " </tr>\n"
  356. " </thead>\n");
  357. mEntriesHeaderAdded = true;
  358. }
  359. // Generate a about:cache-entry URL for this entry...
  360. nsAutoCString url;
  361. url.AssignLiteral("about:cache-entry?storage=");
  362. url.Append(mStorageName);
  363. url.AppendLiteral("&amp;context=");
  364. char* escapedContext = nsEscapeHTML(mContextString.get());
  365. url += escapedContext;
  366. free(escapedContext);
  367. url.AppendLiteral("&amp;eid=");
  368. char* escapedEID = nsEscapeHTML(aIdEnhance.BeginReading());
  369. url += escapedEID;
  370. free(escapedEID);
  371. nsAutoCString cacheUriSpec;
  372. aURI->GetAsciiSpec(cacheUriSpec);
  373. char* escapedCacheURI = nsEscapeHTML(cacheUriSpec.get());
  374. url.AppendLiteral("&amp;uri=");
  375. url += escapedCacheURI;
  376. // Entry start...
  377. mBuffer.AppendLiteral(" <tr>\n");
  378. // URI
  379. mBuffer.AppendLiteral(" <td><a href=\"");
  380. mBuffer.Append(url);
  381. mBuffer.AppendLiteral("\">");
  382. if (!aIdEnhance.IsEmpty()) {
  383. mBuffer.Append(aIdEnhance);
  384. mBuffer.Append(':');
  385. }
  386. mBuffer.Append(escapedCacheURI);
  387. mBuffer.AppendLiteral("</a></td>\n");
  388. free(escapedCacheURI);
  389. // Content length
  390. mBuffer.AppendLiteral(" <td>");
  391. mBuffer.AppendInt(aDataSize);
  392. mBuffer.AppendLiteral(" bytes</td>\n");
  393. // Number of accesses
  394. mBuffer.AppendLiteral(" <td>");
  395. mBuffer.AppendInt(aFetchCount);
  396. mBuffer.AppendLiteral("</td>\n");
  397. // vars for reporting time
  398. char buf[255];
  399. // Last modified time
  400. mBuffer.AppendLiteral(" <td>");
  401. if (aLastModified) {
  402. PrintTimeString(buf, sizeof(buf), aLastModified);
  403. mBuffer.Append(buf);
  404. } else {
  405. mBuffer.AppendLiteral("No last modified time");
  406. }
  407. mBuffer.AppendLiteral("</td>\n");
  408. // Expires time
  409. mBuffer.AppendLiteral(" <td>");
  410. if (aExpirationTime < 0xFFFFFFFF) {
  411. PrintTimeString(buf, sizeof(buf), aExpirationTime);
  412. mBuffer.Append(buf);
  413. } else {
  414. mBuffer.AppendLiteral("No expiration time");
  415. }
  416. mBuffer.AppendLiteral("</td>\n");
  417. // Pinning
  418. mBuffer.AppendLiteral(" <td>");
  419. if (aPinned) {
  420. mBuffer.Append(NS_LITERAL_CSTRING("Pinned"));
  421. } else {
  422. mBuffer.Append(NS_LITERAL_CSTRING("&nbsp;"));
  423. }
  424. mBuffer.AppendLiteral("</td>\n");
  425. // Entry is done...
  426. mBuffer.AppendLiteral(" </tr>\n");
  427. return FlushBuffer();
  428. }
  429. NS_IMETHODIMP
  430. nsAboutCache::Channel::OnCacheEntryVisitCompleted()
  431. {
  432. if (!mStream) {
  433. return NS_ERROR_FAILURE;
  434. }
  435. if (mEntriesHeaderAdded) {
  436. mBuffer.AppendLiteral("</table>\n");
  437. }
  438. // Kick another storage visiting (from a storage that allows us.)
  439. while (mStorageList.Length()) {
  440. nsresult rv = VisitNextStorage();
  441. if (NS_SUCCEEDED(rv)) {
  442. // Expecting new round of OnCache* calls.
  443. return NS_OK;
  444. }
  445. }
  446. // We are done!
  447. mBuffer.AppendLiteral("</body>\n"
  448. "</html>\n");
  449. nsresult rv = FlushBuffer();
  450. if (NS_FAILED(rv)) {
  451. NS_WARNING("Failed to flush buffer");
  452. }
  453. mStream->Close();
  454. return NS_OK;
  455. }
  456. nsresult
  457. nsAboutCache::Channel::FlushBuffer()
  458. {
  459. nsresult rv;
  460. uint32_t bytesWritten;
  461. rv = mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
  462. mBuffer.Truncate();
  463. if (NS_FAILED(rv)) {
  464. mCancel = true;
  465. }
  466. return rv;
  467. }
  468. NS_IMETHODIMP
  469. nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result)
  470. {
  471. *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
  472. return NS_OK;
  473. }
  474. // static
  475. nsresult
  476. nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
  477. {
  478. nsAboutCache* about = new nsAboutCache();
  479. if (about == nullptr)
  480. return NS_ERROR_OUT_OF_MEMORY;
  481. NS_ADDREF(about);
  482. nsresult rv = about->QueryInterface(aIID, aResult);
  483. NS_RELEASE(about);
  484. return rv;
  485. }
  486. ////////////////////////////////////////////////////////////////////////////////