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:
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