diff options
Diffstat (limited to 'netwerk/protocol/about')
-rw-r--r-- | netwerk/protocol/about/moz.build | 34 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutBlank.cpp | 59 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutBlank.h | 35 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutCache.cpp | 585 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutCache.h | 136 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutCacheEntry.cpp | 603 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutCacheEntry.h | 98 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutProtocolHandler.cpp | 439 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutProtocolHandler.h | 90 | ||||
-rw-r--r-- | netwerk/protocol/about/nsAboutProtocolUtils.h | 71 | ||||
-rw-r--r-- | netwerk/protocol/about/nsIAboutModule.idl | 88 |
11 files changed, 2238 insertions, 0 deletions
diff --git a/netwerk/protocol/about/moz.build b/netwerk/protocol/about/moz.build new file mode 100644 index 000000000..60a10c9cc --- /dev/null +++ b/netwerk/protocol/about/moz.build @@ -0,0 +1,34 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + 'nsIAboutModule.idl', +] + +XPIDL_MODULE = 'necko_about' + +EXPORTS += [ + 'nsAboutProtocolUtils.h', +] + +UNIFIED_SOURCES += [ + 'nsAboutBlank.cpp', + 'nsAboutCache.cpp', + 'nsAboutCacheEntry.cpp', + 'nsAboutProtocolHandler.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/netwerk/base', + '/netwerk/cache2', +] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/netwerk/protocol/about/nsAboutBlank.cpp b/netwerk/protocol/about/nsAboutBlank.cpp new file mode 100644 index 000000000..be10be9ac --- /dev/null +++ b/netwerk/protocol/about/nsAboutBlank.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAboutBlank.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "nsContentUtils.h" + +NS_IMPL_ISUPPORTS(nsAboutBlank, nsIAboutModule) + +NS_IMETHODIMP +nsAboutBlank::NewChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + NS_ENSURE_ARG_POINTER(aURI); + + nsCOMPtr<nsIInputStream> in; + nsresult rv = NS_NewCStringInputStream(getter_AddRefs(in), EmptyCString()); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), + aURI, + in, + NS_LITERAL_CSTRING("text/html"), + NS_LITERAL_CSTRING("utf-8"), + aLoadInfo); + if (NS_FAILED(rv)) return rv; + + channel.forget(result); + return rv; +} + +NS_IMETHODIMP +nsAboutBlank::GetURIFlags(nsIURI *aURI, uint32_t *result) +{ + *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::URI_CAN_LOAD_IN_CHILD | + nsIAboutModule::MAKE_LINKABLE | + nsIAboutModule::HIDE_FROM_ABOUTABOUT; + return NS_OK; +} + +nsresult +nsAboutBlank::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + nsAboutBlank* about = new nsAboutBlank(); + if (about == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(about); + nsresult rv = about->QueryInterface(aIID, aResult); + NS_RELEASE(about); + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/netwerk/protocol/about/nsAboutBlank.h b/netwerk/protocol/about/nsAboutBlank.h new file mode 100644 index 000000000..aae6e072d --- /dev/null +++ b/netwerk/protocol/about/nsAboutBlank.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAboutBlank_h__ +#define nsAboutBlank_h__ + +#include "nsIAboutModule.h" + +class nsAboutBlank : public nsIAboutModule +{ +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIABOUTMODULE + + nsAboutBlank() {} + + static nsresult + Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + +private: + virtual ~nsAboutBlank() {} +}; + +#define NS_ABOUT_BLANK_MODULE_CID \ +{ /* 3decd6c8-30ef-11d3-8cd0-0060b0fc14a3 */ \ + 0x3decd6c8, \ + 0x30ef, \ + 0x11d3, \ + {0x8c, 0xd0, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ +} + +#endif // nsAboutBlank_h__ diff --git a/netwerk/protocol/about/nsAboutCache.cpp b/netwerk/protocol/about/nsAboutCache.cpp new file mode 100644 index 000000000..2eb5e3b42 --- /dev/null +++ b/netwerk/protocol/about/nsAboutCache.cpp @@ -0,0 +1,585 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAboutCache.h" +#include "nsIInputStream.h" +#include "nsIStorageStream.h" +#include "nsIURI.h" +#include "nsCOMPtr.h" +#include "nsNetUtil.h" +#include "nsIPipe.h" +#include "nsContentUtils.h" +#include "nsEscape.h" +#include "nsAboutProtocolUtils.h" +#include "nsPrintfCString.h" + +#include "nsICacheStorageService.h" +#include "nsICacheStorage.h" +#include "CacheFileUtils.h" +#include "CacheObserver.h" + +#include "nsThreadUtils.h" + +using namespace mozilla::net; + +NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule) +NS_IMPL_ISUPPORTS(nsAboutCache::Channel, nsIChannel, nsIRequest, nsICacheStorageVisitor) + +NS_IMETHODIMP +nsAboutCache::NewChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + nsresult rv; + + NS_ENSURE_ARG_POINTER(aURI); + + RefPtr<Channel> channel = new Channel(); + rv = channel->Init(aURI, aLoadInfo); + if (NS_FAILED(rv)) return rv; + + channel.forget(result); + + return NS_OK; +} + +nsresult +nsAboutCache::Channel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) +{ + nsresult rv; + + mCancel = false; + + nsCOMPtr<nsIInputStream> inputStream; + rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(mStream), + 16384, (uint32_t)-1, + true, // non-blocking input + false // blocking output + ); + if (NS_FAILED(rv)) return rv; + + nsAutoCString storageName; + rv = ParseURI(aURI, storageName); + if (NS_FAILED(rv)) return rv; + + mOverview = storageName.IsEmpty(); + if (mOverview) { + // ...and visit all we can + mStorageList.AppendElement(NS_LITERAL_CSTRING("memory")); + mStorageList.AppendElement(NS_LITERAL_CSTRING("disk")); + mStorageList.AppendElement(NS_LITERAL_CSTRING("appcache")); + } else { + // ...and visit just the specified storage, entries will output too + mStorageList.AppendElement(storageName); + } + + // The entries header is added on encounter of the first entry + mEntriesHeaderAdded = false; + + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel), + aURI, + inputStream, + NS_LITERAL_CSTRING("text/html"), + NS_LITERAL_CSTRING("utf-8"), + aLoadInfo); + if (NS_FAILED(rv)) return rv; + + mBuffer.AssignLiteral( + "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + " <title>Network Cache Storage Information</title>\n" + " <meta charset=\"utf-8\">\n" + " <link rel=\"stylesheet\" href=\"chrome://global/skin/about.css\"/>\n" + " <link rel=\"stylesheet\" href=\"chrome://global/skin/aboutCache.css\"/>\n" + " <script src=\"chrome://global/content/aboutCache.js\"></script>" + "</head>\n" + "<body class=\"aboutPageWideContainer\">\n" + "<h1>Information about the Network Cache Storage Service</h1>\n"); + + // Add the context switch controls + mBuffer.AppendLiteral( + "<label><input id='priv' type='checkbox'/> Private</label>\n" + "<label><input id='anon' type='checkbox'/> Anonymous</label>\n" + ); + + if (CacheObserver::UseNewCache()) { + // Visit scoping by browser and appid is not implemented for + // the old cache, simply don't add these controls. + // The appid/inbrowser entries are already mixed in the default + // view anyway. + mBuffer.AppendLiteral( + "<label><input id='appid' type='text' size='6'/> AppID</label>\n" + "<label><input id='inbrowser' type='checkbox'/> In Browser Element</label>\n" + ); + } + + mBuffer.AppendLiteral( + "<label><input id='submit' type='button' value='Update' onclick='navigate()'/></label>\n" + ); + + if (!mOverview) { + mBuffer.AppendLiteral("<a href=\"about:cache?storage=&context="); + char* escapedContext = nsEscapeHTML(mContextString.get()); + mBuffer.Append(escapedContext); + free(escapedContext); + mBuffer.AppendLiteral("\">Back to overview</a>"); + } + + rv = FlushBuffer(); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to flush buffer"); + } + + return NS_OK; +} + +NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) +{ + nsresult rv; + + if (!mChannel) { + return NS_ERROR_UNEXPECTED; + } + + // Kick the walk loop. + rv = VisitNextStorage(); + if (NS_FAILED(rv)) return rv; + + MOZ_ASSERT(!aContext, "asyncOpen2() does not take a context argument"); + rv = NS_MaybeOpenChannelUsingAsyncOpen2(mChannel, aListener); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen2(nsIStreamListener *aListener) +{ + return AsyncOpen(aListener, nullptr); +} + +NS_IMETHODIMP nsAboutCache::Channel::Open(nsIInputStream * *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsAboutCache::Channel::Open2(nsIInputStream * *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsAboutCache::Channel::ParseURI(nsIURI * uri, nsACString & storage) +{ + // + // about:cache[?storage=<storage-name>[&context=<context-key>]] + // + nsresult rv; + + nsAutoCString path; + rv = uri->GetPath(path); + if (NS_FAILED(rv)) return rv; + + mContextString.Truncate(); + mLoadInfo = CacheFileUtils::ParseKey(NS_LITERAL_CSTRING("")); + storage.Truncate(); + + nsACString::const_iterator start, valueStart, end; + path.BeginReading(start); + path.EndReading(end); + + valueStart = end; + if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), start, valueStart)) { + return NS_OK; + } + + nsACString::const_iterator storageNameBegin = valueStart; + + start = valueStart; + valueStart = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&context="), start, valueStart)) + start = end; + + nsACString::const_iterator storageNameEnd = start; + + mContextString = Substring(valueStart, end); + mLoadInfo = CacheFileUtils::ParseKey(mContextString); + storage.Assign(Substring(storageNameBegin, storageNameEnd)); + + return NS_OK; +} + +nsresult +nsAboutCache::Channel::VisitNextStorage() +{ + if (!mStorageList.Length()) + return NS_ERROR_NOT_AVAILABLE; + + mStorageName = mStorageList[0]; + mStorageList.RemoveElementAt(0); + + // Must re-dispatch since we cannot start another visit cycle + // from visitor callback. The cache v1 service doesn't like it. + // TODO - mayhemer, bug 913828, remove this dispatch and call + // directly. + return NS_DispatchToMainThread(mozilla::NewRunnableMethod(this, &nsAboutCache::Channel::FireVisitStorage)); +} + +void +nsAboutCache::Channel::FireVisitStorage() +{ + nsresult rv; + + rv = VisitStorage(mStorageName); + if (NS_FAILED(rv)) { + if (mLoadInfo) { + char* escaped = nsEscapeHTML(mStorageName.get()); + mBuffer.Append( + nsPrintfCString("<p>Unrecognized storage name '%s' in about:cache URL</p>", + escaped)); + free(escaped); + } else { + char* escaped = nsEscapeHTML(mContextString.get()); + mBuffer.Append( + nsPrintfCString("<p>Unrecognized context key '%s' in about:cache URL</p>", + escaped)); + free(escaped); + } + + rv = FlushBuffer(); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to flush buffer"); + } + + // Simulate finish of a visit cycle, this tries the next storage + // or closes the output stream (i.e. the UI loader will stop spinning) + OnCacheEntryVisitCompleted(); + } +} + +nsresult +nsAboutCache::Channel::VisitStorage(nsACString const & storageName) +{ + nsresult rv; + + rv = GetStorage(storageName, mLoadInfo, getter_AddRefs(mStorage)); + if (NS_FAILED(rv)) return rv; + + rv = mStorage->AsyncVisitStorage(this, !mOverview); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +//static +nsresult +nsAboutCache::GetStorage(nsACString const & storageName, + nsILoadContextInfo* loadInfo, + nsICacheStorage **storage) +{ + nsresult rv; + + nsCOMPtr<nsICacheStorageService> cacheService = + do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsICacheStorage> cacheStorage; + if (storageName == "disk") { + rv = cacheService->DiskCacheStorage( + loadInfo, false, getter_AddRefs(cacheStorage)); + } else if (storageName == "memory") { + rv = cacheService->MemoryCacheStorage( + loadInfo, getter_AddRefs(cacheStorage)); + } else if (storageName == "appcache") { + rv = cacheService->AppCacheStorage( + loadInfo, nullptr, getter_AddRefs(cacheStorage)); + } else { + rv = NS_ERROR_UNEXPECTED; + } + if (NS_FAILED(rv)) return rv; + + cacheStorage.forget(storage); + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCache::Channel::OnCacheStorageInfo(uint32_t aEntryCount, uint64_t aConsumption, + uint64_t aCapacity, nsIFile * aDirectory) +{ + // We need mStream for this + if (!mStream) { + return NS_ERROR_FAILURE; + } + + mBuffer.AssignLiteral("<h2>"); + mBuffer.Append(mStorageName); + mBuffer.AppendLiteral("</h2>\n" + "<table id=\""); + mBuffer.AppendLiteral("\">\n"); + + // Write out cache info + // Number of entries + mBuffer.AppendLiteral(" <tr>\n" + " <th>Number of entries:</th>\n" + " <td>"); + mBuffer.AppendInt(aEntryCount); + mBuffer.AppendLiteral("</td>\n" + " </tr>\n"); + + // Maximum storage size + mBuffer.AppendLiteral(" <tr>\n" + " <th>Maximum storage size:</th>\n" + " <td>"); + mBuffer.AppendInt(aCapacity / 1024); + mBuffer.AppendLiteral(" KiB</td>\n" + " </tr>\n"); + + // Storage in use + mBuffer.AppendLiteral(" <tr>\n" + " <th>Storage in use:</th>\n" + " <td>"); + mBuffer.AppendInt(aConsumption / 1024); + mBuffer.AppendLiteral(" KiB</td>\n" + " </tr>\n"); + + // Storage disk location + mBuffer.AppendLiteral(" <tr>\n" + " <th>Storage disk location:</th>\n" + " <td>"); + if (aDirectory) { + nsAutoString path; + aDirectory->GetPath(path); + mBuffer.Append(NS_ConvertUTF16toUTF8(path)); + } else { + mBuffer.AppendLiteral("none, only stored in memory"); + } + mBuffer.AppendLiteral(" </td>\n" + " </tr>\n"); + + if (mOverview) { // The about:cache case + if (aEntryCount != 0) { // Add the "List Cache Entries" link + mBuffer.AppendLiteral(" <tr>\n" + " <th><a href=\"about:cache?storage="); + mBuffer.Append(mStorageName); + mBuffer.AppendLiteral("&context="); + char* escapedContext = nsEscapeHTML(mContextString.get()); + mBuffer.Append(escapedContext); + free(escapedContext); + mBuffer.AppendLiteral("\">List Cache Entries</a></th>\n" + " </tr>\n"); + } + } + + mBuffer.AppendLiteral("</table>\n"); + + // The entries header is added on encounter of the first entry + mEntriesHeaderAdded = false; + + nsresult rv = FlushBuffer(); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to flush buffer"); + } + + if (mOverview) { + // OnCacheEntryVisitCompleted() is not called when we do not iterate + // cache entries. Since this moves forward to the next storage in + // the list we want to visit, artificially call it here. + OnCacheEntryVisitCompleted(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCache::Channel::OnCacheEntryInfo(nsIURI *aURI, const nsACString & aIdEnhance, + int64_t aDataSize, int32_t aFetchCount, + uint32_t aLastModified, uint32_t aExpirationTime, + bool aPinned) +{ + // We need mStream for this + if (!mStream || mCancel) { + // Returning a failure from this callback stops the iteration + return NS_ERROR_FAILURE; + } + + if (!mEntriesHeaderAdded) { + mBuffer.AppendLiteral("<hr/>\n" + "<table id=\"entries\">\n" + " <colgroup>\n" + " <col id=\"col-key\">\n" + " <col id=\"col-dataSize\">\n" + " <col id=\"col-fetchCount\">\n" + " <col id=\"col-lastModified\">\n" + " <col id=\"col-expires\">\n" + " <col id=\"col-pinned\">\n" + " </colgroup>\n" + " <thead>\n" + " <tr>\n" + " <th>Key</th>\n" + " <th>Data size</th>\n" + " <th>Fetch count</th>\n" + " <th>Last Modifed</th>\n" + " <th>Expires</th>\n" + " <th>Pinning</th>\n" + " </tr>\n" + " </thead>\n"); + mEntriesHeaderAdded = true; + } + + // Generate a about:cache-entry URL for this entry... + + nsAutoCString url; + url.AssignLiteral("about:cache-entry?storage="); + url.Append(mStorageName); + + url.AppendLiteral("&context="); + char* escapedContext = nsEscapeHTML(mContextString.get()); + url += escapedContext; + free(escapedContext); + + url.AppendLiteral("&eid="); + char* escapedEID = nsEscapeHTML(aIdEnhance.BeginReading()); + url += escapedEID; + free(escapedEID); + + nsAutoCString cacheUriSpec; + aURI->GetAsciiSpec(cacheUriSpec); + char* escapedCacheURI = nsEscapeHTML(cacheUriSpec.get()); + url.AppendLiteral("&uri="); + url += escapedCacheURI; + + // Entry start... + mBuffer.AppendLiteral(" <tr>\n"); + + // URI + mBuffer.AppendLiteral(" <td><a href=\""); + mBuffer.Append(url); + mBuffer.AppendLiteral("\">"); + if (!aIdEnhance.IsEmpty()) { + mBuffer.Append(aIdEnhance); + mBuffer.Append(':'); + } + mBuffer.Append(escapedCacheURI); + mBuffer.AppendLiteral("</a></td>\n"); + + free(escapedCacheURI); + + // Content length + mBuffer.AppendLiteral(" <td>"); + mBuffer.AppendInt(aDataSize); + mBuffer.AppendLiteral(" bytes</td>\n"); + + // Number of accesses + mBuffer.AppendLiteral(" <td>"); + mBuffer.AppendInt(aFetchCount); + mBuffer.AppendLiteral("</td>\n"); + + // vars for reporting time + char buf[255]; + + // Last modified time + mBuffer.AppendLiteral(" <td>"); + if (aLastModified) { + PrintTimeString(buf, sizeof(buf), aLastModified); + mBuffer.Append(buf); + } else { + mBuffer.AppendLiteral("No last modified time"); + } + mBuffer.AppendLiteral("</td>\n"); + + // Expires time + mBuffer.AppendLiteral(" <td>"); + if (aExpirationTime < 0xFFFFFFFF) { + PrintTimeString(buf, sizeof(buf), aExpirationTime); + mBuffer.Append(buf); + } else { + mBuffer.AppendLiteral("No expiration time"); + } + mBuffer.AppendLiteral("</td>\n"); + + // Pinning + mBuffer.AppendLiteral(" <td>"); + if (aPinned) { + mBuffer.Append(NS_LITERAL_CSTRING("Pinned")); + } else { + mBuffer.Append(NS_LITERAL_CSTRING(" ")); + } + mBuffer.AppendLiteral("</td>\n"); + + // Entry is done... + mBuffer.AppendLiteral(" </tr>\n"); + + return FlushBuffer(); +} + +NS_IMETHODIMP +nsAboutCache::Channel::OnCacheEntryVisitCompleted() +{ + if (!mStream) { + return NS_ERROR_FAILURE; + } + + if (mEntriesHeaderAdded) { + mBuffer.AppendLiteral("</table>\n"); + } + + // Kick another storage visiting (from a storage that allows us.) + while (mStorageList.Length()) { + nsresult rv = VisitNextStorage(); + if (NS_SUCCEEDED(rv)) { + // Expecting new round of OnCache* calls. + return NS_OK; + } + } + + // We are done! + mBuffer.AppendLiteral("</body>\n" + "</html>\n"); + nsresult rv = FlushBuffer(); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to flush buffer"); + } + mStream->Close(); + + return NS_OK; +} + +nsresult +nsAboutCache::Channel::FlushBuffer() +{ + nsresult rv; + + uint32_t bytesWritten; + rv = mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); + mBuffer.Truncate(); + + if (NS_FAILED(rv)) { + mCancel = true; + } + + return rv; +} + +NS_IMETHODIMP +nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result) +{ + *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT; + return NS_OK; +} + +// static +nsresult +nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + nsAboutCache* about = new nsAboutCache(); + if (about == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(about); + nsresult rv = about->QueryInterface(aIID, aResult); + NS_RELEASE(about); + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/netwerk/protocol/about/nsAboutCache.h b/netwerk/protocol/about/nsAboutCache.h new file mode 100644 index 000000000..c2d1af850 --- /dev/null +++ b/netwerk/protocol/about/nsAboutCache.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAboutCache_h__ +#define nsAboutCache_h__ + +#include "nsIAboutModule.h" +#include "nsICacheStorageVisitor.h" +#include "nsICacheStorage.h" + +#include "nsString.h" +#include "nsIOutputStream.h" +#include "nsILoadContextInfo.h" + +#include "nsCOMPtr.h" +#include "nsTArray.h" + +#define NS_FORWARD_SAFE_NSICHANNEL_SUBSET(_to) \ + NS_IMETHOD GetOriginalURI(nsIURI * *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOriginalURI(aOriginalURI); } \ + NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOriginalURI(aOriginalURI); } \ + NS_IMETHOD GetURI(nsIURI * *aURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetURI(aURI); } \ + NS_IMETHOD GetOwner(nsISupports * *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOwner(aOwner); } \ + NS_IMETHOD SetOwner(nsISupports *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOwner(aOwner); } \ + NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetNotificationCallbacks(aNotificationCallbacks); } \ + NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetNotificationCallbacks(aNotificationCallbacks); } \ + NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSecurityInfo(aSecurityInfo); } \ + NS_IMETHOD GetContentType(nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentType(aContentType); } \ + NS_IMETHOD SetContentType(const nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentType(aContentType); } \ + NS_IMETHOD GetContentCharset(nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentCharset(aContentCharset); } \ + NS_IMETHOD SetContentCharset(const nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentCharset(aContentCharset); } \ + NS_IMETHOD GetContentLength(int64_t *aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentLength(aContentLength); } \ + NS_IMETHOD SetContentLength(int64_t aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentLength(aContentLength); } \ + NS_IMETHOD GetContentDisposition(uint32_t *aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDisposition(aContentDisposition); } \ + NS_IMETHOD SetContentDisposition(uint32_t aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDisposition(aContentDisposition); } \ + NS_IMETHOD GetContentDispositionFilename(nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionFilename(aContentDispositionFilename); } \ + NS_IMETHOD SetContentDispositionFilename(const nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDispositionFilename(aContentDispositionFilename); } \ + NS_IMETHOD GetContentDispositionHeader(nsACString & aContentDispositionHeader) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionHeader(aContentDispositionHeader); } \ + NS_IMETHOD GetLoadInfo(nsILoadInfo * *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetLoadInfo(aLoadInfo); } \ + NS_IMETHOD SetLoadInfo(nsILoadInfo *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetLoadInfo(aLoadInfo); } \ + +class nsAboutCache final : public nsIAboutModule +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIABOUTMODULE + + nsAboutCache() {} + + static MOZ_MUST_USE nsresult + Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + + static MOZ_MUST_USE nsresult + GetStorage(nsACString const & storageName, nsILoadContextInfo* loadInfo, + nsICacheStorage **storage); + +protected: + virtual ~nsAboutCache() {} + + class Channel final : public nsIChannel + , public nsICacheStorageVisitor + { + NS_DECL_ISUPPORTS + NS_DECL_NSICACHESTORAGEVISITOR + NS_FORWARD_SAFE_NSIREQUEST(mChannel) + NS_FORWARD_SAFE_NSICHANNEL_SUBSET(mChannel) + NS_IMETHOD AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) override; + NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override; + NS_IMETHOD Open(nsIInputStream * *_retval) override; + NS_IMETHOD Open2(nsIInputStream * *_retval) override; + + private: + virtual ~Channel() {} + + public: + MOZ_MUST_USE nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo); + MOZ_MUST_USE nsresult ParseURI(nsIURI * uri, nsACString & storage); + + // Finds a next storage we wish to visit (we use this method + // even there is a specified storage name, which is the only + // one in the list then.) Posts FireVisitStorage() when found. + MOZ_MUST_USE nsresult VisitNextStorage(); + // Helper method that calls VisitStorage() for the current storage. + // When it fails, OnCacheEntryVisitCompleted is simulated to close + // the output stream and thus the about:cache channel. + void FireVisitStorage(); + // Kiks the visit cycle for the given storage, names can be: + // "disk", "memory", "appcache" + // Note: any newly added storage type has to be manually handled here. + MOZ_MUST_USE nsresult VisitStorage(nsACString const & storageName); + + // Writes content of mBuffer to mStream and truncates + // the buffer. It may fail when the input stream is closed by canceling + // the input stream channel. It can be used to stop the cache iteration + // process. + MOZ_MUST_USE nsresult FlushBuffer(); + + // Whether we are showing overview status of all available + // storages. + bool mOverview; + + // Flag initially false, that indicates the entries header has + // been added to the output HTML. + bool mEntriesHeaderAdded; + + // Cancelation flag + bool mCancel; + + // The context we are working with. + nsCOMPtr<nsILoadContextInfo> mLoadInfo; + nsCString mContextString; + + // The list of all storage names we want to visit + nsTArray<nsCString> mStorageList; + nsCString mStorageName; + nsCOMPtr<nsICacheStorage> mStorage; + + // Output data buffering and streaming output + nsCString mBuffer; + nsCOMPtr<nsIOutputStream> mStream; + + // The input stream channel, the one that actually does the job + nsCOMPtr<nsIChannel> mChannel; + }; +}; + +#define NS_ABOUT_CACHE_MODULE_CID \ +{ /* 9158c470-86e4-11d4-9be2-00e09872a416 */ \ + 0x9158c470, \ + 0x86e4, \ + 0x11d4, \ + {0x9b, 0xe2, 0x00, 0xe0, 0x98, 0x72, 0xa4, 0x16} \ +} + +#endif // nsAboutCache_h__ diff --git a/netwerk/protocol/about/nsAboutCacheEntry.cpp b/netwerk/protocol/about/nsAboutCacheEntry.cpp new file mode 100644 index 000000000..183395976 --- /dev/null +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp @@ -0,0 +1,603 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAboutCacheEntry.h" + +#include "mozilla/Sprintf.h" + +#include "nsAboutCache.h" +#include "nsICacheStorage.h" +#include "CacheObserver.h" +#include "nsNetUtil.h" +#include "nsEscape.h" +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsAboutProtocolUtils.h" +#include "nsContentUtils.h" +#include "nsInputStreamPump.h" +#include "CacheFileUtils.h" +#include <algorithm> +#include "nsIPipe.h" + +using namespace mozilla::net; + +#define HEXDUMP_MAX_ROWS 16 + +static void +HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result) +{ + char temp[16]; + + const unsigned char *p; + while (n) { + SprintfLiteral(temp, "%08x: ", *state); + result.Append(temp); + *state += HEXDUMP_MAX_ROWS; + + p = (const unsigned char *) buf; + + int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n); + + // print hex codes: + for (i = 0; i < row_max; ++i) { + SprintfLiteral(temp, "%02x ", *p++); + result.Append(temp); + } + for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) { + result.AppendLiteral(" "); + } + + // print ASCII glyphs if possible: + p = (const unsigned char *) buf; + for (i = 0; i < row_max; ++i, ++p) { + switch (*p) { + case '<': + result.AppendLiteral("<"); + break; + case '>': + result.AppendLiteral(">"); + break; + case '&': + result.AppendLiteral("&"); + break; + default: + if (*p < 0x7F && *p > 0x1F) { + result.Append(*p); + } else { + result.Append('.'); + } + } + } + + result.Append('\n'); + + buf += row_max; + n -= row_max; + } +} + +//----------------------------------------------------------------------------- +// nsAboutCacheEntry::nsISupports + +NS_IMPL_ISUPPORTS(nsAboutCacheEntry, + nsIAboutModule) +NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel, + nsICacheEntryOpenCallback, + nsICacheEntryMetaDataVisitor, + nsIStreamListener, + nsIChannel) + +//----------------------------------------------------------------------------- +// nsAboutCacheEntry::nsIAboutModule + +NS_IMETHODIMP +nsAboutCacheEntry::NewChannel(nsIURI* uri, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + NS_ENSURE_ARG_POINTER(uri); + nsresult rv; + + RefPtr<Channel> channel = new Channel(); + rv = channel->Init(uri, aLoadInfo); + if (NS_FAILED(rv)) return rv; + + channel.forget(result); + + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result) +{ + *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT | + nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsAboutCacheEntry::Channel + +nsresult +nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo) +{ + nsresult rv; + + nsCOMPtr<nsIInputStream> stream; + rv = GetContentStream(uri, getter_AddRefs(stream)); + if (NS_FAILED(rv)) return rv; + + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel), + uri, + stream, + NS_LITERAL_CSTRING("text/html"), + NS_LITERAL_CSTRING("utf-8"), + aLoadInfo); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +nsresult +nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result) +{ + nsresult rv; + + // Init: (block size, maximum length) + nsCOMPtr<nsIAsyncInputStream> inputStream; + rv = NS_NewPipe2(getter_AddRefs(inputStream), + getter_AddRefs(mOutputStream), + true, false, + 256, UINT32_MAX); + if (NS_FAILED(rv)) return rv; + + NS_NAMED_LITERAL_CSTRING( + buffer, + "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + " <title>Cache entry information</title>\n" + " <link rel=\"stylesheet\" " + "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n" + " <link rel=\"stylesheet\" " + "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n" + "</head>\n" + "<body>\n" + "<h1>Cache entry information</h1>\n"); + uint32_t n; + rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n); + if (NS_FAILED(rv)) return rv; + if (n != buffer.Length()) return NS_ERROR_UNEXPECTED; + + rv = OpenCacheEntry(uri); + if (NS_FAILED(rv)) return rv; + + inputStream.forget(result); + return NS_OK; +} + +nsresult +nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri) +{ + nsresult rv; + + rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo), + mEnhanceId, getter_AddRefs(mCacheURI)); + if (NS_FAILED(rv)) return rv; + + if (!CacheObserver::UseNewCache() && + mLoadInfo->IsPrivate() && + mStorageName.EqualsLiteral("disk")) { + // The cache v1 is storing all private entries in the memory-only + // cache, so it would not be found in the v1 disk cache. + mStorageName = NS_LITERAL_CSTRING("memory"); + } + + return OpenCacheEntry(); +} + +nsresult +nsAboutCacheEntry::Channel::OpenCacheEntry() +{ + nsresult rv; + + nsCOMPtr<nsICacheStorage> storage; + rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage)); + if (NS_FAILED(rv)) return rv; + + // Invokes OnCacheEntryAvailable() + rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId, + nsICacheStorage::OPEN_READONLY | + nsICacheStorage::OPEN_SECRETLY, + this); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +nsresult +nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri, + nsACString &storageName, + nsILoadContextInfo **loadInfo, + nsCString &enahnceID, + nsIURI **cacheUri) +{ + // + // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string] + // + nsresult rv; + + nsAutoCString path; + rv = uri->GetPath(path); + if (NS_FAILED(rv)) + return rv; + + nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end; + path.BeginReading(begin); + path.EndReading(end); + + keyBegin = begin; keyEnd = end; + if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd)) + return NS_ERROR_FAILURE; + + valBegin = keyEnd; // the value of the storage key starts after the key + + keyBegin = keyEnd; keyEnd = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd)) + return NS_ERROR_FAILURE; + + storageName.Assign(Substring(valBegin, keyBegin)); + valBegin = keyEnd; // the value of the context key starts after the key + + keyBegin = keyEnd; keyEnd = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd)) + return NS_ERROR_FAILURE; + + nsAutoCString contextKey(Substring(valBegin, keyBegin)); + valBegin = keyEnd; // the value of the eid key starts after the key + + keyBegin = keyEnd; keyEnd = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd)) + return NS_ERROR_FAILURE; + + enahnceID.Assign(Substring(valBegin, keyBegin)); + + valBegin = keyEnd; // the value of the uri key starts after the key + nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one + + // Uf... parsing done, now get some objects from it... + + nsCOMPtr<nsILoadContextInfo> info = + CacheFileUtils::ParseKey(contextKey); + if (!info) + return NS_ERROR_FAILURE; + info.forget(loadInfo); + + rv = NS_NewURI(cacheUri, uriSpec); + if (NS_FAILED(rv)) + return rv; + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsICacheEntryOpenCallback implementation +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry, + nsIApplicationCache *aApplicationCache, + uint32_t *result) +{ + *result = nsICacheEntryOpenCallback::ENTRY_WANTED; + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry, + bool isNew, + nsIApplicationCache *aApplicationCache, + nsresult status) +{ + nsresult rv; + + mWaitingForData = false; + if (entry) { + rv = WriteCacheEntryDescription(entry); + } else if (!CacheObserver::UseNewCache() && + !mLoadInfo->IsPrivate() && + mStorageName.EqualsLiteral("memory")) { + // If we were not able to find the entry in the memory storage + // try again in the disk storage. + // This is a workaround for cache v1: when an originally disk + // cache entry is recreated as memory-only, it's clientID doesn't + // change and we cannot find it in "HTTP-memory-only" session. + // "Disk" cache storage looks at "HTTP". + mStorageName = NS_LITERAL_CSTRING("disk"); + rv = OpenCacheEntry(); + if (NS_SUCCEEDED(rv)) { + return NS_OK; + } + } else { + rv = WriteCacheEntryUnavailable(); + } + if (NS_FAILED(rv)) return rv; + + + if (!mWaitingForData) { + // Data is not expected, close the output of content now. + CloseContent(); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// Print-out helper methods +//----------------------------------------------------------------------------- + +#define APPEND_ROW(label, value) \ + PR_BEGIN_MACRO \ + buffer.AppendLiteral(" <tr>\n" \ + " <th>"); \ + buffer.AppendLiteral(label); \ + buffer.AppendLiteral(":</th>\n" \ + " <td>"); \ + buffer.Append(value); \ + buffer.AppendLiteral("</td>\n" \ + " </tr>\n"); \ + PR_END_MACRO + +nsresult +nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry) +{ + nsresult rv; + nsCString buffer; + uint32_t n; + + nsAutoCString str; + + rv = entry->GetKey(str); + if (NS_FAILED(rv)) return rv; + + buffer.SetCapacity(4096); + buffer.AssignLiteral("<table>\n" + " <tr>\n" + " <th>key:</th>\n" + " <td id=\"td-key\">"); + + // Test if the key is actually a URI + nsCOMPtr<nsIURI> uri; + bool isJS = false; + bool isData = false; + + rv = NS_NewURI(getter_AddRefs(uri), str); + // javascript: and data: URLs should not be linkified + // since clicking them can cause scripts to run - bug 162584 + if (NS_SUCCEEDED(rv)) { + uri->SchemeIs("javascript", &isJS); + uri->SchemeIs("data", &isData); + } + char* escapedStr = nsEscapeHTML(str.get()); + if (NS_SUCCEEDED(rv) && !(isJS || isData)) { + buffer.AppendLiteral("<a href=\""); + buffer.Append(escapedStr); + buffer.AppendLiteral("\">"); + buffer.Append(escapedStr); + buffer.AppendLiteral("</a>"); + uri = nullptr; + } else { + buffer.Append(escapedStr); + } + free(escapedStr); + buffer.AppendLiteral("</td>\n" + " </tr>\n"); + + // temp vars for reporting + char timeBuf[255]; + uint32_t u = 0; + int32_t i = 0; + nsAutoCString s; + + // Fetch Count + s.Truncate(); + entry->GetFetchCount(&i); + s.AppendInt(i); + APPEND_ROW("fetch count", s); + + // Last Fetched + entry->GetLastFetched(&u); + if (u) { + PrintTimeString(timeBuf, sizeof(timeBuf), u); + APPEND_ROW("last fetched", timeBuf); + } else { + APPEND_ROW("last fetched", "No last fetch time (bug 1000338)"); + } + + // Last Modified + entry->GetLastModified(&u); + if (u) { + PrintTimeString(timeBuf, sizeof(timeBuf), u); + APPEND_ROW("last modified", timeBuf); + } else { + APPEND_ROW("last modified", "No last modified time (bug 1000338)"); + } + + // Expiration Time + entry->GetExpirationTime(&u); + if (u < 0xFFFFFFFF) { + PrintTimeString(timeBuf, sizeof(timeBuf), u); + APPEND_ROW("expires", timeBuf); + } else { + APPEND_ROW("expires", "No expiration time"); + } + + // Data Size + s.Truncate(); + uint32_t dataSize; + if (NS_FAILED(entry->GetStorageDataSize(&dataSize))) + dataSize = 0; + s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed. + s.AppendLiteral(" B"); + APPEND_ROW("Data size", s); + + // TODO - mayhemer + // Here used to be a link to the disk file (in the old cache for entries that + // did not fit any of the block files, in the new cache every time). + // I'd rather have a small set of buttons here to action on the entry: + // 1. save the content + // 2. save as a complete HTTP response (response head, headers, content) + // 3. doom the entry + // A new bug(s) should be filed here. + + // Security Info + nsCOMPtr<nsISupports> securityInfo; + entry->GetSecurityInfo(getter_AddRefs(securityInfo)); + if (securityInfo) { + APPEND_ROW("Security", "This is a secure document."); + } else { + APPEND_ROW("Security", + "This document does not have any security info associated with it."); + } + + buffer.AppendLiteral("</table>\n" + "<hr/>\n" + "<table>\n"); + + mBuffer = &buffer; // make it available for OnMetaDataElement(). + entry->VisitMetaData(this); + mBuffer = nullptr; + + buffer.AppendLiteral("</table>\n"); + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + buffer.Truncate(); + + // Provide a hexdump of the data + if (!dataSize) { + return NS_OK; + } + + nsCOMPtr<nsIInputStream> stream; + entry->OpenInputStream(0, getter_AddRefs(stream)); + if (!stream) { + return NS_OK; + } + + RefPtr<nsInputStreamPump> pump; + rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream); + if (NS_FAILED(rv)) { + return NS_OK; // just ignore + } + + rv = pump->AsyncRead(this, nullptr); + if (NS_FAILED(rv)) { + return NS_OK; // just ignore + } + + mWaitingForData = true; + return NS_OK; +} + +nsresult +nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable() +{ + uint32_t n; + NS_NAMED_LITERAL_CSTRING(buffer, + "The cache entry you selected is not available."); + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsICacheEntryMetaDataVisitor implementation +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value) +{ + mBuffer->AppendLiteral(" <tr>\n" + " <th>"); + mBuffer->Append(key); + mBuffer->AppendLiteral(":</th>\n" + " <td>"); + char* escapedValue = nsEscapeHTML(value); + mBuffer->Append(escapedValue); + free(escapedValue); + mBuffer->AppendLiteral("</td>\n" + " </tr>\n"); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsIStreamListener implementation +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx) +{ + mHexDumpState = 0; + + NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>"); + uint32_t n; + return mOutputStream->Write(buffer.get(), buffer.Length(), &n); +} + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx, + nsIInputStream *aInputStream, + uint64_t aOffset, + uint32_t aCount) +{ + uint32_t n; + return aInputStream->ReadSegments( + &nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n); +} + +/* static */ nsresult +nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream, + void *aClosure, + const char *aFromSegment, + uint32_t aToOffset, + uint32_t aCount, + uint32_t *aWriteCount) +{ + nsAboutCacheEntry::Channel *a = + static_cast<nsAboutCacheEntry::Channel*>(aClosure); + + nsCString buffer; + HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer); + + uint32_t n; + a->mOutputStream->Write(buffer.get(), buffer.Length(), &n); + + *aWriteCount = aCount; + + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx, + nsresult result) +{ + NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n"); + uint32_t n; + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + + CloseContent(); + + return NS_OK; +} + +void +nsAboutCacheEntry::Channel::CloseContent() +{ + NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n"); + uint32_t n; + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + + mOutputStream->Close(); + mOutputStream = nullptr; +} diff --git a/netwerk/protocol/about/nsAboutCacheEntry.h b/netwerk/protocol/about/nsAboutCacheEntry.h new file mode 100644 index 000000000..44a78760b --- /dev/null +++ b/netwerk/protocol/about/nsAboutCacheEntry.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAboutCacheEntry_h__ +#define nsAboutCacheEntry_h__ + +#include "nsIAboutModule.h" +#include "nsICacheEntryOpenCallback.h" +#include "nsICacheEntry.h" +#include "nsIStreamListener.h" +#include "nsString.h" +#include "nsCOMPtr.h" + +class nsIAsyncOutputStream; +class nsIInputStream; +class nsILoadContextInfo; +class nsIURI; +class nsCString; + +class nsAboutCacheEntry final : public nsIAboutModule +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIABOUTMODULE + +private: + virtual ~nsAboutCacheEntry() {} + + class Channel final : public nsICacheEntryOpenCallback + , public nsICacheEntryMetaDataVisitor + , public nsIStreamListener + , public nsIChannel + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICACHEENTRYOPENCALLBACK + NS_DECL_NSICACHEENTRYMETADATAVISITOR + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_FORWARD_SAFE_NSICHANNEL(mChannel) + NS_FORWARD_SAFE_NSIREQUEST(mChannel) + + Channel() + : mBuffer(nullptr) + , mWaitingForData(false) + , mHexDumpState(0) + {} + + private: + virtual ~Channel() {} + + public: + MOZ_MUST_USE nsresult Init(nsIURI* uri, nsILoadInfo* aLoadInfo); + + MOZ_MUST_USE nsresult GetContentStream(nsIURI *, nsIInputStream **); + MOZ_MUST_USE nsresult OpenCacheEntry(nsIURI *); + MOZ_MUST_USE nsresult OpenCacheEntry(); + MOZ_MUST_USE nsresult WriteCacheEntryDescription(nsICacheEntry *); + MOZ_MUST_USE nsresult WriteCacheEntryUnavailable(); + MOZ_MUST_USE nsresult ParseURI(nsIURI *uri, nsACString &storageName, + nsILoadContextInfo **loadInfo, + nsCString &enahnceID, + nsIURI **cacheUri); + void CloseContent(); + + static MOZ_MUST_USE nsresult + PrintCacheData(nsIInputStream *aInStream, + void *aClosure, + const char *aFromSegment, + uint32_t aToOffset, + uint32_t aCount, + uint32_t *aWriteCount); + + private: + nsCString mStorageName, mEnhanceId; + nsCOMPtr<nsILoadContextInfo> mLoadInfo; + nsCOMPtr<nsIURI> mCacheURI; + + nsCString *mBuffer; + nsCOMPtr<nsIAsyncOutputStream> mOutputStream; + bool mWaitingForData; + uint32_t mHexDumpState; + + nsCOMPtr<nsIChannel> mChannel; + }; +}; + +#define NS_ABOUT_CACHE_ENTRY_MODULE_CID \ +{ /* 7fa5237d-b0eb-438f-9e50-ca0166e63788 */ \ + 0x7fa5237d, \ + 0xb0eb, \ + 0x438f, \ + {0x9e, 0x50, 0xca, 0x01, 0x66, 0xe6, 0x37, 0x88} \ +} + +#endif // nsAboutCacheEntry_h__ diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.cpp b/netwerk/protocol/about/nsAboutProtocolHandler.cpp new file mode 100644 index 000000000..998fc71f9 --- /dev/null +++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp @@ -0,0 +1,439 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/basictypes.h" +#include "mozilla/ArrayUtils.h" + +#include "nsAboutProtocolHandler.h" +#include "nsIURI.h" +#include "nsIAboutModule.h" +#include "nsString.h" +#include "nsNetCID.h" +#include "nsAboutProtocolUtils.h" +#include "nsError.h" +#include "nsNetUtil.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsAutoPtr.h" +#include "nsIWritablePropertyBag2.h" +#include "nsIChannel.h" +#include "nsIScriptError.h" + +namespace mozilla { +namespace net { + +static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); +static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID); + +static bool IsSafeForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) { + uint32_t flags; + nsresult rv = aModule->GetURIFlags(aURI, &flags); + NS_ENSURE_SUCCESS(rv, false); + + return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0; +} + +static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) { + uint32_t flags; + nsresult rv = aModule->GetURIFlags(aURI, &flags); + NS_ENSURE_SUCCESS(rv, false); + + return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && (flags & nsIAboutModule::MAKE_LINKABLE); +} +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, + nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference) + +//////////////////////////////////////////////////////////////////////////////// +// nsIProtocolHandler methods: + +NS_IMETHODIMP +nsAboutProtocolHandler::GetScheme(nsACString &result) +{ + result.AssignLiteral("about"); + return NS_OK; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::GetDefaultPort(int32_t *result) +{ + *result = -1; // no port for about: URLs + return NS_OK; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result) +{ + *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_SCHEME_NOT_SELF_LINKABLE; + return NS_OK; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) +{ + // First use the default (which is "unsafe for content"): + GetProtocolFlags(aFlags); + + // Now try to see if this URI overrides the default: + nsCOMPtr<nsIAboutModule> aboutMod; + nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod)); + if (NS_FAILED(rv)) { + // Swallow this and just tell the consumer the default: + return NS_OK; + } + uint32_t aboutModuleFlags = 0; + rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags); + // This should never happen, so pass back the error: + NS_ENSURE_SUCCESS(rv, rv); + + // Secure (https) pages can load safe about pages without becoming + // mixed content. + if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) { + *aFlags |= URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + // about: pages can only be loaded by unprivileged principals + // if they are marked as LINKABLE + if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) { + // Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE. + *aFlags &= ~URI_DANGEROUS_TO_LOAD; + *aFlags |= URI_LOADABLE_BY_ANYONE; + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::NewURI(const nsACString &aSpec, + const char *aCharset, // ignore charset info + nsIURI *aBaseURI, + nsIURI **result) +{ + *result = nullptr; + nsresult rv; + + // Use a simple URI to parse out some stuff first + nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = url->SetSpec(aSpec); + if (NS_FAILED(rv)) { + return rv; + } + + // Unfortunately, people create random about: URIs that don't correspond to + // about: modules... Since those URIs will never open a channel, might as + // well consider them unsafe for better perf, and just in case. + bool isSafe = false; + + nsCOMPtr<nsIAboutModule> aboutMod; + rv = NS_GetAboutModule(url, getter_AddRefs(aboutMod)); + if (NS_SUCCEEDED(rv)) { + isSafe = IsSafeToLinkForUntrustedContent(aboutMod, url); + } + + if (isSafe) { + // We need to indicate that this baby is safe. Use an inner URI that + // no one but the security manager will see. Make sure to preserve our + // path, in case someone decides to hardcode checks for particular + // about: URIs somewhere. + nsAutoCString spec; + rv = url->GetPath(spec); + NS_ENSURE_SUCCESS(rv, rv); + + spec.Insert("moz-safe-about:", 0); + + nsCOMPtr<nsIURI> inner; + rv = NS_NewURI(getter_AddRefs(inner), spec); + NS_ENSURE_SUCCESS(rv, rv); + + nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI); + NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY); + + // Take a ref to it in the COMPtr we plan to return + url = outer; + + rv = outer->SetSpec(aSpec); + NS_ENSURE_SUCCESS(rv, rv); + } + + // We don't want to allow mutation, since it would allow safe and + // unsafe URIs to change into each other... + NS_TryToSetImmutable(url); + url.swap(*result); + return NS_OK; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::NewChannel2(nsIURI* uri, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + NS_ENSURE_ARG_POINTER(uri); + + // about:what you ask? + nsCOMPtr<nsIAboutModule> aboutMod; + nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod)); + + nsAutoCString path; + nsresult rv2 = NS_GetAboutModuleName(uri, path); + if (NS_SUCCEEDED(rv2) && path.EqualsLiteral("srcdoc")) { + // about:srcdoc is meant to be unresolvable, yet is included in the + // about lookup tables so that it can pass security checks when used in + // a srcdoc iframe. To ensure that it stays unresolvable, we pretend + // that it doesn't exist. + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (NS_SUCCEEDED(rv)) { + // The standard return case: + rv = aboutMod->NewChannel(uri, aLoadInfo, result); + if (NS_SUCCEEDED(rv)) { + // Not all implementations of nsIAboutModule::NewChannel() + // set the LoadInfo on the newly created channel yet, as + // an interim solution we set the LoadInfo here if not + // available on the channel. Bug 1087720 + nsCOMPtr<nsILoadInfo> loadInfo = (*result)->GetLoadInfo(); + if (aLoadInfo != loadInfo) { + if (loadInfo) { + NS_ASSERTION(false, + "nsIAboutModule->newChannel(aURI, aLoadInfo) needs to set LoadInfo"); + const char16_t* params[] = { + u"nsIAboutModule->newChannel(aURI)", + u"nsIAboutModule->newChannel(aURI, aLoadInfo)" + }; + nsContentUtils::ReportToConsole( + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Security by Default"), + nullptr, // aDocument + nsContentUtils::eNECKO_PROPERTIES, + "APIDeprecationWarning", + params, mozilla::ArrayLength(params)); + } + (*result)->SetLoadInfo(aLoadInfo); + } + + // If this URI is safe for untrusted content, enforce that its + // principal be based on the channel's originalURI by setting the + // owner to null. + // Note: this relies on aboutMod's newChannel implementation + // having set the proper originalURI, which probably isn't ideal. + if (IsSafeForUntrustedContent(aboutMod, uri)) { + (*result)->SetOwner(nullptr); + } + + RefPtr<nsNestedAboutURI> aboutURI; + nsresult rv2 = uri->QueryInterface(kNestedAboutURICID, + getter_AddRefs(aboutURI)); + if (NS_SUCCEEDED(rv2) && aboutURI->GetBaseURI()) { + nsCOMPtr<nsIWritablePropertyBag2> writableBag = + do_QueryInterface(*result); + if (writableBag) { + writableBag-> + SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), + aboutURI->GetBaseURI()); + } + } + } + return rv; + } + + // mumble... + + if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) { + // This looks like an about: we don't know about. Convert + // this to an invalid URI error. + rv = NS_ERROR_MALFORMED_URI; + } + + return rv; +} + +NS_IMETHODIMP +nsAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) +{ + return NewChannel2(uri, nullptr, result); +} + +NS_IMETHODIMP +nsAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// Safe about protocol handler impl + +NS_IMPL_ISUPPORTS(nsSafeAboutProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference) + +// nsIProtocolHandler methods: + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::GetScheme(nsACString &result) +{ + result.AssignLiteral("moz-safe-about"); + return NS_OK; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::GetDefaultPort(int32_t *result) +{ + *result = -1; // no port for moz-safe-about: URLs + return NS_OK; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result) +{ + *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + return NS_OK; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec, + const char *aCharset, // ignore charset info + nsIURI *aBaseURI, + nsIURI **result) +{ + nsresult rv; + + nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = url->SetSpec(aSpec); + if (NS_FAILED(rv)) { + return rv; + } + + NS_TryToSetImmutable(url); + + *result = nullptr; + url.swap(*result); + return rv; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::NewChannel2(nsIURI* uri, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + *result = nullptr; + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) +{ + *result = nullptr; + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsSafeAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +//////////////////////////////////////////////////////////// +// nsNestedAboutURI implementation +NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI) + if (aIID.Equals(kNestedAboutURICID)) + foundInterface = static_cast<nsIURI*>(this); + else +NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI) + +// nsISerializable +NS_IMETHODIMP +nsNestedAboutURI::Read(nsIObjectInputStream* aStream) +{ + nsresult rv = nsSimpleNestedURI::Read(aStream); + if (NS_FAILED(rv)) return rv; + + bool haveBase; + rv = aStream->ReadBoolean(&haveBase); + if (NS_FAILED(rv)) return rv; + + if (haveBase) { + nsCOMPtr<nsISupports> supports; + rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(rv)) return rv; + + mBaseURI = do_QueryInterface(supports, &rv); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsNestedAboutURI::Write(nsIObjectOutputStream* aStream) +{ + nsresult rv = nsSimpleNestedURI::Write(aStream); + if (NS_FAILED(rv)) return rv; + + rv = aStream->WriteBoolean(mBaseURI != nullptr); + if (NS_FAILED(rv)) return rv; + + if (mBaseURI) { + // A previous iteration of this code wrote out mBaseURI as nsISupports + // and then read it in as nsIURI, which is non-kosher when mBaseURI + // implements more than just a single line of interfaces and the + // canonical nsISupports* isn't the one a static_cast<> of mBaseURI + // would produce. For backwards compatibility with existing + // serializations we continue to write mBaseURI as nsISupports but + // switch to reading it as nsISupports, with a post-read QI to get to + // nsIURI. + rv = aStream->WriteCompoundObject(mBaseURI, NS_GET_IID(nsISupports), + true); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + +// nsSimpleURI +/* virtual */ nsSimpleURI* +nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode, + const nsACString& aNewRef) +{ + // Sadly, we can't make use of nsSimpleNestedURI::StartClone here. + // However, this function is expected to exactly match that function, + // aside from the "new ns***URI()" call. + NS_ENSURE_TRUE(mInnerURI, nullptr); + + nsCOMPtr<nsIURI> innerClone; + nsresult rv; + if (aRefHandlingMode == eHonorRef) { + rv = mInnerURI->Clone(getter_AddRefs(innerClone)); + } else if (aRefHandlingMode == eReplaceRef) { + rv = mInnerURI->CloneWithNewRef(aNewRef, getter_AddRefs(innerClone)); + } else { + rv = mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone)); + } + + if (NS_FAILED(rv)) { + return nullptr; + } + + nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI); + SetRefOnClone(url, aRefHandlingMode, aNewRef); + url->SetMutable(false); + + return url; +} + +// nsIClassInfo +NS_IMETHODIMP +nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) +{ + *aClassIDNoAlloc = kNestedAboutURICID; + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.h b/netwerk/protocol/about/nsAboutProtocolHandler.h new file mode 100644 index 000000000..72c7e8e66 --- /dev/null +++ b/netwerk/protocol/about/nsAboutProtocolHandler.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAboutProtocolHandler_h___ +#define nsAboutProtocolHandler_h___ + +#include "nsIProtocolHandler.h" +#include "nsSimpleNestedURI.h" +#include "nsWeakReference.h" +#include "mozilla/Attributes.h" + +class nsIURI; + +namespace mozilla { +namespace net { + +class nsAboutProtocolHandler : public nsIProtocolHandlerWithDynamicFlags + , public nsIProtocolHandler + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + + // nsIProtocolHandler methods: + NS_DECL_NSIPROTOCOLHANDLER + NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS + + // nsAboutProtocolHandler methods: + nsAboutProtocolHandler() {} + +private: + virtual ~nsAboutProtocolHandler() {} +}; + +class nsSafeAboutProtocolHandler final : public nsIProtocolHandler + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + + // nsIProtocolHandler methods: + NS_DECL_NSIPROTOCOLHANDLER + + // nsSafeAboutProtocolHandler methods: + nsSafeAboutProtocolHandler() {} + +private: + ~nsSafeAboutProtocolHandler() {} +}; + + +// Class to allow us to propagate the base URI to about:blank correctly +class nsNestedAboutURI : public nsSimpleNestedURI { +public: + nsNestedAboutURI(nsIURI* aInnerURI, nsIURI* aBaseURI) + : nsSimpleNestedURI(aInnerURI) + , mBaseURI(aBaseURI) + {} + + // For use only from deserialization + nsNestedAboutURI() : nsSimpleNestedURI() {} + + virtual ~nsNestedAboutURI() {} + + // Override QI so we can QI to our CID as needed + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr); + + // Override StartClone(), the nsISerializable methods, and + // GetClassIDNoAlloc; this last is needed to make our nsISerializable impl + // work right. + virtual nsSimpleURI* StartClone(RefHandlingEnum aRefHandlingMode, + const nsACString& newRef); + NS_IMETHOD Read(nsIObjectInputStream* aStream); + NS_IMETHOD Write(nsIObjectOutputStream* aStream); + NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc); + + nsIURI* GetBaseURI() const { + return mBaseURI; + } + +protected: + nsCOMPtr<nsIURI> mBaseURI; +}; + +} // namespace net +} // namespace mozilla + +#endif /* nsAboutProtocolHandler_h___ */ diff --git a/netwerk/protocol/about/nsAboutProtocolUtils.h b/netwerk/protocol/about/nsAboutProtocolUtils.h new file mode 100644 index 000000000..c5946412b --- /dev/null +++ b/netwerk/protocol/about/nsAboutProtocolUtils.h @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAboutProtocolUtils_h +#define nsAboutProtocolUtils_h + +#include "nsIURI.h" +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsIAboutModule.h" +#include "nsServiceManagerUtils.h" +#include "prtime.h" + +inline MOZ_MUST_USE nsresult +NS_GetAboutModuleName(nsIURI *aAboutURI, nsCString& aModule) +{ +#ifdef DEBUG + { + bool isAbout; + NS_ASSERTION(NS_SUCCEEDED(aAboutURI->SchemeIs("about", &isAbout)) && + isAbout, + "should be used only on about: URIs"); + } +#endif + + nsresult rv = aAboutURI->GetPath(aModule); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t f = aModule.FindCharInSet(NS_LITERAL_CSTRING("#?")); + if (f != kNotFound) { + aModule.Truncate(f); + } + + // convert to lowercase, as all about: modules are lowercase + ToLowerCase(aModule); + return NS_OK; +} + +inline nsresult +NS_GetAboutModule(nsIURI *aAboutURI, nsIAboutModule** aModule) +{ + NS_PRECONDITION(aAboutURI, "Must have URI"); + + nsAutoCString contractID; + nsresult rv = NS_GetAboutModuleName(aAboutURI, contractID); + if (NS_FAILED(rv)) return rv; + + // look up a handler to deal with "what" + contractID.Insert(NS_LITERAL_CSTRING(NS_ABOUT_MODULE_CONTRACTID_PREFIX), 0); + + return CallGetService(contractID.get(), aModule); +} + +inline PRTime SecondsToPRTime(uint32_t t_sec) +{ + PRTime t_usec, usec_per_sec; + t_usec = t_sec; + usec_per_sec = PR_USEC_PER_SEC; + return t_usec *= usec_per_sec; +} +inline void PrintTimeString(char *buf, uint32_t bufsize, uint32_t t_sec) +{ + PRExplodedTime et; + PRTime t_usec = SecondsToPRTime(t_sec); + PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et); + PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et); +} + + +#endif diff --git a/netwerk/protocol/about/nsIAboutModule.idl b/netwerk/protocol/about/nsIAboutModule.idl new file mode 100644 index 000000000..230cd6c05 --- /dev/null +++ b/netwerk/protocol/about/nsIAboutModule.idl @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIURI; +interface nsIChannel; +interface nsILoadInfo; + +[scriptable, uuid(c0c19db9-1b5a-4ac5-b656-ed6f8149fa48)] +interface nsIAboutModule : nsISupports +{ + + /** + * Constructs a new channel for the about protocol module. + * + * @param aURI the uri of the new channel + * @param aLoadInfo the loadinfo of the new channel + */ + nsIChannel newChannel(in nsIURI aURI, + in nsILoadInfo aLoadInfo); + + /** + * A flag that indicates whether a URI should be run with content + * privileges. If it is, the about: protocol handler will enforce that + * the principal of channels created for it be based on their + * originalURI or URI (depending on the channel flags), by setting + * their "owner" to null. + * If content needs to be able to link to this URI, specify + * URI_CONTENT_LINKABLE as well. + */ + const unsigned long URI_SAFE_FOR_UNTRUSTED_CONTENT = (1 << 0); + + /** + * A flag that indicates whether script should be enabled for the + * given about: URI even if it's disabled in general. + */ + const unsigned long ALLOW_SCRIPT = (1 << 1); + + /** + * A flag that indicates whether this about: URI doesn't want to be listed + * in about:about, especially if it's not useful without a query string. + */ + const unsigned long HIDE_FROM_ABOUTABOUT = (1 << 2); + + /** + * A flag that indicates whether this about: URI wants Indexed DB enabled. + */ + const unsigned long ENABLE_INDEXED_DB = (1 << 3); + + /** + * A flag that indicates that this URI can be loaded in a child process + */ + const unsigned long URI_CAN_LOAD_IN_CHILD = (1 << 4); + + /** + * A flag that indicates that this URI must be loaded in a child process + */ + const unsigned long URI_MUST_LOAD_IN_CHILD = (1 << 5); + + /** + * Obsolete. This flag no longer has any effect and will be removed in future. + */ + const unsigned long MAKE_UNLINKABLE = (1 << 6); + + /** + * A flag that indicates that this URI should be linkable from content. + * Ignored unless URI_SAFE_FOR_UNTRUSTED_CONTENT is also specified. + */ + const unsigned long MAKE_LINKABLE = (1 << 7); + + /** + * A method to get the flags that apply to a given about: URI. The URI + * passed in is guaranteed to be one of the URIs that this module + * registered to deal with. + */ + unsigned long getURIFlags(in nsIURI aURI); +}; + +%{C++ + +#define NS_ABOUT_MODULE_CONTRACTID "@mozilla.org/network/protocol/about;1" +#define NS_ABOUT_MODULE_CONTRACTID_PREFIX NS_ABOUT_MODULE_CONTRACTID "?what=" +#define NS_ABOUT_MODULE_CONTRACTID_LENGTH 49 // strlen(NS_ABOUT_MODULE_CONTRACTID_PREFIX) + +%} |