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/. */
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"
17 #include "nsIAsyncInputStream.h"
18 #include "nsIAsyncOutputStream.h"
19 #include "nsICacheStorage.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
,
33 const unsigned char* p
;
35 SprintfLiteral(temp
, "%08x: ", *state
);
37 *state
+= HEXDUMP_MAX_ROWS
;
39 p
= (const unsigned char*)buf
;
41 int32_t i
, row_max
= std::min(HEXDUMP_MAX_ROWS
, n
);
44 for (i
= 0; i
< row_max
; ++i
) {
45 SprintfLiteral(temp
, "%02x ", *p
++);
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
) {
57 result
.AppendLiteral("<");
60 result
.AppendLiteral(">");
63 result
.AppendLiteral("&");
66 if (*p
< 0x7F && *p
> 0x1F) {
81 //-----------------------------------------------------------------------------
82 // nsAboutCacheEntry::nsISupports
84 NS_IMPL_ISUPPORTS(nsAboutCacheEntry
, nsIAboutModule
)
85 NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel
, nsICacheEntryOpenCallback
,
86 nsICacheEntryMetaDataVisitor
, nsIStreamListener
, nsIRequest
,
89 //-----------------------------------------------------------------------------
90 // nsAboutCacheEntry::nsIAboutModule
93 nsAboutCacheEntry::NewChannel(nsIURI
* uri
, nsILoadInfo
* aLoadInfo
,
94 nsIChannel
** result
) {
95 NS_ENSURE_ARG_POINTER(uri
);
98 RefPtr
<Channel
> channel
= new Channel();
99 rv
= channel
->Init(uri
, aLoadInfo
);
100 if (NS_FAILED(rv
)) return rv
;
102 channel
.forget(result
);
108 nsAboutCacheEntry::GetURIFlags(nsIURI
* aURI
, uint32_t* result
) {
109 *result
= nsIAboutModule::HIDE_FROM_ABOUTABOUT
|
110 nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
;
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
) {
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
;
137 nsresult
nsAboutCacheEntry::Channel::GetContentStream(nsIURI
* uri
,
138 nsIInputStream
** result
) {
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
=
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"
161 "<h1>Cache entry information</h1>\n"_ns
;
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
);
174 nsresult
nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI
* uri
) {
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() {
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
;
201 nsresult
nsAboutCacheEntry::Channel::ParseURI(nsIURI
* uri
,
202 nsACString
& storageName
,
203 nsILoadContextInfo
** loadInfo
,
204 nsCString
& enahnceID
,
207 // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
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
);
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
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
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
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
;
264 //-----------------------------------------------------------------------------
265 // nsICacheEntryOpenCallback implementation
266 //-----------------------------------------------------------------------------
269 nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry
* aEntry
,
271 *result
= nsICacheEntryOpenCallback::ENTRY_WANTED
;
276 nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry
* entry
,
277 bool isNew
, nsresult status
) {
280 mWaitingForData
= false;
282 rv
= WriteCacheEntryDescription(entry
);
284 rv
= WriteCacheEntryUnavailable();
286 if (NS_FAILED(rv
)) return rv
;
288 if (!mWaitingForData
) {
289 // Data is not expected, close the output of content now.
296 //-----------------------------------------------------------------------------
297 // Print-out helper methods
298 //-----------------------------------------------------------------------------
300 #define APPEND_ROW(label, value) \
302 buffer.AppendLiteral( \
305 buffer.AppendLiteral(label); \
306 buffer.AppendLiteral( \
309 buffer.Append(value); \
310 buffer.AppendLiteral( \
315 nsresult
nsAboutCacheEntry::Channel::WriteCacheEntryDescription(
316 nsICacheEntry
* entry
) {
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
321 nsAutoCStringN
<4097> buffer
;
326 rv
= entry
->GetKey(str
);
327 if (NS_FAILED(rv
)) return rv
;
329 buffer
.AssignLiteral(
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>");
353 buffer
.Append(escapedStr
);
355 buffer
.AppendLiteral(
359 // temp vars for reporting
366 entry
->GetFetchCount(&u
);
368 APPEND_ROW("fetch count", s
);
371 entry
->GetLastFetched(&u
);
373 PrintTimeString(timeBuf
, sizeof(timeBuf
), u
);
374 APPEND_ROW("last fetched", timeBuf
);
376 APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
380 entry
->GetLastModified(&u
);
382 PrintTimeString(timeBuf
, sizeof(timeBuf
), u
);
383 APPEND_ROW("last modified", timeBuf
);
385 APPEND_ROW("last modified", "No last modified time (bug 1000338)");
389 entry
->GetExpirationTime(&u
);
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"
395 APPEND_ROW("expires", "Expired Immediately");
396 } else if (u
< 0xFFFFFFFF) {
397 PrintTimeString(timeBuf
, sizeof(timeBuf
), u
);
398 APPEND_ROW("expires", timeBuf
);
400 APPEND_ROW("expires", "No expiration time");
406 if (NS_FAILED(entry
->GetStorageDataSize(&dataSize
))) dataSize
= 0;
408 (int32_t)dataSize
); // XXX nsICacheEntryInfo interfaces should be fixed.
409 s
.AppendLiteral(" B");
410 APPEND_ROW("Data size", s
);
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)
419 // A new bug(s) should be filed here.
422 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo
;
423 entry
->GetSecurityInfo(getter_AddRefs(securityInfo
));
425 APPEND_ROW("Security", "This is a secure document.");
429 "This document does not have any security info associated with it.");
432 buffer
.AppendLiteral(
437 mBuffer
= &buffer
; // make it available for OnMetaDataElement().
438 entry
->VisitMetaData(this);
441 buffer
.AppendLiteral("</table>\n");
442 mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
445 // Provide a hexdump of the data
450 nsCOMPtr
<nsIInputStream
> stream
;
451 entry
->OpenInputStream(0, getter_AddRefs(stream
));
456 RefPtr
<nsInputStreamPump
> pump
;
457 rv
= nsInputStreamPump::Create(getter_AddRefs(pump
), stream
);
459 return NS_OK
; // just ignore
462 rv
= pump
->AsyncRead(this);
464 return NS_OK
; // just ignore
467 mWaitingForData
= true;
471 nsresult
nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable() {
473 constexpr auto buffer
= "The cache entry you selected is not available."_ns
;
474 mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
478 //-----------------------------------------------------------------------------
479 // nsICacheEntryMetaDataVisitor implementation
480 //-----------------------------------------------------------------------------
483 nsAboutCacheEntry::Channel::OnMetaDataElement(char const* key
,
485 mBuffer
->AppendLiteral(
488 mBuffer
->Append(key
);
489 mBuffer
->AppendLiteral(
492 nsAppendEscapedHTML(nsDependentCString(value
), *mBuffer
);
493 mBuffer
->AppendLiteral(
500 //-----------------------------------------------------------------------------
501 // nsIStreamListener implementation
502 //-----------------------------------------------------------------------------
505 nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest
* request
) {
508 constexpr auto buffer
= "<hr/>\n<pre>"_ns
;
510 return mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
514 nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest
* request
,
515 nsIInputStream
* aInputStream
,
516 uint64_t aOffset
, uint32_t aCount
) {
518 return aInputStream
->ReadSegments(&nsAboutCacheEntry::Channel::PrintCacheData
,
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
);
530 HexDump(&a
->mHexDumpState
, aFromSegment
, aCount
, buffer
);
533 a
->mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
535 *aWriteCount
= aCount
;
541 nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest
* request
,
543 constexpr auto buffer
= "</pre>\n"_ns
;
545 mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
552 void nsAboutCacheEntry::Channel::CloseContent() {
553 constexpr auto buffer
= "</body>\n</html>\n"_ns
;
555 mOutputStream
->Write(buffer
.get(), buffer
.Length(), &n
);
557 mOutputStream
->Close();
558 mOutputStream
= nullptr;