michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsAboutCache.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsIStorageStream.h" michael@0: #include "nsIURI.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsEscape.h" michael@0: #include "nsAboutProtocolUtils.h" michael@0: michael@0: #include "nsICacheService.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule, nsICacheVisitor) michael@0: michael@0: NS_IMETHODIMP michael@0: nsAboutCache::NewChannel(nsIURI *aURI, nsIChannel **result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: nsresult rv; michael@0: uint32_t bytesWritten; michael@0: michael@0: *result = nullptr; michael@0: // Get the cache manager service michael@0: nsCOMPtr cacheService = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr storageStream; michael@0: nsCOMPtr outputStream; michael@0: michael@0: // Init: (block size, maximum length) michael@0: rv = NS_NewStorageStream(256, (uint32_t)-1, getter_AddRefs(storageStream)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mBuffer.AssignLiteral( michael@0: "\n" michael@0: "\n" michael@0: "\n" michael@0: " Information about the Cache Service\n" michael@0: " \n" michael@0: " \n" michael@0: "\n" michael@0: "\n" michael@0: "

Information about the Cache Service

\n"); michael@0: michael@0: outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); michael@0: michael@0: rv = ParseURI(aURI, mDeviceID); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mStream = outputStream; michael@0: michael@0: // nsCacheService::VisitEntries calls nsMemoryCacheDevice::Visit, michael@0: // nsDiskCacheDevice::Visit and nsOfflineCacheDevice::Visit, michael@0: // each of which call michael@0: // 1. VisitDevice (for about:cache), michael@0: // 2. VisitEntry in a loop (for about:cache?device=disk etc.) michael@0: rv = cacheService->VisitEntries(this); michael@0: mBuffer.Truncate(); michael@0: if (rv == NS_ERROR_NOT_AVAILABLE) { michael@0: mBuffer.AppendLiteral("

The cache is disabled.

\n"); michael@0: } michael@0: else if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (!mDeviceID.IsEmpty()) { michael@0: mBuffer.AppendLiteral("\n"); michael@0: } michael@0: mBuffer.AppendLiteral("\n" michael@0: "\n"); michael@0: outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); michael@0: michael@0: nsCOMPtr inStr; michael@0: michael@0: rv = storageStream->NewInputStream(0, getter_AddRefs(inStr)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr channel; michael@0: rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inStr, michael@0: NS_LITERAL_CSTRING("text/html"), michael@0: NS_LITERAL_CSTRING("utf-8")); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: channel.forget(result); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result) michael@0: { michael@0: *result = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAboutCache::VisitDevice(const char *deviceID, michael@0: nsICacheDeviceInfo *deviceInfo, michael@0: bool *visitEntries) michael@0: { michael@0: uint32_t bytesWritten, value, entryCount; michael@0: nsXPIDLCString str; michael@0: michael@0: *visitEntries = false; michael@0: michael@0: if (mDeviceID.IsEmpty() || mDeviceID.Equals(deviceID)) { michael@0: michael@0: // We need mStream for this michael@0: if (!mStream) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Write out the Cache Name michael@0: deviceInfo->GetDescription(getter_Copies(str)); michael@0: michael@0: mBuffer.AssignLiteral("

"); michael@0: mBuffer.Append(str); michael@0: mBuffer.AppendLiteral("

\n" michael@0: "\n"); michael@0: michael@0: // Write out cache info michael@0: // Number of entries michael@0: mBuffer.AppendLiteral(" \n" michael@0: " \n" michael@0: " \n" michael@0: " \n"); michael@0: michael@0: // Maximum storage size michael@0: mBuffer.AppendLiteral(" \n" michael@0: " \n" michael@0: " \n" michael@0: " \n"); michael@0: michael@0: // Storage in use michael@0: mBuffer.AppendLiteral(" \n" michael@0: " \n" michael@0: " \n" michael@0: " \n"); michael@0: michael@0: deviceInfo->GetUsageReport(getter_Copies(str)); michael@0: mBuffer.Append(str); michael@0: michael@0: if (mDeviceID.IsEmpty()) { // The about:cache case michael@0: if (entryCount != 0) { // Add the "List Cache Entries" link michael@0: mBuffer.AppendLiteral(" \n" michael@0: " \n" michael@0: " \n"); michael@0: } michael@0: mBuffer.AppendLiteral("
Number of entries:"); michael@0: entryCount = 0; michael@0: deviceInfo->GetEntryCount(&entryCount); michael@0: mBuffer.AppendInt(entryCount); michael@0: mBuffer.AppendLiteral("
Maximum storage size:"); michael@0: value = 0; michael@0: deviceInfo->GetMaximumSize(&value); michael@0: mBuffer.AppendInt(value/1024); michael@0: mBuffer.AppendLiteral(" KiB
Storage in use:"); michael@0: value = 0; michael@0: deviceInfo->GetTotalSize(&value); michael@0: mBuffer.AppendInt(value/1024); michael@0: mBuffer.AppendLiteral(" KiB
List Cache Entries
\n"); michael@0: } else { // The about:cache?device=disk etc. case michael@0: mBuffer.AppendLiteral("\n"); michael@0: if (entryCount != 0) { michael@0: *visitEntries = true; michael@0: mBuffer.AppendLiteral("
\n" michael@0: "\n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n" michael@0: " \n"); michael@0: } michael@0: } michael@0: michael@0: mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAboutCache::VisitEntry(const char *deviceID, michael@0: nsICacheEntryInfo *entryInfo, michael@0: bool *visitNext) michael@0: { michael@0: // We need mStream for this michael@0: if (!mStream) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv; michael@0: uint32_t bytesWritten; michael@0: nsAutoCString key; michael@0: nsXPIDLCString clientID; michael@0: bool streamBased; michael@0: michael@0: rv = entryInfo->GetKey(key); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = entryInfo->GetClientID(getter_Copies(clientID)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = entryInfo->IsStreamBased(&streamBased); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Generate a about:cache-entry URL for this entry... michael@0: nsAutoCString url; michael@0: url.AssignLiteral("about:cache-entry?client="); michael@0: url += clientID; michael@0: url.AppendLiteral("&sb="); michael@0: url += streamBased ? '1' : '0'; michael@0: url.AppendLiteral("&key="); michael@0: char* escapedKey = nsEscapeHTML(key.get()); michael@0: url += escapedKey; // key michael@0: michael@0: // Entry start... michael@0: mBuffer.AssignLiteral(" \n"); michael@0: michael@0: // URI michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: // Content length michael@0: uint32_t length = 0; michael@0: entryInfo->GetDataSize(&length); michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: // Number of accesses michael@0: int32_t fetchCount = 0; michael@0: entryInfo->GetFetchCount(&fetchCount); michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: // vars for reporting time michael@0: char buf[255]; michael@0: uint32_t t; michael@0: michael@0: // Last modified time michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: // Expires time michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: // Entry is done... michael@0: mBuffer.AppendLiteral(" \n"); michael@0: michael@0: mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); michael@0: michael@0: *visitNext = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAboutCache::ParseURI(nsIURI * uri, nsCString &deviceID) michael@0: { michael@0: // michael@0: // about:cache[?device=string] michael@0: // michael@0: nsresult rv; michael@0: michael@0: deviceID.Truncate(); michael@0: michael@0: nsAutoCString path; michael@0: rv = uri->GetPath(path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsACString::const_iterator start, valueStart, end; michael@0: path.BeginReading(start); michael@0: path.EndReading(end); michael@0: michael@0: valueStart = end; michael@0: if (!FindInReadable(NS_LITERAL_CSTRING("?device="), start, valueStart)) michael@0: return NS_OK; michael@0: michael@0: deviceID.Assign(Substring(valueStart, end)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: nsAboutCache* about = new nsAboutCache(); michael@0: if (about == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(about); michael@0: nsresult rv = about->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(about); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////////
KeyData sizeFetch countLast modifiedExpires
"); michael@0: mBuffer.Append(escapedKey); michael@0: nsMemory::Free(escapedKey); michael@0: mBuffer.AppendLiteral(""); michael@0: mBuffer.AppendInt(length); michael@0: mBuffer.AppendLiteral(" bytes"); michael@0: mBuffer.AppendInt(fetchCount); michael@0: mBuffer.AppendLiteral(""); michael@0: entryInfo->GetLastModified(&t); michael@0: if (t) { michael@0: PrintTimeString(buf, sizeof(buf), t); michael@0: mBuffer.Append(buf); michael@0: } else michael@0: mBuffer.AppendLiteral("No last modified time"); michael@0: mBuffer.AppendLiteral(""); michael@0: entryInfo->GetExpirationTime(&t); michael@0: if (t < 0xFFFFFFFF) { michael@0: PrintTimeString(buf, sizeof(buf), t); michael@0: mBuffer.Append(buf); michael@0: } else { michael@0: mBuffer.AppendLiteral("No expiration time"); michael@0: } michael@0: mBuffer.AppendLiteral("