no bug - Correct some typos in the comments. a=typo-fix
[gecko.git] / netwerk / protocol / about / nsAboutCacheEntry.cpp
blob08dfcf6ca0014c5d0d7a932036f984107583bbe8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include <algorithm>
8 #include "nsAboutCacheEntry.h"
10 #include "CacheFileUtils.h"
11 #include "CacheObserver.h"
12 #include "mozilla/Sprintf.h"
13 #include "nsAboutCache.h"
14 #include "nsAboutProtocolUtils.h"
15 #include "nsContentUtils.h"
16 #include "nsEscape.h"
17 #include "nsIAsyncInputStream.h"
18 #include "nsIAsyncOutputStream.h"
19 #include "nsICacheStorage.h"
20 #include "nsIPipe.h"
21 #include "nsITransportSecurityInfo.h"
22 #include "nsInputStreamPump.h"
23 #include "nsNetUtil.h"
25 using namespace mozilla::net;
27 #define HEXDUMP_MAX_ROWS 16
29 static void HexDump(uint32_t* state, const char* buf, int32_t n,
30 nsCString& result) {
31 char temp[16];
33 const unsigned char* p;
34 while (n) {
35 SprintfLiteral(temp, "%08x: ", *state);
36 result.Append(temp);
37 *state += HEXDUMP_MAX_ROWS;
39 p = (const unsigned char*)buf;
41 int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
43 // print hex codes:
44 for (i = 0; i < row_max; ++i) {
45 SprintfLiteral(temp, "%02x ", *p++);
46 result.Append(temp);
48 for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
49 result.AppendLiteral(" ");
52 // print ASCII glyphs if possible:
53 p = (const unsigned char*)buf;
54 for (i = 0; i < row_max; ++i, ++p) {
55 switch (*p) {
56 case '<':
57 result.AppendLiteral("&lt;");
58 break;
59 case '>':
60 result.AppendLiteral("&gt;");
61 break;
62 case '&':
63 result.AppendLiteral("&amp;");
64 break;
65 default:
66 if (*p < 0x7F && *p > 0x1F) {
67 result.Append(*p);
68 } else {
69 result.Append('.');
74 result.Append('\n');
76 buf += row_max;
77 n -= row_max;
81 //-----------------------------------------------------------------------------
82 // nsAboutCacheEntry::nsISupports
84 NS_IMPL_ISUPPORTS(nsAboutCacheEntry, nsIAboutModule)
85 NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel, nsICacheEntryOpenCallback,
86 nsICacheEntryMetaDataVisitor, nsIStreamListener, nsIRequest,
87 nsIChannel)
89 //-----------------------------------------------------------------------------
90 // nsAboutCacheEntry::nsIAboutModule
92 NS_IMETHODIMP
93 nsAboutCacheEntry::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
94 nsIChannel** result) {
95 NS_ENSURE_ARG_POINTER(uri);
96 nsresult rv;
98 RefPtr<Channel> channel = new Channel();
99 rv = channel->Init(uri, aLoadInfo);
100 if (NS_FAILED(rv)) return rv;
102 channel.forget(result);
104 return NS_OK;
107 NS_IMETHODIMP
108 nsAboutCacheEntry::GetURIFlags(nsIURI* aURI, uint32_t* result) {
109 *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT |
110 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
111 return NS_OK;
114 NS_IMETHODIMP
115 nsAboutCacheEntry::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
116 return NS_ERROR_ILLEGAL_VALUE;
119 //-----------------------------------------------------------------------------
120 // nsAboutCacheEntry::Channel
122 nsresult nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo) {
123 nsresult rv;
125 nsCOMPtr<nsIInputStream> stream;
126 rv = GetContentStream(uri, getter_AddRefs(stream));
127 if (NS_FAILED(rv)) return rv;
129 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel), uri,
130 stream.forget(), "text/html"_ns,
131 "utf-8"_ns, aLoadInfo);
132 if (NS_FAILED(rv)) return rv;
134 return NS_OK;
137 nsresult nsAboutCacheEntry::Channel::GetContentStream(nsIURI* uri,
138 nsIInputStream** result) {
139 nsresult rv;
141 // Init: (block size, maximum length)
142 nsCOMPtr<nsIAsyncInputStream> inputStream;
143 NS_NewPipe2(getter_AddRefs(inputStream), getter_AddRefs(mOutputStream), true,
144 false, 256, UINT32_MAX);
146 constexpr auto buffer =
147 "<!DOCTYPE html>\n"
148 "<html>\n"
149 "<head>\n"
150 " <meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
151 "chrome:; object-src 'none'\" />\n"
152 " <meta name=\"color-scheme\" content=\"light dark\" />\n"
153 " <title>Cache entry information</title>\n"
154 " <link rel=\"stylesheet\" "
155 "href=\"chrome://global/skin/in-content/info-pages.css\" "
156 "type=\"text/css\"/>\n"
157 " <link rel=\"stylesheet\" "
158 "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
159 "</head>\n"
160 "<body>\n"
161 "<h1>Cache entry information</h1>\n"_ns;
162 uint32_t n;
163 rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
164 if (NS_FAILED(rv)) return rv;
165 if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
167 rv = OpenCacheEntry(uri);
168 if (NS_FAILED(rv)) return rv;
170 inputStream.forget(result);
171 return NS_OK;
174 nsresult nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI* uri) {
175 nsresult rv;
177 rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo), mEnhanceId,
178 getter_AddRefs(mCacheURI));
179 if (NS_FAILED(rv)) return rv;
181 return OpenCacheEntry();
184 nsresult nsAboutCacheEntry::Channel::OpenCacheEntry() {
185 nsresult rv;
187 nsCOMPtr<nsICacheStorage> storage;
188 rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo,
189 getter_AddRefs(storage));
190 if (NS_FAILED(rv)) return rv;
192 // Invokes OnCacheEntryAvailable()
193 rv = storage->AsyncOpenURI(
194 mCacheURI, mEnhanceId,
195 nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY, this);
196 if (NS_FAILED(rv)) return rv;
198 return NS_OK;
201 nsresult nsAboutCacheEntry::Channel::ParseURI(nsIURI* uri,
202 nsACString& storageName,
203 nsILoadContextInfo** loadInfo,
204 nsCString& enahnceID,
205 nsIURI** cacheUri) {
207 // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
209 nsresult rv;
211 nsAutoCString path;
212 rv = uri->GetPathQueryRef(path);
213 if (NS_FAILED(rv)) return rv;
215 nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end;
216 path.BeginReading(begin);
217 path.EndReading(end);
219 keyBegin = begin;
220 keyEnd = end;
221 if (!FindInReadable("?storage="_ns, keyBegin, keyEnd)) {
222 return NS_ERROR_FAILURE;
225 valBegin = keyEnd; // the value of the storage key starts after the key
227 keyBegin = keyEnd;
228 keyEnd = end;
229 if (!FindInReadable("&context="_ns, keyBegin, keyEnd)) {
230 return NS_ERROR_FAILURE;
233 storageName.Assign(Substring(valBegin, keyBegin));
234 valBegin = keyEnd; // the value of the context key starts after the key
236 keyBegin = keyEnd;
237 keyEnd = end;
238 if (!FindInReadable("&eid="_ns, keyBegin, keyEnd)) return NS_ERROR_FAILURE;
240 nsAutoCString contextKey(Substring(valBegin, keyBegin));
241 valBegin = keyEnd; // the value of the eid key starts after the key
243 keyBegin = keyEnd;
244 keyEnd = end;
245 if (!FindInReadable("&uri="_ns, keyBegin, keyEnd)) return NS_ERROR_FAILURE;
247 enahnceID.Assign(Substring(valBegin, keyBegin));
249 valBegin = keyEnd; // the value of the uri key starts after the key
250 nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one
252 // Uf... parsing done, now get some objects from it...
254 nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(contextKey);
255 if (!info) return NS_ERROR_FAILURE;
256 info.forget(loadInfo);
258 rv = NS_NewURI(cacheUri, uriSpec);
259 if (NS_FAILED(rv)) return rv;
261 return NS_OK;
264 //-----------------------------------------------------------------------------
265 // nsICacheEntryOpenCallback implementation
266 //-----------------------------------------------------------------------------
268 NS_IMETHODIMP
269 nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry* aEntry,
270 uint32_t* result) {
271 *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
272 return NS_OK;
275 NS_IMETHODIMP
276 nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry* entry,
277 bool isNew, nsresult status) {
278 nsresult rv;
280 mWaitingForData = false;
281 if (entry) {
282 rv = WriteCacheEntryDescription(entry);
283 } else {
284 rv = WriteCacheEntryUnavailable();
286 if (NS_FAILED(rv)) return rv;
288 if (!mWaitingForData) {
289 // Data is not expected, close the output of content now.
290 CloseContent();
293 return NS_OK;
296 //-----------------------------------------------------------------------------
297 // Print-out helper methods
298 //-----------------------------------------------------------------------------
300 #define APPEND_ROW(label, value) \
301 PR_BEGIN_MACRO \
302 buffer.AppendLiteral( \
303 " <tr>\n" \
304 " <th>"); \
305 buffer.AppendLiteral(label); \
306 buffer.AppendLiteral( \
307 ":</th>\n" \
308 " <td>"); \
309 buffer.Append(value); \
310 buffer.AppendLiteral( \
311 "</td>\n" \
312 " </tr>\n"); \
313 PR_END_MACRO
315 nsresult nsAboutCacheEntry::Channel::WriteCacheEntryDescription(
316 nsICacheEntry* entry) {
317 nsresult rv;
318 // This method appears to run in a situation where the run-time stack
319 // should have plenty of space, so allocating a large string on the
320 // stack is OK.
321 nsAutoCStringN<4097> buffer;
322 uint32_t n;
324 nsAutoCString str;
326 rv = entry->GetKey(str);
327 if (NS_FAILED(rv)) return rv;
329 buffer.AssignLiteral(
330 "<table>\n"
331 " <tr>\n"
332 " <th>key:</th>\n"
333 " <td id=\"td-key\">");
335 // Test if the key is actually a URI
336 nsCOMPtr<nsIURI> uri;
337 rv = NS_NewURI(getter_AddRefs(uri), str);
339 nsAutoCString escapedStr;
340 nsAppendEscapedHTML(str, escapedStr);
342 // javascript: and data: URLs should not be linkified
343 // since clicking them can cause scripts to run - bug 162584
344 if (NS_SUCCEEDED(rv) &&
345 !(uri->SchemeIs("javascript") || uri->SchemeIs("data"))) {
346 buffer.AppendLiteral("<a href=\"");
347 buffer.Append(escapedStr);
348 buffer.AppendLiteral("\">");
349 buffer.Append(escapedStr);
350 buffer.AppendLiteral("</a>");
351 uri = nullptr;
352 } else {
353 buffer.Append(escapedStr);
355 buffer.AppendLiteral(
356 "</td>\n"
357 " </tr>\n");
359 // temp vars for reporting
360 char timeBuf[255];
361 uint32_t u = 0;
362 nsAutoCString s;
364 // Fetch Count
365 s.Truncate();
366 entry->GetFetchCount(&u);
367 s.AppendInt(u);
368 APPEND_ROW("fetch count", s);
370 // Last Fetched
371 entry->GetLastFetched(&u);
372 if (u) {
373 PrintTimeString(timeBuf, sizeof(timeBuf), u);
374 APPEND_ROW("last fetched", timeBuf);
375 } else {
376 APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
379 // Last Modified
380 entry->GetLastModified(&u);
381 if (u) {
382 PrintTimeString(timeBuf, sizeof(timeBuf), u);
383 APPEND_ROW("last modified", timeBuf);
384 } else {
385 APPEND_ROW("last modified", "No last modified time (bug 1000338)");
388 // Expiration Time
389 entry->GetExpirationTime(&u);
391 // Bug - 633747.
392 // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing.
393 // So we check if time is 0, then we show a message, "Expired Immediately"
394 if (u == 0) {
395 APPEND_ROW("expires", "Expired Immediately");
396 } else if (u < 0xFFFFFFFF) {
397 PrintTimeString(timeBuf, sizeof(timeBuf), u);
398 APPEND_ROW("expires", timeBuf);
399 } else {
400 APPEND_ROW("expires", "No expiration time");
403 // Data Size
404 s.Truncate();
405 uint32_t dataSize;
406 if (NS_FAILED(entry->GetStorageDataSize(&dataSize))) dataSize = 0;
407 s.AppendInt(
408 (int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
409 s.AppendLiteral(" B");
410 APPEND_ROW("Data size", s);
412 // TODO - mayhemer
413 // Here used to be a link to the disk file (in the old cache for entries that
414 // did not fit any of the block files, in the new cache every time).
415 // I'd rather have a small set of buttons here to action on the entry:
416 // 1. save the content
417 // 2. save as a complete HTTP response (response head, headers, content)
418 // 3. doom the entry
419 // A new bug(s) should be filed here.
421 // Security Info
422 nsCOMPtr<nsITransportSecurityInfo> securityInfo;
423 entry->GetSecurityInfo(getter_AddRefs(securityInfo));
424 if (securityInfo) {
425 APPEND_ROW("Security", "This is a secure document.");
426 } else {
427 APPEND_ROW(
428 "Security",
429 "This document does not have any security info associated with it.");
432 buffer.AppendLiteral(
433 "</table>\n"
434 "<hr/>\n"
435 "<table>\n");
437 mBuffer = &buffer; // make it available for OnMetaDataElement().
438 entry->VisitMetaData(this);
439 mBuffer = nullptr;
441 buffer.AppendLiteral("</table>\n");
442 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
443 buffer.Truncate();
445 // Provide a hexdump of the data
446 if (!dataSize) {
447 return NS_OK;
450 nsCOMPtr<nsIInputStream> stream;
451 entry->OpenInputStream(0, getter_AddRefs(stream));
452 if (!stream) {
453 return NS_OK;
456 RefPtr<nsInputStreamPump> pump;
457 rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
458 if (NS_FAILED(rv)) {
459 return NS_OK; // just ignore
462 rv = pump->AsyncRead(this);
463 if (NS_FAILED(rv)) {
464 return NS_OK; // just ignore
467 mWaitingForData = true;
468 return NS_OK;
471 nsresult nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable() {
472 uint32_t n;
473 constexpr auto buffer = "The cache entry you selected is not available."_ns;
474 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
475 return NS_OK;
478 //-----------------------------------------------------------------------------
479 // nsICacheEntryMetaDataVisitor implementation
480 //-----------------------------------------------------------------------------
482 NS_IMETHODIMP
483 nsAboutCacheEntry::Channel::OnMetaDataElement(char const* key,
484 char const* value) {
485 mBuffer->AppendLiteral(
486 " <tr>\n"
487 " <th>");
488 mBuffer->Append(key);
489 mBuffer->AppendLiteral(
490 ":</th>\n"
491 " <td>");
492 nsAppendEscapedHTML(nsDependentCString(value), *mBuffer);
493 mBuffer->AppendLiteral(
494 "</td>\n"
495 " </tr>\n");
497 return NS_OK;
500 //-----------------------------------------------------------------------------
501 // nsIStreamListener implementation
502 //-----------------------------------------------------------------------------
504 NS_IMETHODIMP
505 nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest* request) {
506 mHexDumpState = 0;
508 constexpr auto buffer = "<hr/>\n<pre>"_ns;
509 uint32_t n;
510 return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
513 NS_IMETHODIMP
514 nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest* request,
515 nsIInputStream* aInputStream,
516 uint64_t aOffset, uint32_t aCount) {
517 uint32_t n;
518 return aInputStream->ReadSegments(&nsAboutCacheEntry::Channel::PrintCacheData,
519 this, aCount, &n);
522 /* static */
523 nsresult nsAboutCacheEntry::Channel::PrintCacheData(
524 nsIInputStream* aInStream, void* aClosure, const char* aFromSegment,
525 uint32_t aToOffset, uint32_t aCount, uint32_t* aWriteCount) {
526 nsAboutCacheEntry::Channel* a =
527 static_cast<nsAboutCacheEntry::Channel*>(aClosure);
529 nsCString buffer;
530 HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer);
532 uint32_t n;
533 a->mOutputStream->Write(buffer.get(), buffer.Length(), &n);
535 *aWriteCount = aCount;
537 return NS_OK;
540 NS_IMETHODIMP
541 nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest* request,
542 nsresult result) {
543 constexpr auto buffer = "</pre>\n"_ns;
544 uint32_t n;
545 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
547 CloseContent();
549 return NS_OK;
552 void nsAboutCacheEntry::Channel::CloseContent() {
553 constexpr auto buffer = "</body>\n</html>\n"_ns;
554 uint32_t n;
555 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
557 mOutputStream->Close();
558 mOutputStream = nullptr;