summaryrefslogtreecommitdiff
path: root/dom/fetch
diff options
context:
space:
mode:
authorBrian Smith <brian@dbsoft.org>2023-09-28 17:36:52 -0500
committerBrian Smith <brian@dbsoft.org>2023-09-28 17:36:52 -0500
commitc9020fed9cd5bb829bbee959e94b2e58e91bf6a5 (patch)
tree8d253dd4c669ab8ca1f70acfea66deae9ddd0fc7 /dom/fetch
parent4c6e382f470b79c0dfb962067242c0f1af75a653 (diff)
downloaduxp-c9020fed9cd5bb829bbee959e94b2e58e91bf6a5.tar.gz
Issue #1442 - Part 13 - Implement FetchStreamReader.
https://bugzilla.mozilla.org/show_bug.cgi?id=1329298
Diffstat (limited to 'dom/fetch')
-rw-r--r--dom/fetch/Fetch.cpp100
-rw-r--r--dom/fetch/Fetch.h10
-rw-r--r--dom/fetch/FetchStreamReader.cpp307
-rw-r--r--dom/fetch/FetchStreamReader.h77
-rw-r--r--dom/fetch/InternalResponse.cpp6
-rw-r--r--dom/fetch/InternalResponse.h8
-rw-r--r--dom/fetch/Request.cpp3
-rw-r--r--dom/fetch/Response.cpp106
-rw-r--r--dom/fetch/moz.build2
9 files changed, 557 insertions, 62 deletions
diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp
index 99e26e8511..ce21cd391a 100644
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -881,6 +881,7 @@ FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
: mWorkerPrivate(nullptr)
, mOwner(aOwner)
, mReadableStreamBody(nullptr)
+ , mReadableStreamReader(nullptr)
, mBodyUsed(false)
{
MOZ_ASSERT(aOwner);
@@ -904,6 +905,12 @@ FetchBody<Derived>::~FetchBody()
{
}
+template
+FetchBody<Request>::~FetchBody();
+
+template
+FetchBody<Response>::~FetchBody();
+
template <class Derived>
bool
FetchBody<Derived>::BodyUsed() const
@@ -958,13 +965,27 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorRes
SetBodyUsed();
- // If we already created a ReadableStreamBody we have to lock it now because
- // it may have been shared with other objects..
+ // If we already have a ReadableStreamBody and it has been created by DOM, we
+ // have to lock it now because it can have been shared with other objects.
if (mReadableStreamBody) {
- JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
- LockStream(aCx, body, aRv);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
+ JS::Rooted<JSObject*> readableStreamObj(aCx, mReadableStreamBody);
+ if (JS::ReadableStreamGetMode(readableStreamObj) ==
+ JS::ReadableStreamMode::ExternalSource) {
+ LockStream(aCx, readableStreamObj, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ } else {
+ // If this is not a native ReadableStream, let's activate the
+ // FetchStreamReader.
+ MOZ_ASSERT(mFetchStreamReader);
+ JS::Rooted<JSObject*> reader(aCx);
+ mFetchStreamReader->StartConsuming(aCx, readableStreamObj, &reader, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ mReadableStreamReader = reader;
}
}
@@ -1038,6 +1059,11 @@ FetchBody<Derived>::GetBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
ErrorResult& aRv)
{
+ if (mReadableStreamBody) {
+ aBodyOut.set(mReadableStreamBody);
+ return;
+ }
+
nsCOMPtr<nsIInputStream> inputStream;
DerivedClass()->GetBody(getter_AddRefs(inputStream));
@@ -1046,30 +1072,27 @@ FetchBody<Derived>::GetBody(JSContext* aCx,
return;
}
- if (!mReadableStreamBody) {
- JS::Rooted<JSObject*> body(aCx,
- FetchStream::Create(aCx,
- this,
- DerivedClass()->GetParentObject(),
- inputStream,
- aRv));
- if (NS_WARN_IF(aRv.Failed())) {
- return;
- }
+ JS::Rooted<JSObject*> body(aCx,
+ FetchStream::Create(aCx,
+ this,
+ DerivedClass()->GetParentObject(),
+ inputStream,
+ aRv));
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
- MOZ_ASSERT(body);
+ MOZ_ASSERT(body);
- // If the body has been already consumed, we close the stream.
- if (BodyUsed()) {
- LockStream(aCx, body, aRv);
- if (NS_WARN_IF(aRv.Failed())) {
- return;
- }
+ // If the body has been already consumed, we lock the stream.
+ if (BodyUsed()) {
+ LockStream(aCx, body, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
-
- mReadableStreamBody = body;
}
+ mReadableStreamBody = body;
aBodyOut.set(mReadableStreamBody);
}
@@ -1091,7 +1114,15 @@ FetchBody<Derived>::LockStream(JSContext* aCx,
JS::HandleObject aStream,
ErrorResult& aRv)
{
- // XXXMC: TODO
+ JS::Rooted<JSObject*> reader(aCx,
+ JS::ReadableStreamGetReader(aCx, aStream,
+ JS::ReadableStreamReaderMode::Default));
+ if (!reader) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ mReadableStreamReader = reader;
}
template
@@ -1110,10 +1141,18 @@ template <class Derived>
void
FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
ErrorResult& aRv)
{
+ MOZ_DIAGNOSTIC_ASSERT(aStreamReader);
+ MOZ_DIAGNOSTIC_ASSERT(aInputStream);
MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
+ aBodyOut.set(nullptr);
+ *aStreamReader = nullptr;
+ *aInputStream = nullptr;
+
if (!mReadableStreamBody) {
return;
}
@@ -1138,18 +1177,27 @@ FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
mReadableStreamBody = branch1;
aBodyOut.set(branch2);
+
+ aRv = FetchStreamReader::Create(aCx, mOwner, aStreamReader, aInputStream);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
}
template
void
FetchBody<Request>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
ErrorResult& aRv);
template
void
FetchBody<Response>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
ErrorResult& aRv);
} // namespace dom
diff --git a/dom/fetch/Fetch.h b/dom/fetch/Fetch.h
index 32c6671e08..f95a061bd9 100644
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -17,6 +17,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/FetchStreamReader.h"
#include "mozilla/dom/RequestBinding.h"
class nsIGlobalObject;
@@ -183,6 +184,8 @@ public:
void
MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
ErrorResult& aRv);
// Utility public methods accessed by various runnables.
@@ -199,10 +202,13 @@ public:
return mMimeType;
}
+ // FetchStreamHolder
void
NullifyStream() override
{
mReadableStreamBody = nullptr;
+ mReadableStreamReader = nullptr;
+ mFetchStreamReader = nullptr;
}
virtual AbortSignal*
@@ -219,6 +225,10 @@ protected:
// This is the ReadableStream exposed to content. Its underlying source is a FetchStream object.
JS::Heap<JSObject*> mReadableStreamBody;
+ // This is the Reader used to retrieve data from the body.
+ JS::Heap<JSObject*> mReadableStreamReader;
+ RefPtr<FetchStreamReader> mFetchStreamReader;
+
virtual ~FetchBody();
void
diff --git a/dom/fetch/FetchStreamReader.cpp b/dom/fetch/FetchStreamReader.cpp
new file mode 100644
index 0000000000..98d2588a0e
--- /dev/null
+++ b/dom/fetch/FetchStreamReader.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "FetchStreamReader.h"
+#include "InternalResponse.h"
+#include "mozilla/dom/PromiseBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+namespace {
+
+class FetchStreamReaderWorkerHolder final : public WorkerHolder
+{
+public:
+ explicit FetchStreamReaderWorkerHolder(FetchStreamReader* aReader)
+ : WorkerHolder(WorkerHolder::Behavior::AllowIdleShutdownStart)
+ , mReader(aReader)
+ , mWasNotified(false)
+ {}
+
+ bool Notify(Status aStatus) override
+ {
+ if (!mWasNotified) {
+ mWasNotified = true;
+ mReader->CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ }
+
+ return true;
+ }
+
+private:
+ RefPtr<FetchStreamReader> mReader;
+ bool mWasNotified;
+};
+
+} // anonymous
+
+NS_IMPL_ISUPPORTS(FetchStreamReader, nsIOutputStreamCallback)
+
+/* static */ nsresult
+FetchStreamReader::Create(JSContext* aCx, nsIGlobalObject* aGlobal,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream)
+{
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aGlobal);
+ MOZ_ASSERT(aStreamReader);
+ MOZ_ASSERT(aInputStream);
+
+ RefPtr<FetchStreamReader> streamReader = new FetchStreamReader(aGlobal);
+
+ nsCOMPtr<nsIAsyncInputStream> pipeIn;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn),
+ getter_AddRefs(streamReader->mPipeOut),
+ true, true, 0, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ MOZ_ASSERT(workerPrivate);
+
+ // We need to know when the worker goes away.
+ UniquePtr<FetchStreamReaderWorkerHolder> holder(
+ new FetchStreamReaderWorkerHolder(streamReader));
+ if (NS_WARN_IF(!holder->HoldWorker(workerPrivate, Closing))) {
+ streamReader->mPipeOut->CloseWithStatus(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ // These 2 objects create a ref-cycle here that is broken when the stream is
+ // closed or the worker shutsdown.
+ streamReader->mWorkerHolder = Move(holder);
+ }
+
+ pipeIn.forget(aInputStream);
+ streamReader.forget(aStreamReader);
+ return NS_OK;
+}
+
+FetchStreamReader::FetchStreamReader(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal)
+ , mBufferRemaining(0)
+ , mBufferOffset(0)
+ , mStreamClosed(false)
+{
+ MOZ_ASSERT(aGlobal);
+}
+
+FetchStreamReader::~FetchStreamReader()
+{
+ CloseAndRelease(NS_BASE_STREAM_CLOSED);
+}
+
+void
+FetchStreamReader::CloseAndRelease(nsresult aStatus)
+{
+ NS_ASSERT_OWNINGTHREAD(FetchStreamReader);
+
+ if (mStreamClosed) {
+ // Already closed.
+ return;
+ }
+
+ RefPtr<FetchStreamReader> kungFuDeathGrip = this;
+
+ mStreamClosed = true;
+
+ mGlobal = nullptr;
+
+ mPipeOut->CloseWithStatus(aStatus);
+ mPipeOut = nullptr;
+
+ mWorkerHolder = nullptr;
+
+ mReader = nullptr;
+ mBuffer = nullptr;
+}
+
+void
+FetchStreamReader::StartConsuming(JSContext* aCx,
+ JS::HandleObject aStream,
+ JS::MutableHandle<JSObject*> aReader,
+ ErrorResult& aRv)
+{
+ MOZ_DIAGNOSTIC_ASSERT(!mReader);
+ MOZ_DIAGNOSTIC_ASSERT(aStream);
+
+ JS::Rooted<JSObject*> reader(aCx,
+ JS::ReadableStreamGetReader(aCx, aStream,
+ JS::ReadableStreamReaderMode::Default));
+ if (!reader) {
+ aRv.StealExceptionFromJSContext(aCx);
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ mReader = reader;
+ aReader.set(reader);
+
+ aRv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+}
+
+// nsIOutputStreamCallback interface
+
+NS_IMETHODIMP
+FetchStreamReader::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
+{
+ NS_ASSERT_OWNINGTHREAD(FetchStreamReader);
+ MOZ_ASSERT(aStream == mPipeOut);
+ MOZ_ASSERT(mReader);
+
+ if (mStreamClosed) {
+ return NS_OK;
+ }
+
+ if (mBuffer) {
+ return WriteBuffer();
+ }
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_FAILURE;
+ }
+
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JSObject*> reader(cx, mReader);
+ JS::Rooted<JSObject*> promise(cx,
+ JS::ReadableStreamDefaultReaderRead(cx,
+ reader));
+ if (NS_WARN_IF(!promise)) {
+ // Let's close the stream.
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<Promise> domPromise = Promise::CreateFromExisting(mGlobal, promise);
+ if (NS_WARN_IF(!domPromise)) {
+ // Let's close the stream.
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Let's wait.
+ domPromise->AppendNativeHandler(this);
+ return NS_OK;
+}
+
+void
+FetchStreamReader::ResolvedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue)
+{
+ if (mStreamClosed) {
+ return;
+ }
+
+ // This promise should be resolved with { done: boolean, value: something },
+ // "value" is interesting only if done is false.
+
+ // We don't want to play with JS api, let's WebIDL bindings doing it for us.
+ // FetchReadableStreamReadDataDone is a dictionary with just a boolean, if the
+ // parsing succeeded, we can proceed with the parsing of the "value", which it
+ // must be a Uint8Array.
+ FetchReadableStreamReadDataDone valueDone;
+ if (!valueDone.Init(aCx, aValue)) {
+ JS_ClearPendingException(aCx);
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ if (valueDone.mDone) {
+ // Stream is completed.
+ CloseAndRelease(NS_BASE_STREAM_CLOSED);
+ return;
+ }
+
+ UniquePtr<FetchReadableStreamReadDataArray> value(
+ new FetchReadableStreamReadDataArray);
+ if (!value->Init(aCx, aValue) || !value->mValue.WasPassed()) {
+ JS_ClearPendingException(aCx);
+ CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ Uint8Array& array = value->mValue.Value();
+ array.ComputeLengthAndData();
+ uint32_t len = array.Length();
+
+ if (len == 0) {
+ // If there is nothing to read, let's do another reading.
+ OnOutputStreamReady(mPipeOut);
+ return;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(!mBuffer);
+ mBuffer = Move(value);
+
+ mBufferOffset = 0;
+ mBufferRemaining = len;
+
+ WriteBuffer();
+}
+
+nsresult
+FetchStreamReader::WriteBuffer()
+{
+ MOZ_ASSERT(mBuffer);
+ MOZ_ASSERT(mBuffer->mValue.WasPassed());
+
+ Uint8Array& array = mBuffer->mValue.Value();
+ char* data = reinterpret_cast<char*>(array.Data());
+
+ while (1) {
+ uint32_t written = 0;
+ nsresult rv =
+ mPipeOut->Write(data + mBufferOffset, mBufferRemaining, &written);
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ break;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseAndRelease(rv);
+ return rv;
+ }
+
+ MOZ_ASSERT(written <= mBufferRemaining);
+ mBufferRemaining -= written;
+ mBufferOffset += written;
+
+ if (mBufferRemaining == 0) {
+ mBuffer = nullptr;
+ break;
+ }
+ }
+
+ nsresult rv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseAndRelease(rv);
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+FetchStreamReader::RejectedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue)
+{
+ CloseAndRelease(NS_ERROR_FAILURE);
+}
+
+} // dom namespace
+} // mozilla namespace
diff --git a/dom/fetch/FetchStreamReader.h b/dom/fetch/FetchStreamReader.h
new file mode 100644
index 0000000000..8984a2ea73
--- /dev/null
+++ b/dom/fetch/FetchStreamReader.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_FetchStreamReader_h
+#define mozilla_dom_FetchStreamReader_h
+
+#include "jsapi.h"
+#include "mozilla/dom/FetchBinding.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "nsIAsyncOutputStream.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerHolder;
+}
+
+class FetchStreamReader final : public nsIOutputStreamCallback
+ , public PromiseNativeHandler
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOUTPUTSTREAMCALLBACK
+
+ // This creates a nsIInputStream able to retrieve data from the ReadableStream
+ // object. The reading starts when StartConsuming() is called.
+ static nsresult
+ Create(JSContext* aCx, nsIGlobalObject* aGlobal,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream);
+
+ void
+ ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ void
+ RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ void
+ CloseAndRelease(nsresult aStatus);
+
+ void
+ StartConsuming(JSContext* aCx,
+ JS::HandleObject aStream,
+ JS::MutableHandle<JSObject*> aReader,
+ ErrorResult& aRv);
+
+private:
+ explicit FetchStreamReader(nsIGlobalObject* aGlobal);
+ ~FetchStreamReader();
+
+ nsresult
+ WriteBuffer();
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+
+ nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
+
+ UniquePtr<workers::WorkerHolder> mWorkerHolder;
+
+ JS::Heap<JSObject*> mReader;
+
+ UniquePtr<FetchReadableStreamReadDataArray> mBuffer;
+ uint32_t mBufferRemaining;
+ uint32_t mBufferOffset;
+
+ bool mStreamClosed;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FetchStreamReader_h
diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp
index ec32c4e216..f4fd1912f9 100644
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -139,18 +139,18 @@ InternalResponse::ToIPC(IPCInternalResponse* aIPCResponse,
}
already_AddRefed<InternalResponse>
-InternalResponse::Clone()
+InternalResponse::Clone(CloneType aCloneType)
{
RefPtr<InternalResponse> clone = CreateIncompleteCopy();
clone->mHeaders = new InternalHeaders(*mHeaders);
if (mWrappedResponse) {
- clone->mWrappedResponse = mWrappedResponse->Clone();
+ clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
MOZ_ASSERT(!mBody);
return clone.forget();
}
- if (!mBody) {
+ if (!mBody || aCloneType == eDontCloneInputStream) {
return clone.forget();
}
diff --git a/dom/fetch/InternalResponse.h b/dom/fetch/InternalResponse.h
index e4b4a0ab62..d5c759d577 100644
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -46,7 +46,13 @@ public:
M* aManager,
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
- already_AddRefed<InternalResponse> Clone();
+ enum CloneType
+ {
+ eCloneInputStream,
+ eDontCloneInputStream,
+ };
+
+ already_AddRefed<InternalResponse> Clone(CloneType eCloneType);
static already_AddRefed<InternalResponse>
NetworkError()
diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp
index eec4828131..ec140a22d4 100644
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -44,7 +44,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Request)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Request)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
+ MOZ_DIAGNOSTIC_ASSERT(!tmp->mReadableStreamReader);
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamReader)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp
index b92e918e7c..5055db4e24 100644
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -21,6 +21,7 @@
#include "BodyExtractor.h"
#include "FetchStream.h"
+#include "FetchStreamReader.h"
#include "InternalResponse.h"
#include "WorkerPrivate.h"
@@ -37,6 +38,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Response)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
tmp->mReadableStreamBody = nullptr;
+ tmp->mReadableStreamReader = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -48,6 +50,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Response)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamReader)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -227,7 +230,7 @@ Response::Constructor(const GlobalObject& aGlobal,
nsCString contentTypeWithCharset;
nsCOMPtr<nsIInputStream> bodyStream;
- uint64_t bodySize = 0;
+ int64_t bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
if (aBody.Value().IsReadableStream()) {
const ReadableStream& readableStream =
@@ -245,37 +248,50 @@ Response::Constructor(const GlobalObject& aGlobal,
r->SetReadableStreamBody(readableStreamObj);
- // XXXMC: TODO
- MOZ_ASSERT(JS::ReadableStreamGetMode(readableStreamObj) !=
- JS::ReadableStreamMode::ExternalSource);
-
- void* underlyingSource = nullptr;
- if (!JS::ReadableStreamGetExternalUnderlyingSource(aGlobal.Context(),
- readableStreamObj,
- &underlyingSource)) {
- aRv.StealExceptionFromJSContext(aGlobal.Context());
- return nullptr;
+ if (JS::ReadableStreamGetMode(readableStreamObj) ==
+ JS::ReadableStreamMode::ExternalSource) {
+ // If this is a DOM generated ReadableStream, we can extract the
+ // inputStream directly.
+ void* underlyingSource = nullptr;
+ if (!JS::ReadableStreamGetExternalUnderlyingSource(aGlobal.Context(),
+ readableStreamObj,
+ &underlyingSource)) {
+ aRv.StealExceptionFromJSContext(aGlobal.Context());
+ return nullptr;
+ }
+
+ MOZ_ASSERT(underlyingSource);
+
+ aRv = FetchStream::RetrieveInputStream(underlyingSource,
+ getter_AddRefs(bodyStream));
+
+ // The releasing of the external source is needed in order to avoid an
+ // extra stream lock.
+ JS::ReadableStreamReleaseExternalUnderlyingSource(readableStreamObj);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ } else {
+ // If this is a JS-created ReadableStream, let's create a
+ // FetchStreamReader.
+ aRv = FetchStreamReader::Create(aGlobal.Context(), global,
+ getter_AddRefs(r->mFetchStreamReader),
+ getter_AddRefs(bodyStream));
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
}
-
- bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
-
- MOZ_ASSERT(underlyingSource);
- aRv = FetchStream::RetrieveInputStream(underlyingSource,
- getter_AddRefs(bodyStream));
-
- // Releasing of the external source is needed in order to avoid an extra stream lock.
- JS::ReadableStreamReleaseExternalUnderlyingSource(readableStreamObj);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
- }
} else {
+ uint64_t size = 0;
aRv = ExtractByteStreamFromBody(aBody.Value(),
getter_AddRefs(bodyStream),
contentTypeWithCharset,
- bodySize);
+ size);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
+
+ bodySize = size;
}
internalResponse->SetBody(bodyStream, bodySize);
@@ -306,21 +322,35 @@ Response::Clone(JSContext* aCx, ErrorResult& aRv)
return nullptr;
}
- RefPtr<InternalResponse> ir = mInternalResponse->Clone();
- RefPtr<Response> response = new Response(mOwner, ir, mSignal);
+ RefPtr<FetchStreamReader> streamReader;
+ nsCOMPtr<nsIInputStream> inputStream;
JS::Rooted<JSObject*> body(aCx);
- MaybeTeeReadableStreamBody(aCx, &body, aRv);
+ MaybeTeeReadableStreamBody(aCx, &body,
+ getter_AddRefs(streamReader),
+ getter_AddRefs(inputStream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
+ MOZ_ASSERT_IF(body, streamReader);
+ MOZ_ASSERT_IF(body, inputStream);
+
+ RefPtr<InternalResponse> ir =
+ mInternalResponse->Clone(body
+ ? InternalResponse::eDontCloneInputStream
+ : InternalResponse::eCloneInputStream);
+
+ RefPtr<Response> response = new Response(mOwner, ir, nullptr);
+
if (body) {
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
// if this body is a native stream. In this case, the InternalResponse will
// have a clone of the native body and the ReadableStream will be created
// lazily if needed.
response->SetReadableStreamBody(body);
+ response->mFetchStreamReader = streamReader;
+ ir->SetBody(inputStream, InternalResponse::UNKNOWN_BODY_SIZE);
}
return response.forget();
@@ -334,22 +364,36 @@ Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv)
return nullptr;
}
- RefPtr<InternalResponse> clone = mInternalResponse->Clone();
- RefPtr<InternalResponse> ir = clone->Unfiltered();
- RefPtr<Response> ref = new Response(mOwner, ir, mSignal);
+ RefPtr<FetchStreamReader> streamReader;
+ nsCOMPtr<nsIInputStream> inputStream;
JS::Rooted<JSObject*> body(aCx);
- MaybeTeeReadableStreamBody(aCx, &body, aRv);
+ MaybeTeeReadableStreamBody(aCx, &body,
+ getter_AddRefs(streamReader),
+ getter_AddRefs(inputStream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
+ MOZ_ASSERT_IF(body, streamReader);
+ MOZ_ASSERT_IF(body, inputStream);
+
+ RefPtr<InternalResponse> clone =
+ mInternalResponse->Clone(body
+ ? InternalResponse::eDontCloneInputStream
+ : InternalResponse::eCloneInputStream);
+
+ RefPtr<InternalResponse> ir = clone->Unfiltered();
+ RefPtr<Response> ref = new Response(mOwner, ir, nullptr);
+
if (body) {
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
// if this body is a native stream. In this case, the InternalResponse will
// have a clone of the native body and the ReadableStream will be created
// lazily if needed.
ref->SetReadableStreamBody(body);
+ ref->mFetchStreamReader = streamReader;
+ ir->SetBody(inputStream, InternalResponse::UNKNOWN_BODY_SIZE);
}
return ref.forget();
diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build
index d08bdf0cf2..f01254e042 100644
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -10,6 +10,7 @@ EXPORTS.mozilla.dom += [
'FetchDriver.h',
'FetchIPCTypes.h',
'FetchObserver.h',
+ 'FetchStreamReader.h',
'FetchUtil.h',
'Headers.h',
'InternalHeaders.h',
@@ -27,6 +28,7 @@ UNIFIED_SOURCES += [
'FetchDriver.cpp',
'FetchObserver.cpp',
'FetchStream.cpp',
+ 'FetchStreamReader.cpp',
'FetchUtil.cpp',
'Headers.cpp',
'InternalHeaders.cpp',