summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/about
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /netwerk/protocol/about
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--netwerk/protocol/about/moz.build32
-rw-r--r--netwerk/protocol/about/nsAboutBlank.cpp52
-rw-r--r--netwerk/protocol/about/nsAboutBlank.h32
-rw-r--r--netwerk/protocol/about/nsAboutCache.cpp533
-rw-r--r--netwerk/protocol/about/nsAboutCache.h199
-rw-r--r--netwerk/protocol/about/nsAboutCacheEntry.cpp559
-rw-r--r--netwerk/protocol/about/nsAboutCacheEntry.h86
-rw-r--r--netwerk/protocol/about/nsAboutProtocolHandler.cpp414
-rw-r--r--netwerk/protocol/about/nsAboutProtocolHandler.h139
-rw-r--r--netwerk/protocol/about/nsAboutProtocolUtils.h64
-rw-r--r--netwerk/protocol/about/nsIAboutModule.idl119
11 files changed, 2229 insertions, 0 deletions
diff --git a/netwerk/protocol/about/moz.build b/netwerk/protocol/about/moz.build
new file mode 100644
index 0000000000..f130d9b07b
--- /dev/null
+++ b/netwerk/protocol/about/moz.build
@@ -0,0 +1,32 @@
+# -*- 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 += [
+ "nsAboutProtocolHandler.h",
+ "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",
+]
diff --git a/netwerk/protocol/about/nsAboutBlank.cpp b/netwerk/protocol/about/nsAboutBlank.cpp
new file mode 100644
index 0000000000..88db0d2de3
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutBlank.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+#include "nsIChannel.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), ""_ns);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
+ in.forget(), "text/html"_ns, "utf-8"_ns,
+ 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;
+}
+
+NS_IMETHODIMP
+nsAboutBlank::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+nsresult nsAboutBlank::Create(REFNSIID aIID, void** aResult) {
+ RefPtr<nsAboutBlank> about = new nsAboutBlank();
+ return about->QueryInterface(aIID, aResult);
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/netwerk/protocol/about/nsAboutBlank.h b/netwerk/protocol/about/nsAboutBlank.h
new file mode 100644
index 0000000000..6cb43507f5
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutBlank.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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() = default;
+
+ static nsresult Create(REFNSIID aIID, void** aResult);
+
+ private:
+ virtual ~nsAboutBlank() = default;
+};
+
+#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 0000000000..1873277a7a
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutCache.cpp
@@ -0,0 +1,533 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "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;
+ NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(mStream), 16384,
+ (uint32_t)-1,
+ true, // non-blocking input
+ false // blocking output
+ );
+
+ nsAutoCString storageName;
+ rv = ParseURI(aURI, storageName);
+ if (NS_FAILED(rv)) return rv;
+
+ mOverview = storageName.IsEmpty();
+ if (mOverview) {
+ // ...and visit all we can
+ mStorageList.AppendElement("memory"_ns);
+ mStorageList.AppendElement("disk"_ns);
+ } 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.forget(), "text/html"_ns,
+ "utf-8"_ns, 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"
+ " <meta name=\"color-scheme\" content=\"light dark\">\n"
+ " <meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
+ "chrome:; object-src 'none'\"/>\n"
+ " <link rel=\"stylesheet\" "
+ "href=\"chrome://global/skin/in-content/info-pages.css\"/>\n"
+ " <link rel=\"stylesheet\" "
+ "href=\"chrome://global/skin/aboutCache.css\"/>\n"
+ "</head>\n"
+ "<body class=\"aboutPageWideContainer\">\n"
+ "<h1>Information about the Network Cache Storage Service</h1>\n");
+
+ if (!mOverview) {
+ mBuffer.AppendLiteral(
+ "<a href=\"about:cache?storage=\">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) {
+ nsresult rv;
+
+ if (!mChannel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Kick the walk loop.
+ rv = VisitNextStorage();
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mChannel->AsyncOpen(aListener);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAboutCache::Channel::Open(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->GetPathQueryRef(path);
+ if (NS_FAILED(rv)) return rv;
+
+ storage.Truncate();
+
+ nsACString::const_iterator start, valueStart, end;
+ path.BeginReading(start);
+ path.EndReading(end);
+
+ valueStart = end;
+ if (!FindInReadable("?storage="_ns, start, valueStart)) {
+ return NS_OK;
+ }
+
+ storage.Assign(Substring(valueStart, end));
+
+ 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(
+ "nsAboutCache::Channel::FireVisitStorage", this,
+ &nsAboutCache::Channel::FireVisitStorage));
+}
+
+void nsAboutCache::Channel::FireVisitStorage() {
+ nsresult rv;
+
+ rv = VisitStorage(mStorageName);
+ if (NS_FAILED(rv)) {
+ nsAutoCString escaped;
+ nsAppendEscapedHTML(mStorageName, escaped);
+ mBuffer.Append(nsPrintfCString(
+ "<p>Unrecognized storage name '%s' in about:cache URL</p>",
+ escaped.get()));
+
+ 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, nullptr, 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, getter_AddRefs(cacheStorage));
+ } else if (storageName == "memory") {
+ rv = cacheService->MemoryCacheStorage(loadInfo,
+ 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>");
+ nsAppendEscapedHTML(mStorageName, mBuffer);
+ 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"
+ " <td colspan=\"2\"><a href=\"about:cache?storage=");
+ nsAppendEscapedHTML(mStorageName, mBuffer);
+ mBuffer.AppendLiteral(
+ "\">List Cache Entries</a></td>\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,
+ int64_t aAltDataSize, uint32_t aFetchCount, uint32_t aLastModified,
+ uint32_t aExpirationTime, bool aPinned, nsILoadContextInfo* aInfo) {
+ // 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-altDataSize\">\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>Alternative 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=");
+ nsAppendEscapedHTML(mStorageName, url);
+
+ nsAutoCString context;
+ CacheFileUtils::AppendKeyPrefix(aInfo, context);
+ url.AppendLiteral("&amp;context=");
+ nsAppendEscapedHTML(context, url);
+
+ url.AppendLiteral("&amp;eid=");
+ nsAppendEscapedHTML(aIdEnhance, url);
+
+ nsAutoCString cacheUriSpec;
+ aURI->GetAsciiSpec(cacheUriSpec);
+ nsAutoCString escapedCacheURI;
+ nsAppendEscapedHTML(cacheUriSpec, escapedCacheURI);
+ url.AppendLiteral("&amp;uri=");
+ url += escapedCacheURI;
+
+ // Entry start...
+ mBuffer.AppendLiteral(" <tr>\n");
+
+ // URI
+ mBuffer.AppendLiteral(" <td><a href=\"");
+ mBuffer.Append(url);
+ mBuffer.AppendLiteral("\">");
+ if (!aIdEnhance.IsEmpty()) {
+ nsAppendEscapedHTML(aIdEnhance, mBuffer);
+ mBuffer.Append(':');
+ }
+ mBuffer.Append(escapedCacheURI);
+ mBuffer.AppendLiteral("</a>");
+
+ if (!context.IsEmpty()) {
+ mBuffer.AppendLiteral("<br><span title=\"Context separation key\">");
+ nsAutoCString escapedContext;
+ nsAppendEscapedHTML(context, escapedContext);
+ mBuffer.Append(escapedContext);
+ mBuffer.AppendLiteral("</span>");
+ }
+
+ mBuffer.AppendLiteral("</td>\n");
+
+ // Content length
+ mBuffer.AppendLiteral(" <td>");
+ mBuffer.AppendInt(aDataSize);
+ mBuffer.AppendLiteral(" bytes</td>\n");
+
+ // Length of alternative content
+ mBuffer.AppendLiteral(" <td>");
+ mBuffer.AppendInt(aAltDataSize);
+ 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>");
+
+ // Bug - 633747.
+ // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing.
+ // So we check if time is 0, then we show a message, "Expired Immediately"
+ if (aExpirationTime == 0) {
+ mBuffer.AppendLiteral("Expired Immediately");
+ } else 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.AppendLiteral("Pinned");
+ } else {
+ mBuffer.AppendLiteral("&nbsp;");
+ }
+ 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 |
+ nsIAboutModule::IS_SECURE_CHROME_UI;
+ return NS_OK;
+}
+
+// static
+nsresult nsAboutCache::Create(REFNSIID aIID, void** aResult) {
+ RefPtr<nsAboutCache> about = new nsAboutCache();
+ return about->QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsAboutCache::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/netwerk/protocol/about/nsAboutCache.h b/netwerk/protocol/about/nsAboutCache.h
new file mode 100644
index 0000000000..65292b9aae
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutCache.h
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsIChannel.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(nsITransportSecurityInfo** 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); \
+ } \
+ NS_IMETHOD GetIsDocument(bool* aIsDocument) override { \
+ return !(_to) ? NS_ERROR_NULL_POINTER : (_to)->GetIsDocument(aIsDocument); \
+ } \
+ NS_IMETHOD GetCanceled(bool* aCanceled) override { \
+ return !(_to) ? NS_ERROR_NULL_POINTER : (_to)->GetCanceled(aCanceled); \
+ };
+
+class nsAboutCache final : public nsIAboutModule {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABOUTMODULE
+
+ nsAboutCache() = default;
+
+ [[nodiscard]] static nsresult Create(REFNSIID aIID, void** aResult);
+
+ [[nodiscard]] static nsresult GetStorage(nsACString const& storageName,
+ nsILoadContextInfo* loadInfo,
+ nsICacheStorage** storage);
+
+ protected:
+ virtual ~nsAboutCache() = default;
+
+ 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) override;
+ NS_IMETHOD Open(nsIInputStream** _retval) override;
+
+ private:
+ virtual ~Channel() = default;
+
+ public:
+ [[nodiscard]] nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo);
+ [[nodiscard]] 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.
+ [[nodiscard]] 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.
+ [[nodiscard]] 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.
+ [[nodiscard]] nsresult FlushBuffer();
+
+ // Whether we are showing overview status of all available
+ // storages.
+ bool mOverview = false;
+
+ // Flag initially false, that indicates the entries header has
+ // been added to the output HTML.
+ bool mEntriesHeaderAdded = false;
+
+ // Cancelation flag
+ bool mCancel = false;
+
+ // 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 0000000000..08dfcf6ca0
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <algorithm>
+
+#include "nsAboutCacheEntry.h"
+
+#include "CacheFileUtils.h"
+#include "CacheObserver.h"
+#include "mozilla/Sprintf.h"
+#include "nsAboutCache.h"
+#include "nsAboutProtocolUtils.h"
+#include "nsContentUtils.h"
+#include "nsEscape.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsICacheStorage.h"
+#include "nsIPipe.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsInputStreamPump.h"
+#include "nsNetUtil.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("&lt;");
+ break;
+ case '>':
+ result.AppendLiteral("&gt;");
+ break;
+ case '&':
+ result.AppendLiteral("&amp;");
+ 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, nsIRequest,
+ 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;
+}
+
+NS_IMETHODIMP
+nsAboutCacheEntry::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+//-----------------------------------------------------------------------------
+// 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.forget(), "text/html"_ns,
+ "utf-8"_ns, 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;
+ NS_NewPipe2(getter_AddRefs(inputStream), getter_AddRefs(mOutputStream), true,
+ false, 256, UINT32_MAX);
+
+ constexpr auto buffer =
+ "<!DOCTYPE html>\n"
+ "<html>\n"
+ "<head>\n"
+ " <meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
+ "chrome:; object-src 'none'\" />\n"
+ " <meta name=\"color-scheme\" content=\"light dark\" />\n"
+ " <title>Cache entry information</title>\n"
+ " <link rel=\"stylesheet\" "
+ "href=\"chrome://global/skin/in-content/info-pages.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"_ns;
+ 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;
+
+ 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->GetPathQueryRef(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("?storage="_ns, keyBegin, keyEnd)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ valBegin = keyEnd; // the value of the storage key starts after the key
+
+ keyBegin = keyEnd;
+ keyEnd = end;
+ if (!FindInReadable("&context="_ns, 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("&eid="_ns, 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("&uri="_ns, 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,
+ uint32_t* result) {
+ *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry* entry,
+ bool isNew, nsresult status) {
+ nsresult rv;
+
+ mWaitingForData = false;
+ if (entry) {
+ rv = WriteCacheEntryDescription(entry);
+ } 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;
+ // This method appears to run in a situation where the run-time stack
+ // should have plenty of space, so allocating a large string on the
+ // stack is OK.
+ nsAutoCStringN<4097> buffer;
+ uint32_t n;
+
+ nsAutoCString str;
+
+ rv = entry->GetKey(str);
+ if (NS_FAILED(rv)) return rv;
+
+ 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;
+ rv = NS_NewURI(getter_AddRefs(uri), str);
+
+ nsAutoCString escapedStr;
+ nsAppendEscapedHTML(str, escapedStr);
+
+ // 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") || uri->SchemeIs("data"))) {
+ buffer.AppendLiteral("<a href=\"");
+ buffer.Append(escapedStr);
+ buffer.AppendLiteral("\">");
+ buffer.Append(escapedStr);
+ buffer.AppendLiteral("</a>");
+ uri = nullptr;
+ } else {
+ buffer.Append(escapedStr);
+ }
+ buffer.AppendLiteral(
+ "</td>\n"
+ " </tr>\n");
+
+ // temp vars for reporting
+ char timeBuf[255];
+ uint32_t u = 0;
+ nsAutoCString s;
+
+ // Fetch Count
+ s.Truncate();
+ entry->GetFetchCount(&u);
+ s.AppendInt(u);
+ 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);
+
+ // Bug - 633747.
+ // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing.
+ // So we check if time is 0, then we show a message, "Expired Immediately"
+ if (u == 0) {
+ APPEND_ROW("expires", "Expired Immediately");
+ } else 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<nsITransportSecurityInfo> 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);
+ if (NS_FAILED(rv)) {
+ return NS_OK; // just ignore
+ }
+
+ mWaitingForData = true;
+ return NS_OK;
+}
+
+nsresult nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable() {
+ uint32_t n;
+ constexpr auto buffer = "The cache entry you selected is not available."_ns;
+ 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>");
+ nsAppendEscapedHTML(nsDependentCString(value), *mBuffer);
+ mBuffer->AppendLiteral(
+ "</td>\n"
+ " </tr>\n");
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIStreamListener implementation
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest* request) {
+ mHexDumpState = 0;
+
+ constexpr auto buffer = "<hr/>\n<pre>"_ns;
+ uint32_t n;
+ return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
+}
+
+NS_IMETHODIMP
+nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest* request,
+ 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,
+ nsresult result) {
+ constexpr auto buffer = "</pre>\n"_ns;
+ uint32_t n;
+ mOutputStream->Write(buffer.get(), buffer.Length(), &n);
+
+ CloseContent();
+
+ return NS_OK;
+}
+
+void nsAboutCacheEntry::Channel::CloseContent() {
+ constexpr auto buffer = "</body>\n</html>\n"_ns;
+ 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 0000000000..76f8649061
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutCacheEntry.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+#include "nsIChannel.h"
+
+class nsIAsyncOutputStream;
+class nsIInputStream;
+class nsILoadContextInfo;
+class nsIURI;
+
+class nsAboutCacheEntry final : public nsIAboutModule {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIABOUTMODULE
+
+ private:
+ virtual ~nsAboutCacheEntry() = default;
+
+ 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() = default;
+
+ private:
+ virtual ~Channel() = default;
+
+ public:
+ [[nodiscard]] nsresult Init(nsIURI* uri, nsILoadInfo* aLoadInfo);
+
+ [[nodiscard]] nsresult GetContentStream(nsIURI*, nsIInputStream**);
+ [[nodiscard]] nsresult OpenCacheEntry(nsIURI*);
+ [[nodiscard]] nsresult OpenCacheEntry();
+ [[nodiscard]] nsresult WriteCacheEntryDescription(nsICacheEntry*);
+ [[nodiscard]] nsresult WriteCacheEntryUnavailable();
+ [[nodiscard]] nsresult ParseURI(nsIURI* uri, nsACString& storageName,
+ nsILoadContextInfo** loadInfo,
+ nsCString& enahnceID, nsIURI** cacheUri);
+ void CloseContent();
+
+ [[nodiscard]] static 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{nullptr};
+ nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
+ bool mWaitingForData{false};
+ uint32_t mHexDumpState{0};
+
+ 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 0000000000..fe69c7ec02
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsContentUtils.h"
+#include "nsString.h"
+#include "nsNetCID.h"
+#include "nsAboutProtocolUtils.h"
+#include "nsError.h"
+#include "nsNetUtil.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsIChannel.h"
+#include "nsIScriptError.h"
+#include "nsIClassInfoImpl.h"
+
+#include "mozilla/ipc/URIUtils.h"
+
+namespace mozilla {
+namespace net {
+
+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(nsIURI* aURI) {
+ nsAutoCString path;
+ aURI->GetPathQueryRef(path);
+
+ int32_t f = path.FindChar('#');
+ if (f >= 0) {
+ path.SetLength(f);
+ }
+
+ f = path.FindChar('?');
+ if (f >= 0) {
+ path.SetLength(f);
+ }
+
+ ToLowerCase(path);
+
+ // The about modules for these URL types have the
+ // URI_SAFE_FOR_UNTRUSTED_CONTENT and MAKE_LINKABLE flags set.
+ return path.EqualsLiteral("blank") || path.EqualsLiteral("logo") ||
+ path.EqualsLiteral("srcdoc");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler,
+ nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIProtocolHandler methods:
+
+NS_IMETHODIMP
+nsAboutProtocolHandler::GetScheme(nsACString& result) {
+ result.AssignLiteral("about");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) {
+ // First use the default (which is "unsafe for content"):
+ *aFlags = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
+ URI_SCHEME_NOT_SELF_LINKABLE;
+
+ // 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_IS_POTENTIALLY_TRUSTWORTHY;
+ // 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;
+}
+
+// static
+nsresult nsAboutProtocolHandler::CreateNewURI(const nsACString& aSpec,
+ const char* aCharset,
+ nsIURI* aBaseURI,
+ nsIURI** aResult) {
+ *aResult = nullptr;
+ nsresult rv;
+
+ // Use a simple URI to parse out some stuff first
+ nsCOMPtr<nsIURI> url;
+ rv = NS_MutateURI(new nsSimpleURI::Mutator()).SetSpec(aSpec).Finalize(url);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (IsSafeToLinkForUntrustedContent(url)) {
+ // 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->GetPathQueryRef(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ spec.InsertLiteral("moz-safe-about:", 0);
+
+ nsCOMPtr<nsIURI> inner;
+ rv = NS_NewURI(getter_AddRefs(inner), spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_MutateURI(new nsNestedAboutURI::Mutator())
+ .Apply(&nsINestedAboutURIMutator::InitWithBase, inner, aBaseURI)
+ .SetSpec(aSpec)
+ .Finalize(url);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ url.swap(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAboutProtocolHandler::NewChannel(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)->LoadInfo();
+ if (aLoadInfo != loadInfo) {
+ NS_ASSERTION(false,
+ "nsIAboutModule->newChannel(aURI, aLoadInfo) needs to "
+ "set LoadInfo");
+ AutoTArray<nsString, 2> params = {
+ u"nsIAboutModule->newChannel(aURI)"_ns,
+ u"nsIAboutModule->newChannel(aURI, aLoadInfo)"_ns};
+ nsContentUtils::ReportToConsole(
+ nsIScriptError::warningFlag, "Security by Default"_ns,
+ nullptr, // aDocument
+ nsContentUtils::eNECKO_PROPERTIES, "APIDeprecationWarning", 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(u"baseURI"_ns,
+ 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::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::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
+ 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_IMPL_CLASSINFO(nsNestedAboutURI, nullptr, nsIClassInfo::THREADSAFE,
+ NS_NESTEDABOUTURI_CID);
+// Empty CI getter. We only need nsIClassInfo for Serialization
+NS_IMPL_CI_INTERFACE_GETTER0(nsNestedAboutURI)
+
+NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI)
+ if (aIID.Equals(kNestedAboutURICID)) {
+ foundInterface = static_cast<nsIURI*>(this);
+ } else
+ NS_IMPL_QUERY_CLASSINFO(nsNestedAboutURI)
+NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI)
+
+// nsISerializable
+
+NS_IMETHODIMP
+nsNestedAboutURI::Read(nsIObjectInputStream* aStream) {
+ MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult nsNestedAboutURI::ReadPrivate(nsIObjectInputStream* aStream) {
+ nsresult rv = nsSimpleNestedURI::ReadPrivate(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;
+}
+
+NS_IMETHODIMP_(void)
+nsNestedAboutURI::Serialize(mozilla::ipc::URIParams& aParams) {
+ using namespace mozilla::ipc;
+
+ NestedAboutURIParams params;
+ URIParams nestedParams;
+
+ nsSimpleNestedURI::Serialize(nestedParams);
+ params.nestedParams() = nestedParams;
+
+ if (mBaseURI) {
+ SerializeURI(mBaseURI, params.baseURI());
+ }
+
+ aParams = params;
+}
+
+bool nsNestedAboutURI::Deserialize(const mozilla::ipc::URIParams& aParams) {
+ using namespace mozilla::ipc;
+
+ if (aParams.type() != URIParams::TNestedAboutURIParams) {
+ NS_ERROR("Received unknown parameters from the other process!");
+ return false;
+ }
+
+ const NestedAboutURIParams& params = aParams.get_NestedAboutURIParams();
+ if (!nsSimpleNestedURI::Deserialize(params.nestedParams())) {
+ return false;
+ }
+
+ mBaseURI = nullptr;
+ if (params.baseURI()) {
+ mBaseURI = DeserializeURI(*params.baseURI());
+ }
+ return true;
+}
+
+// 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 = NS_OK;
+ if (aRefHandlingMode == eHonorRef) {
+ innerClone = mInnerURI;
+ } else if (aRefHandlingMode == eReplaceRef) {
+ rv = NS_GetURIWithNewRef(mInnerURI, aNewRef, getter_AddRefs(innerClone));
+ } else {
+ rv = NS_GetURIWithoutRef(mInnerURI, getter_AddRefs(innerClone));
+ }
+
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI);
+ SetRefOnClone(url, aRefHandlingMode, aNewRef);
+
+ return url;
+}
+
+// Queries this list of interfaces. If none match, it queries mURI.
+NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsNestedAboutURI::Mutator, nsIURISetters,
+ nsIURIMutator, nsISerializable,
+ nsINestedAboutURIMutator)
+
+NS_IMETHODIMP
+nsNestedAboutURI::Mutate(nsIURIMutator** aMutator) {
+ RefPtr<nsNestedAboutURI::Mutator> mutator = new nsNestedAboutURI::Mutator();
+ nsresult rv = mutator->InitFromURI(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ mutator.forget(aMutator);
+ 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 0000000000..1a15400361
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+#include "nsIURIMutator.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() = default;
+
+ static nsresult CreateNewURI(const nsACString& aSpec, const char* aCharset,
+ nsIURI* aBaseURI, nsIURI** result);
+
+ private:
+ virtual ~nsAboutProtocolHandler() = default;
+};
+
+class nsSafeAboutProtocolHandler final : public nsIProtocolHandler,
+ public nsSupportsWeakReference {
+ public:
+ NS_DECL_ISUPPORTS
+
+ // nsIProtocolHandler methods:
+ NS_DECL_NSIPROTOCOLHANDLER
+
+ // nsSafeAboutProtocolHandler methods:
+ nsSafeAboutProtocolHandler() = default;
+
+ private:
+ ~nsSafeAboutProtocolHandler() = default;
+};
+
+// Class to allow us to propagate the base URI to about:blank correctly
+class nsNestedAboutURI final : public nsSimpleNestedURI {
+ private:
+ nsNestedAboutURI(nsIURI* aInnerURI, nsIURI* aBaseURI)
+ : nsSimpleNestedURI(aInnerURI), mBaseURI(aBaseURI) {}
+ nsNestedAboutURI() : nsSimpleNestedURI() {}
+ virtual ~nsNestedAboutURI() = default;
+
+ public:
+ // Override QI so we can QI to our CID as needed
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+
+ // Override StartClone(), the nsISerializable methods, and
+ virtual nsSimpleURI* StartClone(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef) override;
+ NS_IMETHOD Mutate(nsIURIMutator** _retval) override;
+ NS_IMETHOD_(void) Serialize(ipc::URIParams& aParams) override;
+
+ // nsISerializable
+ NS_IMETHOD Read(nsIObjectInputStream* aStream) override;
+ NS_IMETHOD Write(nsIObjectOutputStream* aStream) override;
+
+ nsIURI* GetBaseURI() const { return mBaseURI; }
+
+ protected:
+ nsCOMPtr<nsIURI> mBaseURI;
+ bool Deserialize(const mozilla::ipc::URIParams&);
+ nsresult ReadPrivate(nsIObjectInputStream* stream);
+
+ public:
+ class Mutator final : public nsIURIMutator,
+ public BaseURIMutator<nsNestedAboutURI>,
+ public nsISerializable,
+ public nsINestedAboutURIMutator {
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
+
+ explicit Mutator() = default;
+
+ private:
+ virtual ~Mutator() = default;
+
+ [[nodiscard]] NS_IMETHOD Deserialize(
+ const mozilla::ipc::URIParams& aParams) override {
+ return InitFromIPCParams(aParams);
+ }
+
+ NS_IMETHOD
+ Write(nsIObjectOutputStream* aOutputStream) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ [[nodiscard]] NS_IMETHOD Read(nsIObjectInputStream* aStream) override {
+ return InitFromInputStream(aStream);
+ }
+
+ [[nodiscard]] NS_IMETHOD Finalize(nsIURI** aURI) override {
+ mURI.forget(aURI);
+ return NS_OK;
+ }
+
+ [[nodiscard]] NS_IMETHOD SetSpec(const nsACString& aSpec,
+ nsIURIMutator** aMutator) override {
+ if (aMutator) {
+ NS_ADDREF(*aMutator = this);
+ }
+ return InitFromSpec(aSpec);
+ }
+
+ [[nodiscard]] NS_IMETHOD InitWithBase(nsIURI* aInnerURI,
+ nsIURI* aBaseURI) override {
+ mURI = new nsNestedAboutURI(aInnerURI, aBaseURI);
+ return NS_OK;
+ }
+
+ friend class nsNestedAboutURI;
+ };
+
+ friend BaseURIMutator<nsNestedAboutURI>;
+};
+
+} // 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 0000000000..68d1f4d54b
--- /dev/null
+++ b/netwerk/protocol/about/nsAboutProtocolUtils.h
@@ -0,0 +1,64 @@
+/* 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"
+
+[[nodiscard]] inline nsresult NS_GetAboutModuleName(nsIURI* aAboutURI,
+ nsCString& aModule) {
+ NS_ASSERTION(aAboutURI->SchemeIs("about"),
+ "should be used only on about: URIs");
+
+ MOZ_TRY(aAboutURI->GetPathQueryRef(aModule));
+
+ int32_t f = aModule.FindCharInSet("#?"_ns);
+ if (f != kNotFound) {
+ aModule.Truncate(f);
+ }
+
+ // convert to lowercase, as all about: modules are lowercase
+ ToLowerCase(aModule);
+ return NS_OK;
+}
+
+[[nodiscard]] inline bool NS_IsContentAccessibleAboutURI(nsIURI* aURI) {
+ MOZ_ASSERT(aURI->SchemeIs("about"), "Should be used only on about: URIs");
+ nsAutoCString name;
+ if (NS_WARN_IF(NS_FAILED(NS_GetAboutModuleName(aURI, name)))) {
+ return true;
+ }
+ return name.EqualsLiteral("blank") || name.EqualsLiteral("srcdoc");
+}
+
+inline nsresult NS_GetAboutModule(nsIURI* aAboutURI, nsIAboutModule** aModule) {
+ MOZ_ASSERT(aAboutURI, "Must have URI");
+
+ nsAutoCString contractID;
+ MOZ_TRY(NS_GetAboutModuleName(aAboutURI, contractID));
+
+ // look up a handler to deal with "what"
+ contractID.InsertLiteral(NS_ABOUT_MODULE_CONTRACTID_PREFIX, 0);
+
+ return CallGetService(contractID.get(), aModule);
+}
+
+inline PRTime SecondsToPRTime(uint32_t t_sec) {
+ return (PRTime)t_sec * PR_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 0000000000..db82dad11d
--- /dev/null
+++ b/netwerk/protocol/about/nsIAboutModule.idl
@@ -0,0 +1,119 @@
+/* -*- 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.
+ *
+ * When adding a new about module with this flag make sure to also update
+ * IsSafeToLinkForUntrustedContent() in nsAboutProtocolHandler.cpp
+ */
+ const unsigned long MAKE_LINKABLE = (1 << 7);
+
+ /**
+ * A flag that indicates that this URI can be loaded in the privileged
+ * activity stream content process if said process is enabled. Ignored unless
+ * URI_MUST_LOAD_IN_CHILD is also specified.
+ */
+ const unsigned long URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS = (1 << 8);
+
+ /**
+ * A flag that indicates that this URI must be loaded in an extension process (if available).
+ */
+ const unsigned long URI_MUST_LOAD_IN_EXTENSION_PROCESS = (1 << 9);
+
+ /**
+ * A flag that indicates that this about: URI needs to allow unsanitized content.
+ * Only to be used by about:home and about:newtab.
+ */
+ const unsigned long ALLOW_UNSANITIZED_CONTENT = (1 << 10);
+
+ /**
+ * A flag that indicates that this about: URI is a secure chrome UI
+ */
+ const unsigned long IS_SECURE_CHROME_UI = (1 << 11);
+
+ /**
+ * 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);
+
+ /**
+ * A method to get the chrome URI that corresponds to a given about URI.
+ */
+ nsIURI getChromeURI(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)
+
+%}