diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /netwerk/streamconv | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/streamconv')
-rw-r--r-- | netwerk/streamconv/converters/moz.build | 4 | ||||
-rw-r--r-- | netwerk/streamconv/converters/mozTXTToHTMLConv.cpp | 5 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsHTTPCompressConv.cpp | 233 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsHTTPCompressConv.h | 12 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsIndexedToHTML.cpp | 5 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsMultiMixedConv.cpp | 5 | ||||
-rw-r--r-- | netwerk/streamconv/converters/nsUnknownDecoder.cpp | 5 | ||||
-rw-r--r-- | netwerk/streamconv/nsIStreamConverter.idl | 6 |
8 files changed, 270 insertions, 5 deletions
diff --git a/netwerk/streamconv/converters/moz.build b/netwerk/streamconv/converters/moz.build index d985eb363a..2629a7e59a 100644 --- a/netwerk/streamconv/converters/moz.build +++ b/netwerk/streamconv/converters/moz.build @@ -29,3 +29,7 @@ LOCAL_INCLUDES += [ "/modules/brotli/dec", "/netwerk/base", ] + +DIRS += [ + "/third_party/zstd", +] diff --git a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp index a9f1d5183f..de99273862 100644 --- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp +++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp @@ -1263,6 +1263,11 @@ NS_IMETHODIMP mozTXTToHTMLConv::CheckListenerChain() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP +mozTXTToHTMLConv::MaybeRetarget(nsIRequest* request) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP mozTXTToHTMLConv::OnStartRequest(nsIRequest* request) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp index e06c72222b..7c7404f110 100644 --- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp +++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsHTTPCompressConv.h" +#include "ErrorList.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsError.h" @@ -19,6 +20,8 @@ #include "nsIForcePendingChannel.h" #include "nsIRequest.h" #include "mozilla/UniquePtrExtensions.h" +#include "nsIThreadRetargetableRequest.h" +#include "nsIChannel.h" // brotli headers #undef assert @@ -26,6 +29,8 @@ #include "state.h" #include "brotli/decode.h" +#include "zstd/zstd.h" + namespace mozilla { namespace net { @@ -50,6 +55,26 @@ class BrotliWrapper { uint64_t mSourceOffset{0}; }; +class ZstdWrapper { + public: + ZstdWrapper() { + mDStream = ZSTD_createDStream(); + ZSTD_DCtx_setParameter(mDStream, ZSTD_d_windowLogMax, 23 /*8*1024*1024*/); + } + ~ZstdWrapper() { + if (mDStream) { + ZSTD_freeDStream(mDStream); + } + } + + UniquePtr<uint8_t[]> mOutBuffer; + nsresult mStatus = NS_OK; + nsIRequest* mRequest{nullptr}; + nsISupports* mContext{nullptr}; + uint64_t mSourceOffset{0}; + ZSTD_DStream* mDStream{nullptr}; +}; + // nsISupports implementation NS_IMPL_ISUPPORTS(nsHTTPCompressConv, nsIStreamConverter, nsIStreamListener, nsIRequestObserver, nsICompressConvStats, @@ -109,6 +134,12 @@ nsHTTPCompressConv::AsyncConvertData(const char* aFromType, const char* aToType, } else if (!nsCRT::strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE) - 1)) { mMode = HTTP_COMPRESS_BROTLI; + } else if (!nsCRT::strncasecmp(aFromType, HTTP_ZSTD_TYPE, + sizeof(HTTP_ZSTD_TYPE) - 1)) { + mMode = HTTP_COMPRESS_ZSTD; + } else if (!nsCRT::strncasecmp(aFromType, HTTP_ZST_TYPE, + sizeof(HTTP_ZST_TYPE) - 1)) { + mMode = HTTP_COMPRESS_ZSTD; } LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", this, aFromType, aToType, (CompressMode)mMode)); @@ -128,6 +159,54 @@ nsHTTPCompressConv::GetConvertedType(const nsACString& aFromType, } NS_IMETHODIMP +nsHTTPCompressConv::MaybeRetarget(nsIRequest* request) { + MOZ_ASSERT(NS_IsMainThread()); + nsresult rv; + nsCOMPtr<nsIThreadRetargetableRequest> req = do_QueryInterface(request); + if (!req) { + return NS_ERROR_NO_INTERFACE; + } + if (!StaticPrefs::network_decompression_off_mainthread()) { + return NS_OK; + } + nsCOMPtr<nsISerialEventTarget> target; + rv = req->GetDeliveryTarget(getter_AddRefs(target)); + if (NS_FAILED(rv) || !target || target->IsOnCurrentThread()) { + nsCOMPtr<nsIChannel> channel(do_QueryInterface(request)); + int64_t length = -1; + if (channel) { + channel->GetContentLength(&length); + // If this fails we'll retarget + } + if (length <= 0 || + length >= + StaticPrefs::network_decompression_off_mainthread_min_size()) { + LOG(("MaybeRetarget: Retargeting to background thread: Length %" PRId64, + length)); + // No retargetting was performed. Decompress off MainThread, + // and dispatch results back to MainThread. + // Don't do this if the input is small, if we know the length. + // If the length is 0 (unknown), always use OMT. + nsCOMPtr<nsISerialEventTarget> backgroundThread; + rv = NS_CreateBackgroundTaskQueue("nsHTTPCompressConv", + getter_AddRefs(backgroundThread)); + NS_ENSURE_SUCCESS(rv, rv); + rv = req->RetargetDeliveryTo(backgroundThread); + NS_ENSURE_SUCCESS(rv, rv); + if (NS_SUCCEEDED(rv)) { + mDispatchToMainThread = true; + } + } else { + LOG(("MaybeRetarget: Not retargeting: Length %" PRId64, length)); + } + } else { + LOG(("MaybeRetarget: Don't need to retarget")); + } + + return NS_OK; +} + +NS_IMETHODIMP nsHTTPCompressConv::OnStartRequest(nsIRequest* request) { LOG(("nsHttpCompresssConv %p onstart\n", this)); nsCOMPtr<nsIStreamListener> listener; @@ -135,14 +214,33 @@ nsHTTPCompressConv::OnStartRequest(nsIRequest* request) { MutexAutoLock lock(mMutex); listener = mListener; } - return listener->OnStartRequest(request); + nsresult rv = listener->OnStartRequest(request); + if (NS_SUCCEEDED(rv)) { + if (XRE_IsContentProcess()) { + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetlistener = + do_QueryInterface(listener); + // |nsHTTPCompressConv| should *always* be dispatched off of the main + // thread from a content process, even if its listeners don't support it. + // + // If its listener chain does not support being retargeted off of the + // main thread, it will be dispatched back to the main thread in + // |do_OnDataAvailable| and |OnStopRequest|. + if (!retargetlistener || + NS_FAILED(retargetlistener->CheckListenerChain())) { + mDispatchToMainThread = true; + } + } + } + return rv; } NS_IMETHODIMP nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsresult aStatus) { nsresult status = aStatus; - LOG(("nsHttpCompresssConv %p onstop %" PRIx32 "\n", this, - static_cast<uint32_t>(aStatus))); + // Bug 1886237 : TRRServiceChannel calls OnStopRequest OMT + // MOZ_ASSERT(NS_IsMainThread()); + LOG(("nsHttpCompresssConv %p onstop %" PRIx32 " mDispatchToMainThread %d\n", + this, static_cast<uint32_t>(aStatus), mDispatchToMainThread)); // Framing integrity is enforced for content-encoding: gzip, but not for // content-encoding: deflate. Note that gzip vs deflate is NOT determined @@ -181,6 +279,7 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsresult aStatus) { MutexAutoLock lock(mMutex); listener = mListener; } + return listener->OnStopRequest(request, status); } @@ -292,6 +391,71 @@ nsresult nsHTTPCompressConv::BrotliHandler(nsIInputStream* stream, return self->mBrotli->mStatus; } +/* static */ +nsresult nsHTTPCompressConv::ZstdHandler(nsIInputStream* stream, void* closure, + const char* dataIn, uint32_t, + uint32_t aAvail, uint32_t* countRead) { + MOZ_ASSERT(stream); + nsHTTPCompressConv* self = static_cast<nsHTTPCompressConv*>(closure); + *countRead = 0; + + const size_t kOutSize = ZSTD_DStreamOutSize(); // normally 128K + uint8_t* outPtr; + size_t avail = aAvail; + + // Stop decompressing after an error + if (self->mZstd->mStatus != NS_OK) { + *countRead = aAvail; + return NS_OK; + } + + if (!self->mZstd->mOutBuffer) { + self->mZstd->mOutBuffer = MakeUniqueFallible<uint8_t[]>(kOutSize); + if (!self->mZstd->mOutBuffer) { + self->mZstd->mStatus = NS_ERROR_OUT_OF_MEMORY; + return self->mZstd->mStatus; + } + } + ZSTD_inBuffer inBuffer = {.src = dataIn, .size = aAvail, .pos = 0}; + uint32_t last_pos = 0; + while (inBuffer.pos < inBuffer.size) { + outPtr = self->mZstd->mOutBuffer.get(); + + LOG(("nsHttpCompresssConv %p zstdhandler decompress %zu\n", self, avail)); + // Use ZSTD_(de)compressStream to (de)compress the input buffer into the + // output buffer, and fill aReadCount with the number of bytes consumed. + ZSTD_outBuffer outBuffer{.dst = outPtr, .size = kOutSize}; + size_t result; + bool output_full; + do { + outBuffer.pos = 0; + result = + ZSTD_decompressStream(self->mZstd->mDStream, &outBuffer, &inBuffer); + + // If we errored when writing, flag this and abort writing. + if (ZSTD_isError(result)) { + self->mZstd->mStatus = NS_ERROR_INVALID_CONTENT_ENCODING; + return self->mZstd->mStatus; + } + + nsresult rv = self->do_OnDataAvailable( + self->mZstd->mRequest, self->mZstd->mSourceOffset, + reinterpret_cast<const char*>(outPtr), outBuffer.pos); + if (NS_FAILED(rv)) { + self->mZstd->mStatus = rv; + return rv; + } + self->mZstd->mSourceOffset += inBuffer.pos - last_pos; + last_pos = inBuffer.pos; + output_full = outBuffer.pos == outBuffer.size; + // in the unlikely case that the output buffer was full, loop to + // drain it before processing more input + } while (output_full); + } + *countRead = inBuffer.pos; + return NS_OK; +} + NS_IMETHODIMP nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, nsIInputStream* iStr, uint64_t aSourceOffset, uint32_t aCount) { @@ -525,6 +689,25 @@ nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, nsIInputStream* iStr, } } break; + case HTTP_COMPRESS_ZSTD: { + if (!mZstd) { + mZstd = MakeUnique<ZstdWrapper>(); + } + + mZstd->mRequest = request; + mZstd->mContext = nullptr; + mZstd->mSourceOffset = aSourceOffset; + + uint32_t countRead; + rv = iStr->ReadSegments(ZstdHandler, this, streamLen, &countRead); + if (NS_SUCCEEDED(rv)) { + rv = mZstd->mStatus; + } + if (NS_FAILED(rv)) { + return rv; + } + } break; + default: nsCOMPtr<nsIStreamListener> listener; { @@ -553,6 +736,35 @@ nsresult nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, uint64_t offset, const char* buffer, uint32_t count) { + LOG(("nsHttpCompressConv %p do_OnDataAvailable mDispatchToMainThread %d", + this, mDispatchToMainThread)); + if (mDispatchToMainThread && !NS_IsMainThread()) { + nsCOMPtr<nsIInputStream> stream; + MOZ_TRY(NS_NewByteInputStream(getter_AddRefs(stream), Span(buffer, count), + nsAssignmentType::NS_ASSIGNMENT_COPY)); + + nsCOMPtr<nsIStreamListener> listener; + { + MutexAutoLock lock(mMutex); + listener = mListener; + } + + // This is safe and will always run before OnStopRequest, because + // ChanneleventQueue means that we can't enqueue OnStopRequest until after + // the OMT OnDataAvailable call has completed. So Dispatching here will + // ensure it's in the MainThread event queue before OnStopRequest + nsCOMPtr<nsIRunnable> handler = NS_NewRunnableFunction( + "nsHTTPCompressConv::do_OnDataAvailable", + [request{RefPtr<nsIRequest>(request)}, stream{std::move(stream)}, + listener{std::move(listener)}, offset, count]() { + LOG(("nsHttpCompressConv Calling OnDataAvailable on Mainthread")); + Unused << listener->OnDataAvailable(request, stream, offset, count); + }); + + mDecodedDataLength += count; + return NS_DispatchToMainThread(handler); + } + if (!mStream) { mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); NS_ENSURE_STATE(mStream); @@ -725,12 +937,16 @@ uint32_t nsHTTPCompressConv::check_header(nsIInputStream* iStr, NS_IMETHODIMP nsHTTPCompressConv::CheckListenerChain() { + if (XRE_IsContentProcess()) { + // handle decompression OMT always. If the chain needs to be MT, + // we'll determine that in OnStartRequest and dispatch to MT + return NS_OK; + } nsCOMPtr<nsIThreadRetargetableStreamListener> listener; { MutexAutoLock lock(mMutex); listener = do_QueryInterface(mListener); } - if (!listener) { return NS_ERROR_NO_INTERFACE; } @@ -748,6 +964,15 @@ nsHTTPCompressConv::OnDataFinished(nsresult aStatus) { } if (listener) { + if (mDispatchToMainThread && !NS_IsMainThread()) { + nsCOMPtr<nsIRunnable> handler = NS_NewRunnableFunction( + "dispatch", [listener{std::move(listener)}, aStatus]() { + Unused << listener->OnDataFinished(aStatus); + }); + + return NS_DispatchToMainThread(handler); + } + return listener->OnDataFinished(aStatus); } diff --git a/netwerk/streamconv/converters/nsHTTPCompressConv.h b/netwerk/streamconv/converters/nsHTTPCompressConv.h index 4ba2d9819c..1008edcf27 100644 --- a/netwerk/streamconv/converters/nsHTTPCompressConv.h +++ b/netwerk/streamconv/converters/nsHTTPCompressConv.h @@ -34,11 +34,14 @@ class nsIStringInputStream; # define HTTP_BROTLI_TYPE "br" # define HTTP_IDENTITY_TYPE "identity" # define HTTP_UNCOMPRESSED_TYPE "uncompressed" +# define HTTP_ZSTD_TYPE "zstd" +# define HTTP_ZST_TYPE "zst" namespace mozilla { namespace net { class BrotliWrapper; +class ZstdWrapper; class nsHTTPCompressConv : public nsIStreamConverter, public nsICompressConvStats { @@ -60,7 +63,8 @@ class nsHTTPCompressConv : public nsIStreamConverter, HTTP_COMPRESS_DEFLATE, HTTP_COMPRESS_COMPRESS, HTTP_COMPRESS_BROTLI, - HTTP_COMPRESS_IDENTITY + HTTP_COMPRESS_IDENTITY, + HTTP_COMPRESS_ZSTD, }; private: @@ -77,6 +81,7 @@ class nsHTTPCompressConv : public nsIStreamConverter, uint32_t mInpBufferLen{0}; UniquePtr<BrotliWrapper> mBrotli; + UniquePtr<ZstdWrapper> mZstd; nsCOMPtr<nsIStringInputStream> mStream; @@ -84,6 +89,10 @@ class nsHTTPCompressConv : public nsIStreamConverter, const char* dataIn, uint32_t, uint32_t avail, uint32_t* countRead); + static nsresult ZstdHandler(nsIInputStream* stream, void* closure, + const char* dataIn, uint32_t, uint32_t avail, + uint32_t* countRead); + nsresult do_OnDataAvailable(nsIRequest* request, uint64_t aSourceOffset, const char* buffer, uint32_t aCount); @@ -92,6 +101,7 @@ class nsHTTPCompressConv : public nsIStreamConverter, bool mStreamInitialized{false}; bool mDummyStreamInitialised{false}; bool mFailUncleanStops; + bool mDispatchToMainThread{false}; z_stream d_stream{}; unsigned mLen{0}, hMode{0}, mSkipCount{0}, mFlags{0}; diff --git a/netwerk/streamconv/converters/nsIndexedToHTML.cpp b/netwerk/streamconv/converters/nsIndexedToHTML.cpp index efc8962459..40650d9fce 100644 --- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp +++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp @@ -98,6 +98,11 @@ nsIndexedToHTML::GetConvertedType(const nsACString& aFromType, } NS_IMETHODIMP +nsIndexedToHTML::MaybeRetarget(nsIRequest* request) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsIndexedToHTML::OnStartRequest(nsIRequest* request) { nsCString buffer; nsresult rv = DoOnStartRequest(request, buffer); diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/netwerk/streamconv/converters/nsMultiMixedConv.cpp index fb21d6673f..df7294acf9 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -442,6 +442,11 @@ nsMultiMixedConv::GetConvertedType(const nsACString& aFromType, return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsMultiMixedConv::MaybeRetarget(nsIRequest* request) { + return NS_ERROR_NOT_IMPLEMENTED; +} + // nsIRequestObserver implementation NS_IMETHODIMP nsMultiMixedConv::OnStartRequest(nsIRequest* request) { diff --git a/netwerk/streamconv/converters/nsUnknownDecoder.cpp b/netwerk/streamconv/converters/nsUnknownDecoder.cpp index b572b1c276..33c6ea4f1d 100644 --- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp +++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp @@ -223,6 +223,11 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request, nsIInputStream* aStream, return rv; } +NS_IMETHODIMP +nsUnknownDecoder::MaybeRetarget(nsIRequest* request) { + return NS_ERROR_NOT_IMPLEMENTED; +} + // ---- // // nsIRequestObserver methods... diff --git a/netwerk/streamconv/nsIStreamConverter.idl b/netwerk/streamconv/nsIStreamConverter.idl index 68f341b328..b2fc32ebcf 100644 --- a/netwerk/streamconv/nsIStreamConverter.idl +++ b/netwerk/streamconv/nsIStreamConverter.idl @@ -96,6 +96,12 @@ interface nsIStreamConverter : nsIThreadRetargetableStreamListener { in nsISupports aCtxt); /** + * This is called after the request has installed the stream converter as its listener + * giving the stream converter the option to request retargetting of onDataAvailable. + */ + void maybeRetarget(in nsIRequest request); + + /** * Returns the content type that the stream listener passed to asyncConvertData will * see on the channel if the conversion is being done from aFromType to * /*. * |