tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2bcdca1f12345cca4bb8fb52a5ac6aa5a44de98c
parent 674e9d235e1c9d9776371091d300fa38dc5413fd
Author: Kagami Sascha Rosylight <krosylight@proton.me>
Date:   Wed, 22 Oct 2025 23:09:46 +0000

Bug 1995831 - Split Decompress from DecompressAndEnqueue r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D269638

Diffstat:
Mdom/compression/BaseAlgorithms.cpp | 65++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mdom/compression/BaseAlgorithms.h | 16+++++++++++++---
Mdom/compression/FormatZlib.cpp | 50++++++++++++--------------------------------------
Mdom/compression/FormatZlib.h | 8+++-----
Mdom/compression/FormatZstd.cpp | 49+++++++++----------------------------------------
Mdom/compression/FormatZstd.h | 7+++----
6 files changed, 96 insertions(+), 99 deletions(-)

diff --git a/dom/compression/BaseAlgorithms.cpp b/dom/compression/BaseAlgorithms.cpp @@ -13,6 +13,15 @@ namespace mozilla::dom::compression { +NS_IMPL_CYCLE_COLLECTION_INHERITED(DecompressionStreamAlgorithms, + TransformerAlgorithmsBase) +NS_IMPL_ADDREF_INHERITED(DecompressionStreamAlgorithms, + TransformerAlgorithmsBase) +NS_IMPL_RELEASE_INHERITED(DecompressionStreamAlgorithms, + TransformerAlgorithmsBase) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DecompressionStreamAlgorithms) +NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase) + // Step 3 of // https://wicg.github.io/compression/#dom-decompressionstream-decompressionstream // Let transformAlgorithm be an algorithm which takes a chunk argument and @@ -65,17 +74,55 @@ MOZ_CAN_RUN_SCRIPT void DecompressionStreamAlgorithms::FlushCallbackImpl( // Step 1: Let buffer be the result of decompressing an empty input with // ds's format and context, with the finish flag. - // Step 2 - 4: (Done in DecompressAndEnqueue) + // Step 2 - 6: (Done in DecompressAndEnqueue) DecompressAndEnqueue(cx, Span<const uint8_t>(), Flush::Yes, aController, aRv); } -NS_IMPL_CYCLE_COLLECTION_INHERITED(DecompressionStreamAlgorithms, - TransformerAlgorithmsBase) -NS_IMPL_ADDREF_INHERITED(DecompressionStreamAlgorithms, - TransformerAlgorithmsBase) -NS_IMPL_RELEASE_INHERITED(DecompressionStreamAlgorithms, - TransformerAlgorithmsBase) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DecompressionStreamAlgorithms) -NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase) +// Shared by: +// https://wicg.github.io/compression/#decompress-and-enqueue-a-chunk +// https://wicg.github.io/compression/#decompress-flush-and-enqueue +MOZ_CAN_RUN_SCRIPT void DecompressionStreamAlgorithms::DecompressAndEnqueue( + JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, + TransformStreamDefaultController& aController, ErrorResult& aRv) { + MOZ_ASSERT_IF(aFlush == Flush::Yes, !aInput.Length()); + + JS::RootedVector<JSObject*> array(aCx); + + // Step 2: Let buffer be the result of decompressing chunk with ds’s format + // and context. If this results in an error, then throw a TypeError. + // Step 3: If buffer is empty, return. + // (Skipping, see https://github.com/whatwg/compression/issues/78) + // Step 4: Let arrays be the result of splitting buffer into one or more + // non-empty pieces and converting them into Uint8Arrays. + bool fullyConsumed = Decompress(aCx, aInput, &array, aFlush, aRv); + if (aRv.Failed()) { + return; + } + + // Step 5: For each Uint8Array array, enqueue array in ds's transform. + for (const auto& view : array) { + JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*view)); + aController.Enqueue(aCx, value, aRv); + if (aRv.Failed()) { + return; + } + } + + // Step 6: If the end of the compressed input has been reached, and ds's + // context has not fully consumed chunk, then throw a TypeError. + if (mObservedStreamEnd && !fullyConsumed) { + aRv.ThrowTypeError("Unexpected input after the end of stream"); + return; + } + + // Step 3 of + // https://wicg.github.io/compression/#decompress-flush-and-enqueue + // If the end of the compressed input has not been reached, then throw a + // TypeError. + if (aFlush == Flush::Yes && !mObservedStreamEnd) { + aRv.ThrowTypeError("The input is ended without reaching the stream end"); + return; + } +} } // namespace mozilla::dom::compression diff --git a/dom/compression/BaseAlgorithms.h b/dom/compression/BaseAlgorithms.h @@ -7,6 +7,7 @@ #ifndef DOM_COMPRESSION_BASEALGORITHMS_H_ #define DOM_COMPRESSION_BASEALGORITHMS_H_ +#include "js/TypeDecls.h" #include "mozilla/dom/TransformerCallbackHelpers.h" namespace mozilla::dom::compression { @@ -43,10 +44,19 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper { ~DecompressionStreamAlgorithms() = default; - MOZ_CAN_RUN_SCRIPT - virtual void DecompressAndEnqueue( + /** + * @return true if the input is fully consumed, else false + */ + virtual bool Decompress(JSContext* aCx, Span<const uint8_t> aInput, + JS::MutableHandleVector<JSObject*> aOutput, + Flush aFlush, ErrorResult& aRv) = 0; + + bool mObservedStreamEnd = false; + + private: + MOZ_CAN_RUN_SCRIPT void DecompressAndEnqueue( JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, - TransformStreamDefaultController& aController, ErrorResult& aRv) = 0; + TransformStreamDefaultController& aController, ErrorResult& aRv); }; } // namespace mozilla::dom::compression diff --git a/dom/compression/FormatZlib.cpp b/dom/compression/FormatZlib.cpp @@ -268,22 +268,19 @@ ZLibDecompressionStreamAlgorithms::Create(CompressionFormat format) { // https://wicg.github.io/compression/#decompress-flush-and-enqueue // All data errors throw TypeError by step 2: If this results in an error, // then throw a TypeError. -MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( - JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, - TransformStreamDefaultController& aController, ErrorResult& aRv) { - MOZ_ASSERT_IF(aFlush == Flush::Yes, !aInput.Length()); - +bool ZLibDecompressionStreamAlgorithms::Decompress( + JSContext* aCx, Span<const uint8_t> aInput, + JS::MutableHandleVector<JSObject*> aOutput, Flush aFlush, + ErrorResult& aRv) { mZStream.avail_in = aInput.Length(); mZStream.next_in = const_cast<uint8_t*>(aInput.Elements()); - JS::RootedVector<JSObject*> array(aCx); - do { UniquePtr<uint8_t[], JS::FreePolicy> buffer( static_cast<uint8_t*>(JS_malloc(aCx, kBufferSize))); if (!buffer) { aRv.ThrowTypeError("Out of memory"); - return; + return false; } mZStream.avail_out = kBufferSize; @@ -299,11 +296,11 @@ MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( // case strm->msg points to a string with a more specific error) aRv.ThrowTypeError("The input data is corrupted: "_ns + nsDependentCString(mZStream.msg)); - return; + return false; case Z_MEM_ERROR: // Z_MEM_ERROR if there was not enough memory aRv.ThrowTypeError("Out of memory"); - return; + return false; case Z_NEED_DICT: // Z_NEED_DICT if a preset dictionary is needed at this point // @@ -316,7 +313,7 @@ MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( aRv.ThrowTypeError( "The stream needs a preset dictionary but such setup is " "unsupported"); - return; + return false; case Z_STREAM_END: // Z_STREAM_END if the end of the compressed data has been reached and // all uncompressed output has been produced @@ -344,7 +341,7 @@ MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( // (which is fatal) MOZ_ASSERT_UNREACHABLE("Unexpected decompression error code"); aRv.ThrowTypeError("Unexpected decompression error"); - return; + return false; } // At this point we either exhausted the input or the output buffer, or @@ -365,10 +362,10 @@ MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array( aCx, written, std::move(buffer))); - if (!view || !array.append(view)) { + if (!view || !aOutput.append(view)) { JS_ClearPendingException(aCx); aRv.ThrowTypeError("Out of memory"); - return; + return false; } } while (mZStream.avail_out == 0 && !mObservedStreamEnd); // From the manual: @@ -377,30 +374,7 @@ MOZ_CAN_RUN_SCRIPT void ZLibDecompressionStreamAlgorithms::DecompressAndEnqueue( // * inflate() should normally be called until it returns Z_STREAM_END or an // error. - // Step 5: For each Uint8Array array, enqueue array in ds's transform. - for (const auto& view : array) { - JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*view)); - aController.Enqueue(aCx, value, aRv); - if (aRv.Failed()) { - return; - } - } - - // Step 6: If the end of the compressed input has been reached, and ds's - // context has not fully consumed chunk, then throw a TypeError. - if (mObservedStreamEnd && mZStream.avail_in > 0) { - aRv.ThrowTypeError("Unexpected input after the end of stream"); - return; - } - - // Step 3 of - // https://wicg.github.io/compression/#decompress-flush-and-enqueue - // If the end of the compressed input has not been reached, then throw a - // TypeError. - if (aFlush == Flush::Yes && !mObservedStreamEnd) { - aRv.ThrowTypeError("The input is ended without reaching the stream end"); - return; - } + return mZStream.avail_in == 0; } ZLibDecompressionStreamAlgorithms::~ZLibDecompressionStreamAlgorithms() { diff --git a/dom/compression/FormatZlib.h b/dom/compression/FormatZlib.h @@ -76,20 +76,18 @@ class ZLibDecompressionStreamAlgorithms : public DecompressionStreamAlgorithms { [[nodiscard]] nsresult Init(CompressionFormat format); - private: // Shared by: // https://wicg.github.io/compression/#decompress-and-enqueue-a-chunk // https://wicg.github.io/compression/#decompress-flush-and-enqueue // All data errors throw TypeError by step 2: If this results in an error, // then throw a TypeError. - MOZ_CAN_RUN_SCRIPT void DecompressAndEnqueue( - JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, - TransformStreamDefaultController& aController, ErrorResult& aRv) override; + bool Decompress(JSContext* aCx, Span<const uint8_t> aInput, + JS::MutableHandleVector<JSObject*> aOutput, Flush aFlush, + ErrorResult& aRv) override; ~ZLibDecompressionStreamAlgorithms() override; z_stream mZStream = {}; - bool mObservedStreamEnd = false; }; } // namespace mozilla::dom::compression diff --git a/dom/compression/FormatZstd.cpp b/dom/compression/FormatZstd.cpp @@ -48,28 +48,20 @@ ZstdDecompressionStreamAlgorithms::Create() { // https://wicg.github.io/compression/#decompress-flush-and-enqueue // All data errors throw TypeError by step 2: If this results in an error, // then throw a TypeError. -MOZ_CAN_RUN_SCRIPT void ZstdDecompressionStreamAlgorithms::DecompressAndEnqueue( - JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, - TransformStreamDefaultController& aController, ErrorResult& aRv) { - MOZ_ASSERT_IF(aFlush == Flush::Yes, !aInput.Length()); - - if (mObservedStreamEnd && aInput.Length() > 0) { - aRv.ThrowTypeError("Unexpected input after the end of stream"); - return; - } - +bool ZstdDecompressionStreamAlgorithms::Decompress( + JSContext* aCx, Span<const uint8_t> aInput, + JS::MutableHandleVector<JSObject*> aOutput, Flush aFlush, + ErrorResult& aRv) { ZSTD_inBuffer inBuffer = {/* src */ const_cast<uint8_t*>(aInput.Elements()), /* size */ aInput.Length(), /* pos */ 0}; - JS::RootedVector<JSObject*> array(aCx); - while (inBuffer.pos < inBuffer.size && !mObservedStreamEnd) { UniquePtr<uint8_t[], JS::FreePolicy> buffer( static_cast<uint8_t*>(JS_malloc(aCx, kBufferSize))); if (!buffer) { aRv.ThrowTypeError("Out of memory"); - return; + return false; } ZSTD_outBuffer outBuffer = {/* dst */ buffer.get(), @@ -80,7 +72,7 @@ MOZ_CAN_RUN_SCRIPT void ZstdDecompressionStreamAlgorithms::DecompressAndEnqueue( if (ZSTD_isError(rv)) { aRv.ThrowTypeError("zstd decompression error: "_ns + nsDependentCString(ZSTD_getErrorName(rv))); - return; + return false; } if (rv == 0) { @@ -98,38 +90,15 @@ MOZ_CAN_RUN_SCRIPT void ZstdDecompressionStreamAlgorithms::DecompressAndEnqueue( if (written > 0) { JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array( aCx, written, std::move(buffer))); - if (!view || !array.append(view)) { + if (!view || !aOutput.append(view)) { JS_ClearPendingException(aCx); aRv.ThrowTypeError("Out of memory"); - return; + return false; } } } - // Step 5: For each Uint8Array array, enqueue array in ds's transform. - for (const auto& view : array) { - JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*view)); - aController.Enqueue(aCx, value, aRv); - if (aRv.Failed()) { - return; - } - } - - // Step 6: If the end of the compressed input has been reached, and ds's - // context has not fully consumed chunk, then throw a TypeError. - if (mObservedStreamEnd && inBuffer.pos < inBuffer.size) { - aRv.ThrowTypeError("Unexpected input after the end of stream"); - return; - } - - // Step 3 of - // https://wicg.github.io/compression/#decompress-flush-and-enqueue - // If the end of the compressed input has not been reached, then throw a - // TypeError. - if (aFlush == Flush::Yes && !mObservedStreamEnd) { - aRv.ThrowTypeError("The input is ended without reaching the stream end"); - return; - } + return inBuffer.pos == inBuffer.size; } ZstdDecompressionStreamAlgorithms::~ZstdDecompressionStreamAlgorithms() { diff --git a/dom/compression/FormatZstd.h b/dom/compression/FormatZstd.h @@ -35,14 +35,13 @@ class ZstdDecompressionStreamAlgorithms : public DecompressionStreamAlgorithms { // https://wicg.github.io/compression/#decompress-flush-and-enqueue // All data errors throw TypeError by step 2: If this results in an error, // then throw a TypeError. - MOZ_CAN_RUN_SCRIPT void DecompressAndEnqueue( - JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, - TransformStreamDefaultController& aController, ErrorResult& aRv) override; + bool Decompress(JSContext* aCx, Span<const uint8_t> aInput, + JS::MutableHandleVector<JSObject*> aOutput, Flush aFlush, + ErrorResult& aRv) override; ~ZstdDecompressionStreamAlgorithms() override; ZSTD_DCtx_s* mDStream = nullptr; - bool mObservedStreamEnd = false; }; } // namespace mozilla::dom::compression