diff options
Diffstat (limited to 'dom/fetch/Response.cpp')
-rw-r--r-- | dom/fetch/Response.cpp | 95 |
1 files changed, 85 insertions, 10 deletions
diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index de38fa3e53..bc3d543663 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -12,12 +12,14 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FetchBinding.h" +#include "mozilla/dom/ResponseBinding.h" #include "mozilla/dom/Headers.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/URL.h" #include "nsDOMString.h" +#include "FetchStream.h" #include "InternalResponse.h" #include "WorkerPrivate.h" @@ -129,7 +131,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, return nullptr; } - Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>> body; + Optional<Nullable<fetch::ResponseBodyInit>> body; ResponseInit init; init.mStatus = aStatus; RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv); @@ -150,7 +152,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, /*static*/ already_AddRefed<Response> Response::Constructor(const GlobalObject& aGlobal, - const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody, + const Optional<Nullable<fetch::ResponseBodyInit>>& aBody, const ResponseInit& aInit, ErrorResult& aRv) { nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); @@ -225,13 +227,56 @@ Response::Constructor(const GlobalObject& aGlobal, nsCOMPtr<nsIInputStream> bodyStream; nsCString contentType; uint64_t bodySize = 0; - aRv = ExtractByteStreamFromBody(aBody.Value().Value(), - getter_AddRefs(bodyStream), - contentType, - bodySize); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; + + if (aBody.Value().IsReadableStream()) { + const ReadableStream& readableStream = + aBody.Value().GetAsReadableStream(); + + JS::Rooted<JSObject*> readableStreamObj(aGlobal.Context(), + readableStream.Obj()); + + if (JS::ReadableStreamIsDisturbed(readableStreamObj) || + JS::ReadableStreamIsLocked(readableStreamObj) || + !JS::ReadableStreamIsReadable(readableStreamObj)) { + aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); + return nullptr; + } + + 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; + } + + 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 { + aRv = ExtractByteStreamFromBody(aBody.Value().Value(), + getter_AddRefs(bodyStream), + contentType, + bodySize); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } } + internalResponse->SetBody(bodyStream, bodySize); if (!contentType.IsVoid() && @@ -252,7 +297,7 @@ Response::Constructor(const GlobalObject& aGlobal, } already_AddRefed<Response> -Response::Clone(ErrorResult& aRv) const +Response::Clone(JSContext* aCx, ErrorResult& aRv) const { if (BodyUsed()) { aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); @@ -261,11 +306,26 @@ Response::Clone(ErrorResult& aRv) const RefPtr<InternalResponse> ir = mInternalResponse->Clone(); RefPtr<Response> response = new Response(mOwner, ir, mSignal); + + JS::Rooted<JSObject*> body(aCx); + MaybeTeeReadableStreamBody(aCx, &body, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return 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); + } + return response.forget(); } already_AddRefed<Response> -Response::CloneUnfiltered(ErrorResult& aRv) const +Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const { if (BodyUsed()) { aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); @@ -275,6 +335,21 @@ Response::CloneUnfiltered(ErrorResult& aRv) const RefPtr<InternalResponse> clone = mInternalResponse->Clone(); RefPtr<InternalResponse> ir = clone->Unfiltered(); RefPtr<Response> ref = new Response(mOwner, ir, mSignal); + + JS::Rooted<JSObject*> body(aCx); + MaybeTeeReadableStreamBody(aCx, &body, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return 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); + } + return ref.forget(); } |