Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / protocol / about / src / nsAboutCacheEntry.cpp
blob84a8c45280d950cb69cf2276edb6ec07fe0019b9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Darin Fisher <darin@netscape.com> (original author)
24 * Alexey Chernyak <alexeyc@bigfoot.com> (XHTML 1.1 conversion)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include <limits.h>
42 #include "nsAboutCacheEntry.h"
43 #include "nsICacheService.h"
44 #include "nsICacheEntryDescriptor.h"
45 #include "nsIStorageStream.h"
46 #include "nsNetUtil.h"
47 #include "nsAutoPtr.h"
48 #include "prprf.h"
49 #include "prtime.h"
50 #include "nsEscape.h"
52 #define HEXDUMP_MAX_ROWS 16
54 static void
55 HexDump(PRUint32 *state, const char *buf, PRInt32 n, nsCString &result)
57 char temp[16];
59 const unsigned char *p;
60 while (n) {
61 PR_snprintf(temp, sizeof(temp), "%08x: ", *state);
62 result.Append(temp);
63 *state += HEXDUMP_MAX_ROWS;
65 p = (const unsigned char *) buf;
67 PRInt32 i, row_max = PR_MIN(HEXDUMP_MAX_ROWS, n);
69 // print hex codes:
70 for (i = 0; i < row_max; ++i) {
71 PR_snprintf(temp, sizeof(temp), "%02x ", *p++);
72 result.Append(temp);
74 for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
75 result.AppendLiteral(" ");
78 // print ASCII glyphs if possible:
79 p = (const unsigned char *) buf;
80 for (i = 0; i < row_max; ++i, ++p) {
81 switch (*p) {
82 case '<':
83 result.AppendLiteral("&lt;");
84 break;
85 case '>':
86 result.AppendLiteral("&gt;");
87 break;
88 case '&':
89 result.AppendLiteral("&amp;");
90 break;
91 default:
92 if (*p < 0x7F && *p > 0x1F) {
93 result.Append(*p);
94 } else {
95 result.Append('.');
100 result.Append('\n');
102 buf += row_max;
103 n -= row_max;
107 //-----------------------------------------------------------------------------
108 // nsAboutCacheEntry::nsISupports
110 NS_IMPL_ISUPPORTS2(nsAboutCacheEntry,
111 nsIAboutModule,
112 nsICacheMetaDataVisitor)
114 //-----------------------------------------------------------------------------
115 // nsAboutCacheEntry::nsIAboutModule
117 NS_IMETHODIMP
118 nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result)
120 NS_ENSURE_ARG_POINTER(uri);
121 nsresult rv;
123 nsCOMPtr<nsIInputStream> stream;
124 rv = GetContentStream(uri, getter_AddRefs(stream));
125 if (NS_FAILED(rv)) return rv;
127 return NS_NewInputStreamChannel(result, uri, stream,
128 NS_LITERAL_CSTRING("application/xhtml+xml"),
129 NS_LITERAL_CSTRING("utf-8"));
132 NS_IMETHODIMP
133 nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, PRUint32 *result)
135 *result = 0;
136 return NS_OK;
139 //-----------------------------------------------------------------------------
140 // nsAboutCacheEntry
142 nsresult
143 nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result)
145 nsCOMPtr<nsIStorageStream> storageStream;
146 nsCOMPtr<nsIOutputStream> outputStream;
147 PRUint32 n;
148 nsCString buffer;
149 nsresult rv;
151 nsCOMPtr<nsICacheEntryDescriptor> descriptor;
152 OpenCacheEntry(uri, getter_AddRefs(descriptor));
154 // Init: (block size, maximum length)
155 rv = NS_NewStorageStream(256, PRUint32(-1), getter_AddRefs(storageStream));
156 if (NS_FAILED(rv)) return rv;
158 rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
159 if (NS_FAILED(rv)) return rv;
161 buffer.AssignLiteral(
162 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
163 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
164 " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
165 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
166 "<head>\n<title>Cache entry information</title>\n"
167 "<style type=\"text/css\">\npre {\n margin: 0;\n}\n"
168 "td:first-child {\n text-align: right;\n vertical-align: top;\n"
169 " line-height: 0.8em;\n}\n</style>\n</head>\n<body>\n");
170 outputStream->Write(buffer.get(), buffer.Length(), &n);
172 if (descriptor)
173 rv = WriteCacheEntryDescription(outputStream, descriptor);
174 else
175 rv = WriteCacheEntryUnavailable(outputStream);
176 if (NS_FAILED(rv)) return rv;
178 buffer.AssignLiteral("</body>\n</html>\n");
179 outputStream->Write(buffer.get(), buffer.Length(), &n);
181 nsCOMPtr<nsIInputStream> inStr;
182 PRUint32 size;
184 rv = storageStream->GetLength(&size);
185 if (NS_FAILED(rv)) return rv;
187 return storageStream->NewInputStream(0, result);
190 nsresult
191 nsAboutCacheEntry::OpenCacheEntry(nsIURI *uri, nsICacheEntryDescriptor **result)
193 nsresult rv;
194 nsCAutoString clientID, key;
195 PRBool streamBased = PR_TRUE;
197 rv = ParseURI(uri, clientID, streamBased, key);
198 if (NS_FAILED(rv)) return rv;
200 nsCOMPtr<nsICacheService> serv =
201 do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
202 if (NS_FAILED(rv)) return rv;
204 nsCOMPtr<nsICacheSession> session;
205 rv = serv->CreateSession(clientID.get(),
206 nsICache::STORE_ANYWHERE,
207 streamBased,
208 getter_AddRefs(session));
209 if (NS_FAILED(rv)) return rv;
211 rv = session->SetDoomEntriesIfExpired(PR_FALSE);
212 if (NS_FAILED(rv)) return rv;
214 rv = session->OpenCacheEntry(key, nsICache::ACCESS_READ, PR_FALSE, result);
215 return rv;
219 //-----------------------------------------------------------------------------
220 // helper methods
221 //-----------------------------------------------------------------------------
223 static PRTime SecondsToPRTime(PRUint32 t_sec)
225 PRTime t_usec, usec_per_sec;
226 LL_I2L(t_usec, t_sec);
227 LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
228 LL_MUL(t_usec, t_usec, usec_per_sec);
229 return t_usec;
231 static void PrintTimeString(char *buf, PRUint32 bufsize, PRUint32 t_sec)
233 PRExplodedTime et;
234 PRTime t_usec = SecondsToPRTime(t_sec);
235 PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et);
236 PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et);
239 #define APPEND_ROW(label, value) \
240 PR_BEGIN_MACRO \
241 buffer.AppendLiteral("<tr><td><tt><b>"); \
242 buffer.AppendLiteral(label); \
243 buffer.AppendLiteral(":</b></tt></td>\n<td><pre>"); \
244 buffer.Append(value); \
245 buffer.AppendLiteral("</pre></td></tr>\n"); \
246 PR_END_MACRO
248 nsresult
249 nsAboutCacheEntry::WriteCacheEntryDescription(nsIOutputStream *outputStream,
250 nsICacheEntryDescriptor *descriptor)
252 nsresult rv;
253 nsCString buffer;
254 PRUint32 n;
256 nsCAutoString str;
258 rv = descriptor->GetKey(str);
259 if (NS_FAILED(rv)) return rv;
261 buffer.SetCapacity(4096);
262 buffer.AssignLiteral("<table>"
263 "<tr><td><tt><b>key:</b></tt></td><td>");
265 // Test if the key is actually a URI
266 nsCOMPtr<nsIURI> uri;
267 PRBool isJS = PR_FALSE;
268 PRBool isData = PR_FALSE;
270 rv = NS_NewURI(getter_AddRefs(uri), str);
271 // javascript: and data: URLs should not be linkified
272 // since clicking them can cause scripts to run - bug 162584
273 if (NS_SUCCEEDED(rv)) {
274 uri->SchemeIs("javascript", &isJS);
275 uri->SchemeIs("data", &isData);
277 char* escapedStr = nsEscapeHTML(str.get());
278 if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
279 buffer.AppendLiteral("<a href=\"");
280 buffer.Append(escapedStr);
281 buffer.AppendLiteral("\">");
282 buffer.Append(escapedStr);
283 buffer.AppendLiteral("</a>");
284 uri = 0;
286 else
287 buffer.Append(escapedStr);
288 nsMemory::Free(escapedStr);
289 buffer.AppendLiteral("</td></tr>\n");
292 // temp vars for reporting
293 char timeBuf[255];
294 PRUint32 u = 0;
295 PRInt32 i = 0;
296 nsCAutoString s;
298 // Fetch Count
299 s.Truncate();
300 descriptor->GetFetchCount(&i);
301 s.AppendInt(i);
302 APPEND_ROW("fetch count", s);
304 // Last Fetched
305 descriptor->GetLastFetched(&u);
306 if (u) {
307 PrintTimeString(timeBuf, sizeof(timeBuf), u);
308 APPEND_ROW("last fetched", timeBuf);
309 } else {
310 APPEND_ROW("last fetched", "No last fetch time");
313 // Last Modified
314 descriptor->GetLastModified(&u);
315 if (u) {
316 PrintTimeString(timeBuf, sizeof(timeBuf), u);
317 APPEND_ROW("last modified", timeBuf);
318 } else {
319 APPEND_ROW("last modified", "No last modified time");
322 // Expiration Time
323 descriptor->GetExpirationTime(&u);
324 if (u < 0xFFFFFFFF) {
325 PrintTimeString(timeBuf, sizeof(timeBuf), u);
326 APPEND_ROW("expires", timeBuf);
327 } else {
328 APPEND_ROW("expires", "No expiration time");
331 // Data Size
332 s.Truncate();
333 PRUint32 dataSize;
334 descriptor->GetDataSize(&dataSize);
335 s.AppendInt((PRInt32)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
336 APPEND_ROW("Data size", s);
338 // Storage Policy
340 // XXX Stream Based?
342 // XXX Cache Device
343 // File on disk
344 nsCOMPtr<nsIFile> cacheFile;
345 rv = descriptor->GetFile(getter_AddRefs(cacheFile));
346 if (NS_SUCCEEDED(rv)) {
347 nsAutoString filePath;
348 cacheFile->GetPath(filePath);
349 APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath));
351 else
352 APPEND_ROW("file on disk", "none");
354 // Security Info
355 nsCOMPtr<nsISupports> securityInfo;
356 descriptor->GetSecurityInfo(getter_AddRefs(securityInfo));
357 if (securityInfo) {
358 APPEND_ROW("Security", "This is a secure document.");
359 } else {
360 APPEND_ROW("Security",
361 "This document does not have any security info associated with it.");
364 buffer.AppendLiteral("</table>\n"
365 "<hr />\n<table>");
366 // Meta Data
367 // let's just look for some well known (HTTP) meta data tags, for now.
369 // Client ID
370 nsXPIDLCString str2;
371 descriptor->GetClientID(getter_Copies(str2));
372 if (!str2.IsEmpty()) APPEND_ROW("Client", str2);
375 mBuffer = &buffer; // make it available for VisitMetaDataElement()
376 descriptor->VisitMetaData(this);
377 mBuffer = nsnull;
379 buffer.AppendLiteral("</table>\n"
380 "<hr />\n<pre>");
381 outputStream->Write(buffer.get(), buffer.Length(), &n);
383 buffer.Truncate();
385 // Provide a hexdump of the data
386 nsCOMPtr<nsIInputStream> stream;
387 descriptor->OpenInputStream(0, getter_AddRefs(stream));
388 if (stream) {
389 PRUint32 hexDumpState = 0;
390 char chunk[4096];
391 while (dataSize) {
392 PRUint32 count = PR_MIN(dataSize, sizeof(chunk));
393 if (NS_FAILED(stream->Read(chunk, count, &n)) || n == 0)
394 break;
395 dataSize -= n;
396 HexDump(&hexDumpState, chunk, n, buffer);
397 outputStream->Write(buffer.get(), buffer.Length(), &n);
398 buffer.Truncate();
402 buffer.AssignLiteral("</pre>");
403 outputStream->Write(buffer.get(), buffer.Length(), &n);
404 return NS_OK;
407 nsresult
408 nsAboutCacheEntry::WriteCacheEntryUnavailable(nsIOutputStream *outputStream)
410 PRUint32 n;
411 NS_NAMED_LITERAL_CSTRING(buffer,
412 "The cache entry you selected is not available.");
413 outputStream->Write(buffer.get(), buffer.Length(), &n);
414 return NS_OK;
417 nsresult
418 nsAboutCacheEntry::ParseURI(nsIURI *uri, nsCString &clientID,
419 PRBool &streamBased, nsCString &key)
422 // about:cache-entry?client=[string]&sb=[boolean]&key=[string]
424 nsresult rv;
426 nsCAutoString path;
427 rv = uri->GetPath(path);
428 if (NS_FAILED(rv)) return rv;
430 nsACString::const_iterator i1, i2, i3, end;
431 path.BeginReading(i1);
432 path.EndReading(end);
434 i2 = end;
435 if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2))
436 return NS_ERROR_FAILURE;
437 // i2 points to the start of clientID
439 i1 = i2;
440 i3 = end;
441 if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3))
442 return NS_ERROR_FAILURE;
443 // i1 points to the end of clientID
444 // i3 points to the start of isStreamBased
446 clientID.Assign(Substring(i2, i1));
448 i1 = i3;
449 i2 = end;
450 if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2))
451 return NS_ERROR_FAILURE;
452 // i1 points to the end of isStreamBased
453 // i2 points to the start of key
455 streamBased = FindCharInReadable('1', i3, i1);
456 key.Assign(Substring(i2, end));
458 return NS_OK;
462 //-----------------------------------------------------------------------------
463 // nsICacheMetaDataVisitor implementation
464 //-----------------------------------------------------------------------------
466 NS_IMETHODIMP
467 nsAboutCacheEntry::VisitMetaDataElement(const char * key,
468 const char * value,
469 PRBool * keepGoing)
471 mBuffer->AppendLiteral("<tr><td><tt><b>");
472 mBuffer->Append(key);
473 mBuffer->AppendLiteral(":</b></tt></td>\n<td><pre>");
474 char* escapedValue = nsEscapeHTML(value);
475 mBuffer->Append(escapedValue);
476 nsMemory::Free(escapedValue);
477 mBuffer->AppendLiteral("</pre></td></tr>\n");
479 *keepGoing = PR_TRUE;
480 return NS_OK;