summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/PageIconProtocolHandler.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/places/PageIconProtocolHandler.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/places/PageIconProtocolHandler.cpp')
-rw-r--r--toolkit/components/places/PageIconProtocolHandler.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/toolkit/components/places/PageIconProtocolHandler.cpp b/toolkit/components/places/PageIconProtocolHandler.cpp
new file mode 100644
index 0000000000..43f59da29b
--- /dev/null
+++ b/toolkit/components/places/PageIconProtocolHandler.cpp
@@ -0,0 +1,246 @@
+/* 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 "PageIconProtocolHandler.h"
+
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "nsFaviconService.h"
+#include "nsIChannel.h"
+#include "nsIFaviconService.h"
+#include "nsIIOService.h"
+#include "nsILoadInfo.h"
+#include "nsIOutputStream.h"
+#include "nsIPipe.h"
+#include "nsIRequestObserver.h"
+#include "nsIURIMutator.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace places {
+
+StaticRefPtr<PageIconProtocolHandler> PageIconProtocolHandler::sSingleton;
+
+namespace {
+
+class DefaultFaviconObserver final : public nsIRequestObserver {
+ public:
+ explicit DefaultFaviconObserver(nsIOutputStream* aOutputStream)
+ : mOutputStream(aOutputStream) {
+ MOZ_ASSERT(aOutputStream);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ private:
+ ~DefaultFaviconObserver() = default;
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+};
+
+NS_IMPL_ISUPPORTS(DefaultFaviconObserver, nsIRequestObserver);
+
+NS_IMETHODIMP DefaultFaviconObserver::OnStartRequest(nsIRequest*) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP DefaultFaviconObserver::OnStopRequest(nsIRequest*, nsresult) {
+ // We must close the outputStream regardless.
+ mOutputStream->Close();
+ return NS_OK;
+}
+
+} // namespace
+
+static nsresult MakeDefaultFaviconChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
+ nsIChannel** aOutChannel) {
+ nsCOMPtr<nsIIOService> ios = mozilla::services::GetIOService();
+ nsCOMPtr<nsIChannel> chan;
+ nsCOMPtr<nsIURI> defaultFaviconURI;
+
+ auto* faviconService = nsFaviconService::GetFaviconService();
+ if (MOZ_UNLIKELY(!faviconService)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv =
+ faviconService->GetDefaultFavicon(getter_AddRefs(defaultFaviconURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ios->NewChannelFromURIWithLoadInfo(defaultFaviconURI, aLoadInfo,
+ getter_AddRefs(chan));
+ NS_ENSURE_SUCCESS(rv, rv);
+ chan->SetOriginalURI(aURI);
+ chan->SetContentType(nsLiteralCString(FAVICON_DEFAULT_MIMETYPE));
+ chan.forget(aOutChannel);
+ return NS_OK;
+}
+
+static nsresult StreamDefaultFavicon(nsIURI* aURI, nsILoadInfo* aLoadInfo,
+ nsIOutputStream* aOutputStream,
+ nsIChannel* aOriginalChannel) {
+ auto closeStreamOnError =
+ mozilla::MakeScopeExit([&] { aOutputStream->Close(); });
+
+ auto observer = MakeRefPtr<DefaultFaviconObserver>(aOutputStream);
+
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(listener),
+ aOutputStream, observer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOriginalChannel->SetContentType(nsLiteralCString(FAVICON_DEFAULT_MIMETYPE));
+ nsCOMPtr<nsIChannel> defaultIconChannel;
+ rv = MakeDefaultFaviconChannel(aURI, aLoadInfo,
+ getter_AddRefs(defaultIconChannel));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = defaultIconChannel->AsyncOpen(listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ closeStreamOnError.release();
+ return NS_OK;
+}
+
+namespace {
+
+class FaviconDataCallback final : public nsIFaviconDataCallback {
+ public:
+ FaviconDataCallback(nsIURI* aURI, nsIOutputStream* aOutputStream,
+ nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
+ : mURI(aURI),
+ mOutputStream(aOutputStream),
+ mChannel(aChannel),
+ mLoadInfo(aLoadInfo) {
+ MOZ_ASSERT(aOutputStream);
+ MOZ_ASSERT(aLoadInfo);
+ MOZ_ASSERT(aChannel);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFAVICONDATACALLBACK
+ private:
+ ~FaviconDataCallback() = default;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIChannel> mChannel;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+};
+
+NS_IMPL_ISUPPORTS(FaviconDataCallback, nsIFaviconDataCallback);
+
+NS_IMETHODIMP FaviconDataCallback::OnComplete(nsIURI* aURI, uint32_t aDataLen,
+ const uint8_t* aData,
+ const nsACString& aMimeType,
+ uint16_t aWidth) {
+ if (!aDataLen) {
+ return StreamDefaultFavicon(mURI, mLoadInfo, mOutputStream, mChannel);
+ }
+ mChannel->SetContentLength(aDataLen);
+ mChannel->SetContentType(aMimeType);
+ uint32_t wroteLength = 0;
+ mOutputStream->Write(reinterpret_cast<const char*>(aData), aDataLen,
+ &wroteLength);
+ return mOutputStream->Close();
+}
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(PageIconProtocolHandler, nsIProtocolHandler,
+ nsISupportsWeakReference);
+
+NS_IMETHODIMP PageIconProtocolHandler::GetScheme(nsACString& aScheme) {
+ aScheme.Assign("page-icon"_ns);
+ return NS_OK;
+}
+
+NS_IMETHODIMP PageIconProtocolHandler::GetDefaultPort(int32_t* aPort) {
+ *aPort = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP PageIconProtocolHandler::GetProtocolFlags(uint32_t* aFlags) {
+ *aFlags = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
+ URI_IS_LOCAL_RESOURCE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP PageIconProtocolHandler::AllowPort(int32_t, const char*,
+ bool* aAllow) {
+ *aAllow = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP PageIconProtocolHandler::NewChannel(nsIURI* aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** aOutChannel) {
+ nsresult rv = NewChannelInternal(aURI, aLoadInfo, aOutChannel);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+ return MakeDefaultFaviconChannel(aURI, aLoadInfo, aOutChannel);
+}
+
+nsresult PageIconProtocolHandler::NewChannelInternal(nsIURI* aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** aOutChannel) {
+ // Create a pipe that will give us an output stream that we can use once
+ // we got all the favicon data.
+ nsCOMPtr<nsIInputStream> pipeIn;
+ nsCOMPtr<nsIOutputStream> pipeOut;
+ nsresult rv =
+ NS_NewPipe(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), 0,
+ nsIFaviconService::MAX_FAVICON_BUFFER_SIZE, true, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create our channel.
+ nsCOMPtr<nsIChannel> channel;
+ {
+ // We override the channel's loadinfo below anyway, so using a null
+ // principal here is alright.
+ nsCOMPtr<nsIPrincipal> loadingPrincipal =
+ NullPrincipal::CreateWithoutOriginAttributes();
+ rv = NS_NewInputStreamChannel(
+ getter_AddRefs(channel), aURI, pipeIn.forget(), loadingPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = channel->SetLoadInfo(aLoadInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ auto faviconCallback =
+ MakeRefPtr<FaviconDataCallback>(aURI, pipeOut, channel, aLoadInfo);
+ auto* faviconService = nsFaviconService::GetFaviconService();
+ if (MOZ_UNLIKELY(!faviconService)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ uint16_t preferredSize = 0;
+ faviconService->PreferredSizeFromURI(aURI, &preferredSize);
+
+ nsCOMPtr<nsIURI> pageURI;
+ {
+ // NOTE: We don't need to strip #size= fragments because
+ // GetFaviconDataForPage strips them when doing the database lookup.
+ nsAutoCString pageQuery;
+ aURI->GetPathQueryRef(pageQuery);
+ rv = NS_NewURI(getter_AddRefs(pageURI), pageQuery);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = faviconService->GetFaviconDataForPage(pageURI, faviconCallback,
+ preferredSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ channel.forget(aOutChannel);
+ return NS_OK;
+}
+
+} // namespace places
+} // namespace mozilla