/src/mozilla-central/netwerk/protocol/about/nsAboutCacheEntry.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
6 | | #include "nsAboutCacheEntry.h" |
7 | | |
8 | | #include "mozilla/Sprintf.h" |
9 | | |
10 | | #include "nsAboutCache.h" |
11 | | #include "nsICacheStorage.h" |
12 | | #include "CacheObserver.h" |
13 | | #include "nsNetUtil.h" |
14 | | #include "nsEscape.h" |
15 | | #include "nsIAsyncInputStream.h" |
16 | | #include "nsIAsyncOutputStream.h" |
17 | | #include "nsAboutProtocolUtils.h" |
18 | | #include "nsContentUtils.h" |
19 | | #include "nsInputStreamPump.h" |
20 | | #include "CacheFileUtils.h" |
21 | | #include <algorithm> |
22 | | #include "nsIPipe.h" |
23 | | |
24 | | using namespace mozilla::net; |
25 | | |
26 | 0 | #define HEXDUMP_MAX_ROWS 16 |
27 | | |
28 | | static void |
29 | | HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result) |
30 | 0 | { |
31 | 0 | char temp[16]; |
32 | 0 |
|
33 | 0 | const unsigned char *p; |
34 | 0 | while (n) { |
35 | 0 | SprintfLiteral(temp, "%08x: ", *state); |
36 | 0 | result.Append(temp); |
37 | 0 | *state += HEXDUMP_MAX_ROWS; |
38 | 0 |
|
39 | 0 | p = (const unsigned char *) buf; |
40 | 0 |
|
41 | 0 | int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n); |
42 | 0 |
|
43 | 0 | // print hex codes: |
44 | 0 | for (i = 0; i < row_max; ++i) { |
45 | 0 | SprintfLiteral(temp, "%02x ", *p++); |
46 | 0 | result.Append(temp); |
47 | 0 | } |
48 | 0 | for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) { |
49 | 0 | result.AppendLiteral(" "); |
50 | 0 | } |
51 | 0 |
|
52 | 0 | // print ASCII glyphs if possible: |
53 | 0 | p = (const unsigned char *) buf; |
54 | 0 | for (i = 0; i < row_max; ++i, ++p) { |
55 | 0 | switch (*p) { |
56 | 0 | case '<': |
57 | 0 | result.AppendLiteral("<"); |
58 | 0 | break; |
59 | 0 | case '>': |
60 | 0 | result.AppendLiteral(">"); |
61 | 0 | break; |
62 | 0 | case '&': |
63 | 0 | result.AppendLiteral("&"); |
64 | 0 | break; |
65 | 0 | default: |
66 | 0 | if (*p < 0x7F && *p > 0x1F) { |
67 | 0 | result.Append(*p); |
68 | 0 | } else { |
69 | 0 | result.Append('.'); |
70 | 0 | } |
71 | 0 | } |
72 | 0 | } |
73 | 0 |
|
74 | 0 | result.Append('\n'); |
75 | 0 |
|
76 | 0 | buf += row_max; |
77 | 0 | n -= row_max; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | //----------------------------------------------------------------------------- |
82 | | // nsAboutCacheEntry::nsISupports |
83 | | |
84 | | NS_IMPL_ISUPPORTS(nsAboutCacheEntry, |
85 | | nsIAboutModule) |
86 | | NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel, |
87 | | nsICacheEntryOpenCallback, |
88 | | nsICacheEntryMetaDataVisitor, |
89 | | nsIStreamListener, |
90 | | nsIRequest, |
91 | | nsIChannel) |
92 | | |
93 | | //----------------------------------------------------------------------------- |
94 | | // nsAboutCacheEntry::nsIAboutModule |
95 | | |
96 | | NS_IMETHODIMP |
97 | | nsAboutCacheEntry::NewChannel(nsIURI* uri, |
98 | | nsILoadInfo* aLoadInfo, |
99 | | nsIChannel** result) |
100 | 0 | { |
101 | 0 | NS_ENSURE_ARG_POINTER(uri); |
102 | 0 | nsresult rv; |
103 | 0 |
|
104 | 0 | RefPtr<Channel> channel = new Channel(); |
105 | 0 | rv = channel->Init(uri, aLoadInfo); |
106 | 0 | if (NS_FAILED(rv)) return rv; |
107 | 0 | |
108 | 0 | channel.forget(result); |
109 | 0 |
|
110 | 0 | return NS_OK; |
111 | 0 | } |
112 | | |
113 | | NS_IMETHODIMP |
114 | | nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result) |
115 | 0 | { |
116 | 0 | *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT | |
117 | 0 | nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT; |
118 | 0 | return NS_OK; |
119 | 0 | } |
120 | | |
121 | | //----------------------------------------------------------------------------- |
122 | | // nsAboutCacheEntry::Channel |
123 | | |
124 | | nsresult |
125 | | nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo) |
126 | 0 | { |
127 | 0 | nsresult rv; |
128 | 0 |
|
129 | 0 | nsCOMPtr<nsIInputStream> stream; |
130 | 0 | rv = GetContentStream(uri, getter_AddRefs(stream)); |
131 | 0 | if (NS_FAILED(rv)) return rv; |
132 | 0 | |
133 | 0 | rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel), |
134 | 0 | uri, |
135 | 0 | stream.forget(), |
136 | 0 | NS_LITERAL_CSTRING("text/html"), |
137 | 0 | NS_LITERAL_CSTRING("utf-8"), |
138 | 0 | aLoadInfo); |
139 | 0 | if (NS_FAILED(rv)) return rv; |
140 | 0 | |
141 | 0 | return NS_OK; |
142 | 0 | } |
143 | | |
144 | | nsresult |
145 | | nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result) |
146 | 0 | { |
147 | 0 | nsresult rv; |
148 | 0 |
|
149 | 0 | // Init: (block size, maximum length) |
150 | 0 | nsCOMPtr<nsIAsyncInputStream> inputStream; |
151 | 0 | rv = NS_NewPipe2(getter_AddRefs(inputStream), |
152 | 0 | getter_AddRefs(mOutputStream), |
153 | 0 | true, false, |
154 | 0 | 256, UINT32_MAX); |
155 | 0 | if (NS_FAILED(rv)) return rv; |
156 | 0 | |
157 | 0 | NS_NAMED_LITERAL_CSTRING( |
158 | 0 | buffer, |
159 | 0 | "<!DOCTYPE html>\n" |
160 | 0 | "<html>\n" |
161 | 0 | "<head>\n" |
162 | 0 | " <title>Cache entry information</title>\n" |
163 | 0 | " <link rel=\"stylesheet\" " |
164 | 0 | "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n" |
165 | 0 | " <link rel=\"stylesheet\" " |
166 | 0 | "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n" |
167 | 0 | "</head>\n" |
168 | 0 | "<body>\n" |
169 | 0 | "<h1>Cache entry information</h1>\n"); |
170 | 0 | uint32_t n; |
171 | 0 | rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
172 | 0 | if (NS_FAILED(rv)) return rv; |
173 | 0 | if (n != buffer.Length()) return NS_ERROR_UNEXPECTED; |
174 | 0 | |
175 | 0 | rv = OpenCacheEntry(uri); |
176 | 0 | if (NS_FAILED(rv)) return rv; |
177 | 0 | |
178 | 0 | inputStream.forget(result); |
179 | 0 | return NS_OK; |
180 | 0 | } |
181 | | |
182 | | nsresult |
183 | | nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri) |
184 | 0 | { |
185 | 0 | nsresult rv; |
186 | 0 |
|
187 | 0 | rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo), |
188 | 0 | mEnhanceId, getter_AddRefs(mCacheURI)); |
189 | 0 | if (NS_FAILED(rv)) return rv; |
190 | 0 | |
191 | 0 | return OpenCacheEntry(); |
192 | 0 | } |
193 | | |
194 | | nsresult |
195 | | nsAboutCacheEntry::Channel::OpenCacheEntry() |
196 | 0 | { |
197 | 0 | nsresult rv; |
198 | 0 |
|
199 | 0 | nsCOMPtr<nsICacheStorage> storage; |
200 | 0 | rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage)); |
201 | 0 | if (NS_FAILED(rv)) return rv; |
202 | 0 | |
203 | 0 | // Invokes OnCacheEntryAvailable() |
204 | 0 | rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId, |
205 | 0 | nsICacheStorage::OPEN_READONLY | |
206 | 0 | nsICacheStorage::OPEN_SECRETLY, |
207 | 0 | this); |
208 | 0 | if (NS_FAILED(rv)) return rv; |
209 | 0 | |
210 | 0 | return NS_OK; |
211 | 0 | } |
212 | | |
213 | | nsresult |
214 | | nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri, |
215 | | nsACString &storageName, |
216 | | nsILoadContextInfo **loadInfo, |
217 | | nsCString &enahnceID, |
218 | | nsIURI **cacheUri) |
219 | 0 | { |
220 | 0 | // |
221 | 0 | // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string] |
222 | 0 | // |
223 | 0 | nsresult rv; |
224 | 0 |
|
225 | 0 | nsAutoCString path; |
226 | 0 | rv = uri->GetPathQueryRef(path); |
227 | 0 | if (NS_FAILED(rv)) |
228 | 0 | return rv; |
229 | 0 | |
230 | 0 | nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end; |
231 | 0 | path.BeginReading(begin); |
232 | 0 | path.EndReading(end); |
233 | 0 |
|
234 | 0 | keyBegin = begin; keyEnd = end; |
235 | 0 | if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd)) |
236 | 0 | return NS_ERROR_FAILURE; |
237 | 0 | |
238 | 0 | valBegin = keyEnd; // the value of the storage key starts after the key |
239 | 0 |
|
240 | 0 | keyBegin = keyEnd; keyEnd = end; |
241 | 0 | if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd)) |
242 | 0 | return NS_ERROR_FAILURE; |
243 | 0 | |
244 | 0 | storageName.Assign(Substring(valBegin, keyBegin)); |
245 | 0 | valBegin = keyEnd; // the value of the context key starts after the key |
246 | 0 |
|
247 | 0 | keyBegin = keyEnd; keyEnd = end; |
248 | 0 | if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd)) |
249 | 0 | return NS_ERROR_FAILURE; |
250 | 0 | |
251 | 0 | nsAutoCString contextKey(Substring(valBegin, keyBegin)); |
252 | 0 | valBegin = keyEnd; // the value of the eid key starts after the key |
253 | 0 |
|
254 | 0 | keyBegin = keyEnd; keyEnd = end; |
255 | 0 | if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd)) |
256 | 0 | return NS_ERROR_FAILURE; |
257 | 0 | |
258 | 0 | enahnceID.Assign(Substring(valBegin, keyBegin)); |
259 | 0 |
|
260 | 0 | valBegin = keyEnd; // the value of the uri key starts after the key |
261 | 0 | nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one |
262 | 0 |
|
263 | 0 | // Uf... parsing done, now get some objects from it... |
264 | 0 |
|
265 | 0 | nsCOMPtr<nsILoadContextInfo> info = |
266 | 0 | CacheFileUtils::ParseKey(contextKey); |
267 | 0 | if (!info) |
268 | 0 | return NS_ERROR_FAILURE; |
269 | 0 | info.forget(loadInfo); |
270 | 0 |
|
271 | 0 | rv = NS_NewURI(cacheUri, uriSpec); |
272 | 0 | if (NS_FAILED(rv)) |
273 | 0 | return rv; |
274 | 0 | |
275 | 0 | return NS_OK; |
276 | 0 | } |
277 | | |
278 | | //----------------------------------------------------------------------------- |
279 | | // nsICacheEntryOpenCallback implementation |
280 | | //----------------------------------------------------------------------------- |
281 | | |
282 | | NS_IMETHODIMP |
283 | | nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry, |
284 | | nsIApplicationCache *aApplicationCache, |
285 | | uint32_t *result) |
286 | 0 | { |
287 | 0 | *result = nsICacheEntryOpenCallback::ENTRY_WANTED; |
288 | 0 | return NS_OK; |
289 | 0 | } |
290 | | |
291 | | NS_IMETHODIMP |
292 | | nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry, |
293 | | bool isNew, |
294 | | nsIApplicationCache *aApplicationCache, |
295 | | nsresult status) |
296 | 0 | { |
297 | 0 | nsresult rv; |
298 | 0 |
|
299 | 0 | mWaitingForData = false; |
300 | 0 | if (entry) { |
301 | 0 | rv = WriteCacheEntryDescription(entry); |
302 | 0 | } else { |
303 | 0 | rv = WriteCacheEntryUnavailable(); |
304 | 0 | } |
305 | 0 | if (NS_FAILED(rv)) return rv; |
306 | 0 | |
307 | 0 | |
308 | 0 | if (!mWaitingForData) { |
309 | 0 | // Data is not expected, close the output of content now. |
310 | 0 | CloseContent(); |
311 | 0 | } |
312 | 0 |
|
313 | 0 | return NS_OK; |
314 | 0 | } |
315 | | |
316 | | //----------------------------------------------------------------------------- |
317 | | // Print-out helper methods |
318 | | //----------------------------------------------------------------------------- |
319 | | |
320 | | #define APPEND_ROW(label, value) \ |
321 | 0 | PR_BEGIN_MACRO \ |
322 | 0 | buffer.AppendLiteral(" <tr>\n" \ |
323 | 0 | " <th>"); \ |
324 | 0 | buffer.AppendLiteral(label); \ |
325 | 0 | buffer.AppendLiteral(":</th>\n" \ |
326 | 0 | " <td>"); \ |
327 | 0 | buffer.Append(value); \ |
328 | 0 | buffer.AppendLiteral("</td>\n" \ |
329 | 0 | " </tr>\n"); \ |
330 | 0 | PR_END_MACRO |
331 | | |
332 | | nsresult |
333 | | nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry) |
334 | 0 | { |
335 | 0 | nsresult rv; |
336 | 0 | nsCString buffer; |
337 | 0 | uint32_t n; |
338 | 0 |
|
339 | 0 | nsAutoCString str; |
340 | 0 |
|
341 | 0 | rv = entry->GetKey(str); |
342 | 0 | if (NS_FAILED(rv)) return rv; |
343 | 0 | |
344 | 0 | buffer.SetCapacity(4096); |
345 | 0 | buffer.AssignLiteral("<table>\n" |
346 | 0 | " <tr>\n" |
347 | 0 | " <th>key:</th>\n" |
348 | 0 | " <td id=\"td-key\">"); |
349 | 0 |
|
350 | 0 | // Test if the key is actually a URI |
351 | 0 | nsCOMPtr<nsIURI> uri; |
352 | 0 | bool isJS = false; |
353 | 0 | bool isData = false; |
354 | 0 |
|
355 | 0 | rv = NS_NewURI(getter_AddRefs(uri), str); |
356 | 0 | // javascript: and data: URLs should not be linkified |
357 | 0 | // since clicking them can cause scripts to run - bug 162584 |
358 | 0 | if (NS_SUCCEEDED(rv)) { |
359 | 0 | uri->SchemeIs("javascript", &isJS); |
360 | 0 | uri->SchemeIs("data", &isData); |
361 | 0 | } |
362 | 0 | nsAutoCString escapedStr; |
363 | 0 | nsAppendEscapedHTML(str, escapedStr); |
364 | 0 | if (NS_SUCCEEDED(rv) && !(isJS || isData)) { |
365 | 0 | buffer.AppendLiteral("<a href=\""); |
366 | 0 | buffer.Append(escapedStr); |
367 | 0 | buffer.AppendLiteral("\">"); |
368 | 0 | buffer.Append(escapedStr); |
369 | 0 | buffer.AppendLiteral("</a>"); |
370 | 0 | uri = nullptr; |
371 | 0 | } else { |
372 | 0 | buffer.Append(escapedStr); |
373 | 0 | } |
374 | 0 | buffer.AppendLiteral("</td>\n" |
375 | 0 | " </tr>\n"); |
376 | 0 |
|
377 | 0 | // temp vars for reporting |
378 | 0 | char timeBuf[255]; |
379 | 0 | uint32_t u = 0; |
380 | 0 | int32_t i = 0; |
381 | 0 | nsAutoCString s; |
382 | 0 |
|
383 | 0 | // Fetch Count |
384 | 0 | s.Truncate(); |
385 | 0 | entry->GetFetchCount(&i); |
386 | 0 | s.AppendInt(i); |
387 | 0 | APPEND_ROW("fetch count", s); |
388 | 0 |
|
389 | 0 | // Last Fetched |
390 | 0 | entry->GetLastFetched(&u); |
391 | 0 | if (u) { |
392 | 0 | PrintTimeString(timeBuf, sizeof(timeBuf), u); |
393 | 0 | APPEND_ROW("last fetched", timeBuf); |
394 | 0 | } else { |
395 | 0 | APPEND_ROW("last fetched", "No last fetch time (bug 1000338)"); |
396 | 0 | } |
397 | 0 |
|
398 | 0 | // Last Modified |
399 | 0 | entry->GetLastModified(&u); |
400 | 0 | if (u) { |
401 | 0 | PrintTimeString(timeBuf, sizeof(timeBuf), u); |
402 | 0 | APPEND_ROW("last modified", timeBuf); |
403 | 0 | } else { |
404 | 0 | APPEND_ROW("last modified", "No last modified time (bug 1000338)"); |
405 | 0 | } |
406 | 0 |
|
407 | 0 | // Expiration Time |
408 | 0 | entry->GetExpirationTime(&u); |
409 | 0 |
|
410 | 0 | // Bug - 633747. |
411 | 0 | // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing. |
412 | 0 | // So we check if time is 0, then we show a message, "Expired Immediately" |
413 | 0 | if (u == 0) { |
414 | 0 | APPEND_ROW("expires", "Expired Immediately"); |
415 | 0 | } else if (u < 0xFFFFFFFF) { |
416 | 0 | PrintTimeString(timeBuf, sizeof(timeBuf), u); |
417 | 0 | APPEND_ROW("expires", timeBuf); |
418 | 0 | } else { |
419 | 0 | APPEND_ROW("expires", "No expiration time"); |
420 | 0 | } |
421 | 0 |
|
422 | 0 | // Data Size |
423 | 0 | s.Truncate(); |
424 | 0 | uint32_t dataSize; |
425 | 0 | if (NS_FAILED(entry->GetStorageDataSize(&dataSize))) |
426 | 0 | dataSize = 0; |
427 | 0 | s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed. |
428 | 0 | s.AppendLiteral(" B"); |
429 | 0 | APPEND_ROW("Data size", s); |
430 | 0 |
|
431 | 0 | // TODO - mayhemer |
432 | 0 | // Here used to be a link to the disk file (in the old cache for entries that |
433 | 0 | // did not fit any of the block files, in the new cache every time). |
434 | 0 | // I'd rather have a small set of buttons here to action on the entry: |
435 | 0 | // 1. save the content |
436 | 0 | // 2. save as a complete HTTP response (response head, headers, content) |
437 | 0 | // 3. doom the entry |
438 | 0 | // A new bug(s) should be filed here. |
439 | 0 |
|
440 | 0 | // Security Info |
441 | 0 | nsCOMPtr<nsISupports> securityInfo; |
442 | 0 | entry->GetSecurityInfo(getter_AddRefs(securityInfo)); |
443 | 0 | if (securityInfo) { |
444 | 0 | APPEND_ROW("Security", "This is a secure document."); |
445 | 0 | } else { |
446 | 0 | APPEND_ROW("Security", |
447 | 0 | "This document does not have any security info associated with it."); |
448 | 0 | } |
449 | 0 |
|
450 | 0 | buffer.AppendLiteral("</table>\n" |
451 | 0 | "<hr/>\n" |
452 | 0 | "<table>\n"); |
453 | 0 |
|
454 | 0 | mBuffer = &buffer; // make it available for OnMetaDataElement(). |
455 | 0 | entry->VisitMetaData(this); |
456 | 0 | mBuffer = nullptr; |
457 | 0 |
|
458 | 0 | buffer.AppendLiteral("</table>\n"); |
459 | 0 | mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
460 | 0 | buffer.Truncate(); |
461 | 0 |
|
462 | 0 | // Provide a hexdump of the data |
463 | 0 | if (!dataSize) { |
464 | 0 | return NS_OK; |
465 | 0 | } |
466 | 0 | |
467 | 0 | nsCOMPtr<nsIInputStream> stream; |
468 | 0 | entry->OpenInputStream(0, getter_AddRefs(stream)); |
469 | 0 | if (!stream) { |
470 | 0 | return NS_OK; |
471 | 0 | } |
472 | 0 | |
473 | 0 | RefPtr<nsInputStreamPump> pump; |
474 | 0 | rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream); |
475 | 0 | if (NS_FAILED(rv)) { |
476 | 0 | return NS_OK; // just ignore |
477 | 0 | } |
478 | 0 | |
479 | 0 | rv = pump->AsyncRead(this, nullptr); |
480 | 0 | if (NS_FAILED(rv)) { |
481 | 0 | return NS_OK; // just ignore |
482 | 0 | } |
483 | 0 | |
484 | 0 | mWaitingForData = true; |
485 | 0 | return NS_OK; |
486 | 0 | } |
487 | | |
488 | | nsresult |
489 | | nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable() |
490 | 0 | { |
491 | 0 | uint32_t n; |
492 | 0 | NS_NAMED_LITERAL_CSTRING(buffer, |
493 | 0 | "The cache entry you selected is not available."); |
494 | 0 | mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
495 | 0 | return NS_OK; |
496 | 0 | } |
497 | | |
498 | | //----------------------------------------------------------------------------- |
499 | | // nsICacheEntryMetaDataVisitor implementation |
500 | | //----------------------------------------------------------------------------- |
501 | | |
502 | | NS_IMETHODIMP |
503 | | nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value) |
504 | 0 | { |
505 | 0 | mBuffer->AppendLiteral(" <tr>\n" |
506 | 0 | " <th>"); |
507 | 0 | mBuffer->Append(key); |
508 | 0 | mBuffer->AppendLiteral(":</th>\n" |
509 | 0 | " <td>"); |
510 | 0 | nsAppendEscapedHTML(nsDependentCString(value), *mBuffer); |
511 | 0 | mBuffer->AppendLiteral("</td>\n" |
512 | 0 | " </tr>\n"); |
513 | 0 |
|
514 | 0 | return NS_OK; |
515 | 0 | } |
516 | | |
517 | | //----------------------------------------------------------------------------- |
518 | | // nsIStreamListener implementation |
519 | | //----------------------------------------------------------------------------- |
520 | | |
521 | | NS_IMETHODIMP |
522 | | nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx) |
523 | 0 | { |
524 | 0 | mHexDumpState = 0; |
525 | 0 |
|
526 | 0 | NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>"); |
527 | 0 | uint32_t n; |
528 | 0 | return mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
529 | 0 | } |
530 | | |
531 | | NS_IMETHODIMP |
532 | | nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx, |
533 | | nsIInputStream *aInputStream, |
534 | | uint64_t aOffset, |
535 | | uint32_t aCount) |
536 | 0 | { |
537 | 0 | uint32_t n; |
538 | 0 | return aInputStream->ReadSegments( |
539 | 0 | &nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n); |
540 | 0 | } |
541 | | |
542 | | /* static */ nsresult |
543 | | nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream, |
544 | | void *aClosure, |
545 | | const char *aFromSegment, |
546 | | uint32_t aToOffset, |
547 | | uint32_t aCount, |
548 | | uint32_t *aWriteCount) |
549 | 0 | { |
550 | 0 | nsAboutCacheEntry::Channel *a = |
551 | 0 | static_cast<nsAboutCacheEntry::Channel*>(aClosure); |
552 | 0 |
|
553 | 0 | nsCString buffer; |
554 | 0 | HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer); |
555 | 0 |
|
556 | 0 | uint32_t n; |
557 | 0 | a->mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
558 | 0 |
|
559 | 0 | *aWriteCount = aCount; |
560 | 0 |
|
561 | 0 | return NS_OK; |
562 | 0 | } |
563 | | |
564 | | NS_IMETHODIMP |
565 | | nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx, |
566 | | nsresult result) |
567 | 0 | { |
568 | 0 | NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n"); |
569 | 0 | uint32_t n; |
570 | 0 | mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
571 | 0 |
|
572 | 0 | CloseContent(); |
573 | 0 |
|
574 | 0 | return NS_OK; |
575 | 0 | } |
576 | | |
577 | | void |
578 | | nsAboutCacheEntry::Channel::CloseContent() |
579 | 0 | { |
580 | 0 | NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n"); |
581 | 0 | uint32_t n; |
582 | 0 | mOutputStream->Write(buffer.get(), buffer.Length(), &n); |
583 | 0 |
|
584 | 0 | mOutputStream->Close(); |
585 | 0 | mOutputStream = nullptr; |
586 | 0 | } |