Back to index

lightning-sunbird  0.9+nobinonly
nsAboutCacheEntry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com> (original author)
00024  *   Alexey Chernyak <alexeyc@bigfoot.com> (XHTML 1.1 conversion)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include <limits.h>
00041 
00042 #include "nsAboutCacheEntry.h"
00043 #include "nsICacheService.h"
00044 #include "nsICacheEntryDescriptor.h"
00045 #include "nsIStorageStream.h"
00046 #include "nsNetUtil.h"
00047 #include "prtime.h"
00048 #include "nsEscape.h"
00049 
00050 //-----------------------------------------------------------------------------
00051 // nsISupports implementation
00052 //-----------------------------------------------------------------------------
00053 
00054 NS_IMPL_ISUPPORTS4(nsAboutCacheEntry,
00055                    nsIAboutModule,
00056                    nsIChannel,
00057                    nsIRequest,
00058                    nsICacheListener)
00059 
00060 //-----------------------------------------------------------------------------
00061 // nsIAboutModule implementation
00062 //-----------------------------------------------------------------------------
00063 
00064 NS_IMETHODIMP
00065 nsAboutCacheEntry::NewChannel(nsIURI *aURI, nsIChannel **result)
00066 {
00067     NS_ENSURE_ARG_POINTER(aURI);
00068     nsresult rv;
00069 
00070     nsCOMPtr<nsIChannel> chan;
00071     rv = NS_NewInputStreamChannel(getter_AddRefs(chan), aURI, nsnull,
00072                                   NS_LITERAL_CSTRING("application/xhtml+xml"),
00073                                   NS_LITERAL_CSTRING("utf-8"));
00074     if (NS_FAILED(rv)) return rv;
00075 
00076     mStreamChannel = do_QueryInterface(chan, &rv);
00077     if (NS_FAILED(rv)) return rv;
00078 
00079     return CallQueryInterface((nsIAboutModule *) this, result);
00080 }
00081 
00082 //-----------------------------------------------------------------------------
00083 // nsICacheListener implementation
00084 //-----------------------------------------------------------------------------
00085 
00086 NS_IMETHODIMP
00087 nsAboutCacheEntry::OnCacheEntryAvailable(nsICacheEntryDescriptor *descriptor,
00088                                          nsCacheAccessMode accessGranted,
00089                                          nsresult status)
00090 {
00091     nsCOMPtr<nsIStorageStream> storageStream;
00092     nsCOMPtr<nsIOutputStream> outputStream;
00093     PRUint32 n;
00094     nsCString buffer;
00095     nsresult rv;
00096 
00097     // Init: (block size, maximum length)
00098     rv = NS_NewStorageStream(256, PRUint32(-1), getter_AddRefs(storageStream));
00099     if (NS_FAILED(rv)) return rv;
00100 
00101     rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
00102     if (NS_FAILED(rv)) return rv;
00103 
00104     buffer.AssignLiteral(
00105                   "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00106                   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
00107                   "    \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
00108                   "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
00109                   "<head>\n<title>Cache entry information</title>\n"
00110                   "<style type=\"text/css\">\npre {\n  margin: 0;\n}\n"
00111                   "td:first-child {\n  text-align: right;\n  vertical-align: top;\n"
00112                   "  line-height: 0.8em;\n}\n</style>\n</head>\n<body>\n");
00113     outputStream->Write(buffer.get(), buffer.Length(), &n);
00114 
00115     if (NS_SUCCEEDED(status))
00116         rv = WriteCacheEntryDescription(outputStream, descriptor);
00117     else
00118         rv = WriteCacheEntryUnavailable(outputStream, status);
00119     if (NS_FAILED(rv)) return rv;
00120 
00121     buffer.AssignLiteral("</body>\n</html>\n");
00122     outputStream->Write(buffer.get(), buffer.Length(), &n);
00123         
00124     nsCOMPtr<nsIInputStream> inStr;
00125     PRUint32 size;
00126 
00127     rv = storageStream->GetLength(&size);
00128     if (NS_FAILED(rv)) return rv;
00129 
00130     rv = storageStream->NewInputStream(0, getter_AddRefs(inStr));
00131     if (NS_FAILED(rv)) return rv;
00132 
00133     rv = mStreamChannel->SetContentStream(inStr);
00134     if (NS_FAILED(rv)) return rv;
00135 
00136     return mStreamChannel->AsyncOpen(mListener, mListenerContext);
00137 }
00138 
00139 //-----------------------------------------------------------------------------
00140 // nsIRequest implementation
00141 //-----------------------------------------------------------------------------
00142 
00143 NS_IMETHODIMP
00144 nsAboutCacheEntry::GetName(nsACString &result)
00145 {
00146     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00147     return mStreamChannel->GetName(result);
00148 }
00149 
00150 NS_IMETHODIMP
00151 nsAboutCacheEntry::IsPending(PRBool *result)
00152 {
00153     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00154     return mStreamChannel->IsPending(result);
00155 }
00156 
00157 NS_IMETHODIMP
00158 nsAboutCacheEntry::GetStatus(nsresult *result)
00159 {
00160     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00161     return mStreamChannel->GetStatus(result);
00162 }
00163 
00164 NS_IMETHODIMP
00165 nsAboutCacheEntry::Cancel(nsresult status)
00166 {
00167     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00168     return mStreamChannel->Cancel(status);
00169 }
00170 
00171 NS_IMETHODIMP
00172 nsAboutCacheEntry::Suspend()
00173 {
00174     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00175     return mStreamChannel->Suspend();
00176 }
00177 
00178 NS_IMETHODIMP
00179 nsAboutCacheEntry::Resume()
00180 {
00181     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00182     return mStreamChannel->Resume();
00183 }
00184 
00185 //-----------------------------------------------------------------------------
00186 // nsIChannel implementation
00187 //-----------------------------------------------------------------------------
00188 
00189 NS_IMETHODIMP
00190 nsAboutCacheEntry::GetOriginalURI(nsIURI **value)
00191 {
00192     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00193     return mStreamChannel->GetOriginalURI(value);
00194 }
00195 
00196 NS_IMETHODIMP
00197 nsAboutCacheEntry::SetOriginalURI(nsIURI *value)
00198 {
00199     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00200     return mStreamChannel->SetOriginalURI(value);
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsAboutCacheEntry::GetURI(nsIURI **value)
00205 {
00206     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00207     return mStreamChannel->GetURI(value);
00208 }
00209 
00210 NS_IMETHODIMP
00211 nsAboutCacheEntry::GetOwner(nsISupports **value)
00212 {
00213     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00214     return mStreamChannel->GetOwner(value);
00215 }
00216 
00217 NS_IMETHODIMP
00218 nsAboutCacheEntry::SetOwner(nsISupports *value)
00219 {
00220     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00221     return mStreamChannel->SetOwner(value);
00222 }
00223 
00224 NS_IMETHODIMP
00225 nsAboutCacheEntry::GetLoadGroup(nsILoadGroup **value)
00226 {
00227     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00228     return mStreamChannel->GetLoadGroup(value);
00229 }
00230 
00231 NS_IMETHODIMP
00232 nsAboutCacheEntry::SetLoadGroup(nsILoadGroup *value)
00233 {
00234     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00235     return mStreamChannel->SetLoadGroup(value);
00236 }
00237 
00238 NS_IMETHODIMP
00239 nsAboutCacheEntry::GetLoadFlags(PRUint32 *value)
00240 {
00241     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00242     return mStreamChannel->GetLoadFlags(value);
00243 }
00244 
00245 NS_IMETHODIMP
00246 nsAboutCacheEntry::SetLoadFlags(PRUint32 value)
00247 {
00248     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00249     return mStreamChannel->SetLoadFlags(value);
00250 }
00251 
00252 NS_IMETHODIMP
00253 nsAboutCacheEntry::GetNotificationCallbacks(nsIInterfaceRequestor **value)
00254 {
00255     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00256     return mStreamChannel->GetNotificationCallbacks(value);
00257 }
00258 
00259 NS_IMETHODIMP
00260 nsAboutCacheEntry::SetNotificationCallbacks(nsIInterfaceRequestor *value)
00261 {
00262     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00263     return mStreamChannel->SetNotificationCallbacks(value);
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsAboutCacheEntry::GetSecurityInfo(nsISupports **value)
00268 {
00269     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00270     return mStreamChannel->GetSecurityInfo(value);
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsAboutCacheEntry::GetContentType(nsACString &value)
00275 {
00276     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00277     return mStreamChannel->GetContentType(value);
00278 }
00279 
00280 NS_IMETHODIMP
00281 nsAboutCacheEntry::SetContentType(const nsACString &value)
00282 {
00283     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00284     return mStreamChannel->SetContentType(value);
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsAboutCacheEntry::GetContentCharset(nsACString &value)
00289 {
00290     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00291     return mStreamChannel->GetContentCharset(value);
00292 }
00293 
00294 NS_IMETHODIMP
00295 nsAboutCacheEntry::SetContentCharset(const nsACString &value)
00296 {
00297     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00298     return mStreamChannel->SetContentCharset(value);
00299 }
00300 
00301 NS_IMETHODIMP
00302 nsAboutCacheEntry::GetContentLength(PRInt32 *value)
00303 {
00304     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00305     return mStreamChannel->GetContentLength(value);
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsAboutCacheEntry::SetContentLength(PRInt32 value)
00310 {
00311     NS_ENSURE_TRUE(mStreamChannel, NS_ERROR_NOT_INITIALIZED);
00312     return mStreamChannel->SetContentLength(value);
00313 }
00314 
00315 NS_IMETHODIMP
00316 nsAboutCacheEntry::Open(nsIInputStream **result)
00317 {
00318     NS_NOTREACHED("nsAboutCacheEntry::Open");
00319     return NS_ERROR_NOT_IMPLEMENTED;
00320 }
00321 
00322 NS_IMETHODIMP
00323 nsAboutCacheEntry::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
00324 {
00325     nsresult rv;
00326     nsCAutoString clientID, key;
00327     PRBool streamBased = PR_TRUE;
00328 
00329     rv = ParseURI(clientID, streamBased, key);
00330     if (NS_FAILED(rv)) return rv;
00331 
00332     nsCOMPtr<nsICacheService> serv =
00333         do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
00334     if (NS_FAILED(rv)) return rv;
00335 
00336     rv = serv->CreateSession(clientID.get(),
00337                              nsICache::STORE_ANYWHERE,
00338                              streamBased,
00339                              getter_AddRefs(mCacheSession));
00340     if (NS_FAILED(rv)) return rv;
00341 
00342     rv = mCacheSession->SetDoomEntriesIfExpired(PR_FALSE);
00343     if (NS_FAILED(rv)) return rv;
00344 
00345     mListener = listener;
00346     mListenerContext = context;
00347 
00348     return mCacheSession->AsyncOpenCacheEntry(key, nsICache::ACCESS_READ, this);
00349 }
00350 
00351 
00352 //-----------------------------------------------------------------------------
00353 // helper methods
00354 //-----------------------------------------------------------------------------
00355 
00356 static PRTime SecondsToPRTime(PRUint32 t_sec)
00357 {
00358     PRTime t_usec, usec_per_sec;
00359     LL_I2L(t_usec, t_sec);
00360     LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
00361     LL_MUL(t_usec, t_usec, usec_per_sec);
00362     return t_usec;
00363 }
00364 static void PrintTimeString(char *buf, PRUint32 bufsize, PRUint32 t_sec)
00365 {
00366     PRExplodedTime et;
00367     PRTime t_usec = SecondsToPRTime(t_sec);
00368     PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et);
00369     PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et);
00370 }
00371 
00372 #define APPEND_ROW(label, value) \
00373     PR_BEGIN_MACRO \
00374     buffer.AppendLiteral("<tr><td><tt><b>"); \
00375     buffer.AppendLiteral(label); \
00376     buffer.AppendLiteral(":</b></tt></td>\n<td><pre>"); \
00377     buffer.Append(value); \
00378     buffer.AppendLiteral("</pre></td></tr>\n"); \
00379     PR_END_MACRO
00380 
00381 nsresult
00382 nsAboutCacheEntry::WriteCacheEntryDescription(nsIOutputStream *outputStream,
00383                                               nsICacheEntryDescriptor *descriptor)
00384 {
00385     nsresult rv;
00386     nsCString buffer;
00387     PRUint32 n;
00388 
00389     nsCAutoString str;
00390 
00391     rv = descriptor->GetKey(str);
00392     if (NS_FAILED(rv)) return rv;
00393 
00394     buffer.SetCapacity(4096);
00395     buffer.AssignLiteral("<table>"
00396                          "<tr><td><tt><b>key:</b></tt></td><td>");
00397 
00398     // Test if the key is actually a URI
00399     nsCOMPtr<nsIURI> uri;
00400     PRBool isJS = PR_FALSE;
00401     PRBool isData = PR_FALSE;
00402 
00403     rv = NS_NewURI(getter_AddRefs(uri), str);
00404     // javascript: and data: URLs should not be linkified
00405     // since clicking them can cause scripts to run - bug 162584
00406     if (NS_SUCCEEDED(rv)) {
00407         uri->SchemeIs("javascript", &isJS);
00408         uri->SchemeIs("data", &isData);
00409     }
00410     char* escapedStr = nsEscapeHTML(str.get());
00411     if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
00412         buffer.AppendLiteral("<a href=\"");
00413         buffer.Append(escapedStr);
00414         buffer.AppendLiteral("\">");
00415         buffer.Append(escapedStr);
00416         buffer.AppendLiteral("</a>");
00417         uri = 0;
00418     }
00419     else
00420         buffer.Append(escapedStr);
00421     nsMemory::Free(escapedStr);
00422     buffer.AppendLiteral("</td></tr>\n");
00423 
00424 
00425     // temp vars for reporting
00426     char timeBuf[255];
00427     PRUint32 u = 0;
00428     PRInt32  i = 0;
00429     nsCAutoString s;
00430 
00431     // Fetch Count
00432     s.Truncate();
00433     descriptor->GetFetchCount(&i);
00434     s.AppendInt(i);
00435     APPEND_ROW("fetch count", s);
00436 
00437     // Last Fetched
00438     descriptor->GetLastFetched(&u);
00439     if (u) {
00440         PrintTimeString(timeBuf, sizeof(timeBuf), u);
00441         APPEND_ROW("last fetched", timeBuf);
00442     } else {
00443         APPEND_ROW("last fetched", "No last fetch time");
00444     }
00445 
00446     // Last Modified
00447     descriptor->GetLastModified(&u);
00448     if (u) {
00449         PrintTimeString(timeBuf, sizeof(timeBuf), u);
00450         APPEND_ROW("last modified", timeBuf);
00451     } else {
00452         APPEND_ROW("last modified", "No last modified time");
00453     }
00454 
00455     // Expiration Time
00456     descriptor->GetExpirationTime(&u);
00457     if (u < 0xFFFFFFFF) {
00458         PrintTimeString(timeBuf, sizeof(timeBuf), u);
00459         APPEND_ROW("expires", timeBuf);
00460     } else {
00461         APPEND_ROW("expires", "No expiration time");
00462     }
00463 
00464     // Data Size
00465     s.Truncate();
00466     descriptor->GetDataSize(&u);
00467     s.AppendInt((PRInt32)u);     // XXX nsICacheEntryInfo interfaces should be fixed.
00468     APPEND_ROW("Data size", s);
00469 
00470     // Storage Policy
00471 
00472     // XXX Stream Based?
00473 
00474     // XXX Cache Device
00475     // File on disk
00476     nsCOMPtr<nsIFile> cacheFile;
00477     rv = descriptor->GetFile(getter_AddRefs(cacheFile));
00478     if (NS_SUCCEEDED(rv)) {
00479         nsAutoString filePath;
00480         cacheFile->GetPath(filePath);
00481         APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath));
00482     }
00483     else
00484         APPEND_ROW("file on disk", "none");
00485 
00486     // Security Info
00487     nsCOMPtr<nsISupports> securityInfo;
00488     descriptor->GetSecurityInfo(getter_AddRefs(securityInfo));
00489     if (securityInfo) {
00490         APPEND_ROW("Security", "This is a secure document.");
00491     } else {
00492         APPEND_ROW("Security",
00493                    "This document does not have any security info associated with it.");
00494     }
00495 
00496     buffer.AppendLiteral("</table>\n"
00497                          "<hr />\n<table>");
00498     // Meta Data
00499     // let's just look for some well known (HTTP) meta data tags, for now.
00500 
00501     // Client ID
00502     nsXPIDLCString str2;
00503     descriptor->GetClientID(getter_Copies(str2));
00504     if (!str2.IsEmpty())  APPEND_ROW("Client", str2);
00505 
00506 
00507     mBuffer = &buffer;  // make it available for VisitMetaDataElement()
00508     descriptor->VisitMetaData(this);
00509     mBuffer = nsnull;
00510     
00511 
00512     buffer.AppendLiteral("</table>\n");
00513 
00514     outputStream->Write(buffer.get(), buffer.Length(), &n);
00515     return NS_OK;
00516 }
00517 
00518 nsresult
00519 nsAboutCacheEntry::WriteCacheEntryUnavailable(nsIOutputStream *outputStream,
00520                                               nsresult reason)
00521 {
00522     PRUint32 n;
00523     NS_NAMED_LITERAL_CSTRING(buffer, "The cache entry you selected is no longer available.");
00524     outputStream->Write(buffer.get(), buffer.Length(), &n);
00525     return NS_OK;
00526 }
00527 
00528 nsresult
00529 nsAboutCacheEntry::ParseURI(nsCString &clientID, PRBool &streamBased, nsCString &key)
00530 {
00531     //
00532     // about:cache-entry?client=[string]&sb=[boolean]&key=[string]
00533     //
00534     nsresult rv;
00535 
00536     nsCOMPtr<nsIURI> uri;
00537     rv = mStreamChannel->GetURI(getter_AddRefs(uri));
00538     if (NS_FAILED(rv)) return rv;
00539 
00540     nsCAutoString path;
00541     rv = uri->GetPath(path);
00542     if (NS_FAILED(rv)) return rv;
00543 
00544     nsACString::const_iterator i1, i2, i3, end;
00545     path.BeginReading(i1);
00546     path.EndReading(end);
00547 
00548     i2 = end;
00549     if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2))
00550         return NS_ERROR_FAILURE;
00551     // i2 points to the start of clientID
00552 
00553     i1 = i2;
00554     i3 = end;
00555     if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3))
00556         return NS_ERROR_FAILURE;
00557     // i1 points to the end of clientID
00558     // i3 points to the start of isStreamBased
00559 
00560     clientID.Assign(Substring(i2, i1));
00561 
00562     i1 = i3;
00563     i2 = end;
00564     if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2))
00565         return NS_ERROR_FAILURE;
00566     // i1 points to the end of isStreamBased
00567     // i2 points to the start of key
00568 
00569     streamBased = FindCharInReadable('1', i3, i1);
00570     key.Assign(Substring(i2, end));
00571 
00572     return NS_OK;
00573 }
00574 
00575 
00576 //-----------------------------------------------------------------------------
00577 // nsICacheMetaDataVisitor implementation
00578 //-----------------------------------------------------------------------------
00579 
00580 NS_IMETHODIMP
00581 nsAboutCacheEntry::VisitMetaDataElement(const char * key,
00582                                         const char * value,
00583                                         PRBool *     keepGoing)
00584 {
00585     mBuffer->AppendLiteral("<tr><td><tt><b>");
00586     mBuffer->Append(key);
00587     mBuffer->AppendLiteral(":</b></tt></td>\n<td><pre>");
00588     char* escapedValue = nsEscapeHTML(value);
00589     mBuffer->Append(escapedValue);
00590     nsMemory::Free(escapedValue);
00591     mBuffer->AppendLiteral("</pre></td></tr>\n");
00592 
00593     *keepGoing = PR_TRUE;
00594     return NS_OK;
00595 }
00596