diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/places/PageIconProtocolHandler.cpp | |
parent | Initial commit. (diff) | |
download | firefox-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.cpp | 246 |
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 |