зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1128959 - Implement the WHATWG Streams spec - part 7 - Response.body, r=bkelly
This commit is contained in:
Родитель
4c6f9d7816
Коммит
3a14f6c21f
|
@ -745,7 +745,8 @@ DOMInterfaces = {
|
|||
|
||||
'Response': {
|
||||
'binaryNames': { 'headers': 'headers_' },
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text',
|
||||
'clone', 'cloneUnfiltered' ],
|
||||
},
|
||||
|
||||
'RGBColor': {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMSerializer.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIStorageStream.h"
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
#ifndef mozilla_dom_BodyExtractor_h
|
||||
#define mozilla_dom_BodyExtractor_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -25,7 +27,7 @@ public:
|
|||
|
||||
// The implementation versions of this template are:
|
||||
// ArrayBuffer, ArrayBufferView, nsIXHRSendable (Blob, FormData,
|
||||
// URLSearchParams), nsAString, nsIDocument, nsIInputStream
|
||||
// URLSearchParams), nsAString, nsIDocument, nsIInputStream.
|
||||
template<typename Type>
|
||||
class BodyExtractor final : public BodyExtractorBase
|
||||
{
|
||||
|
|
|
@ -839,6 +839,62 @@ ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentTypeWithCharset,
|
||||
uint64_t& aContentLength)
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
MOZ_ASSERT(!*aStream);
|
||||
|
||||
// ReadableStreams should be handled by
|
||||
// BodyExtractorReadableStream::GetAsStream.
|
||||
MOZ_ASSERT(!aBodyInit.IsReadableStream());
|
||||
|
||||
nsAutoCString charset;
|
||||
aContentTypeWithCharset.SetIsVoid(true);
|
||||
|
||||
if (aBodyInit.IsArrayBuffer()) {
|
||||
BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
if (aBodyInit.IsArrayBufferView()) {
|
||||
BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
if (aBodyInit.IsBlob()) {
|
||||
BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsBlob());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
if (aBodyInit.IsFormData()) {
|
||||
BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsFormData());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
if (aBodyInit.IsUSVString()) {
|
||||
BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
if (aBodyInit.IsURLSearchParams()) {
|
||||
BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsURLSearchParams());
|
||||
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
|
||||
charset);
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Should never reach here");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
|
||||
: mOwner(aOwner)
|
||||
|
@ -889,7 +945,8 @@ FetchBody<Derived>::BodyUsed() const
|
|||
|
||||
JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
|
||||
if (JS::ReadableStreamIsDisturbed(body) ||
|
||||
JS::ReadableStreamIsLocked(body)) {
|
||||
JS::ReadableStreamIsLocked(body) ||
|
||||
!JS::ReadableStreamIsReadable(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -917,14 +974,18 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
|
|||
|
||||
SetBodyUsed();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
|
||||
|
||||
// If we already created a ReadableStreamBody we have to close it now.
|
||||
// If we already created a ReadableStreamBody we have to lock it now because
|
||||
// it can have been shared with other objects.
|
||||
if (mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
|
||||
JS::ReadableStreamClose(aCx, body);
|
||||
LockStream(aCx, body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
|
||||
|
||||
RefPtr<Promise> promise =
|
||||
FetchBodyConsumer<Derived>::Create(global, mMainThreadEventTarget, this,
|
||||
aType, aRv);
|
||||
|
@ -973,6 +1034,23 @@ template
|
|||
void
|
||||
FetchBody<Response>::SetMimeType();
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::SetReadableStreamBody(JSObject* aBody)
|
||||
{
|
||||
MOZ_ASSERT(!mReadableStreamBody);
|
||||
MOZ_ASSERT(aBody);
|
||||
mReadableStreamBody = aBody;
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::GetBody(JSContext* aCx,
|
||||
|
@ -990,6 +1068,7 @@ FetchBody<Derived>::GetBody(JSContext* aCx,
|
|||
if (!mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx,
|
||||
FetchStream::Create(aCx,
|
||||
this,
|
||||
DerivedClass()->GetParentObject(),
|
||||
inputStream,
|
||||
aRv));
|
||||
|
@ -1000,9 +1079,11 @@ FetchBody<Derived>::GetBody(JSContext* aCx,
|
|||
MOZ_ASSERT(body);
|
||||
|
||||
// If the body has been already consumed, we close the stream.
|
||||
if (BodyUsed() && !JS::ReadableStreamClose(aCx, body)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
if (BodyUsed()) {
|
||||
LockStream(aCx, body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mReadableStreamBody = body;
|
||||
|
@ -1023,6 +1104,72 @@ FetchBody<Response>::GetBody(JSContext* aCx,
|
|||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// TODO: next patch.
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
|
||||
|
||||
if (!mReadableStreamBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> stream(aCx, mReadableStreamBody);
|
||||
|
||||
// If this is a ReadableStream with an external source, this has been
|
||||
// generated by a Fetch. In this case, Fetch will be able to recreate it
|
||||
// again when GetBody() is called.
|
||||
if (JS::ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource) {
|
||||
aBodyOut.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> branch1(aCx);
|
||||
JS::Rooted<JSObject*> branch2(aCx);
|
||||
|
||||
if (!JS::ReadableStreamTee(aCx, stream, &branch1, &branch2)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
mReadableStreamBody = branch1;
|
||||
aBodyOut.set(branch2);
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -27,9 +27,11 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
|
||||
class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
|
||||
class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString;
|
||||
class BlobImpl;
|
||||
class InternalRequest;
|
||||
class OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
|
||||
struct ReadableStream;
|
||||
class RequestOrUSVString;
|
||||
enum class CallerType : uint32_t;
|
||||
|
||||
|
@ -47,6 +49,7 @@ UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
|
|||
|
||||
namespace fetch {
|
||||
typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString BodyInit;
|
||||
typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString ResponseBodyInit;
|
||||
typedef OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString OwningBodyInit;
|
||||
};
|
||||
|
||||
|
@ -70,6 +73,16 @@ ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
|
|||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
/*
|
||||
* Non-owning version. This method should go away when BodyInit will contain
|
||||
* ReadableStream.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
template <class Derived> class FetchBodyConsumer;
|
||||
|
||||
enum FetchConsumeType
|
||||
|
@ -81,6 +94,15 @@ enum FetchConsumeType
|
|||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
class FetchStreamHolder
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
virtual void
|
||||
NullifyStream() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* FetchBody's body consumption uses nsIInputStreamPump to read from the
|
||||
* underlying stream to a block of memory, which is then adopted by
|
||||
|
@ -115,13 +137,11 @@ enum FetchConsumeType
|
|||
* The pump is always released on the main thread.
|
||||
*/
|
||||
template <class Derived>
|
||||
class FetchBody
|
||||
class FetchBody : public FetchStreamHolder
|
||||
{
|
||||
public:
|
||||
friend class FetchBodyConsumer<Derived>;
|
||||
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
bool
|
||||
BodyUsed() const;
|
||||
|
||||
|
@ -160,6 +180,13 @@ public:
|
|||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// If the body contains a ReadableStream body object, this method produces a
|
||||
// tee() of it.
|
||||
void
|
||||
MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Utility public methods accessed by various runnables.
|
||||
|
||||
void
|
||||
|
@ -174,6 +201,12 @@ public:
|
|||
return mMimeType;
|
||||
}
|
||||
|
||||
void
|
||||
NullifyStream() override
|
||||
{
|
||||
mReadableStreamBody = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
|
||||
|
@ -191,6 +224,9 @@ protected:
|
|||
void
|
||||
SetMimeType();
|
||||
|
||||
void
|
||||
SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
private:
|
||||
Derived*
|
||||
DerivedClass() const
|
||||
|
@ -201,6 +237,9 @@ private:
|
|||
already_AddRefed<Promise>
|
||||
ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
IsOnTargetThread()
|
||||
{
|
||||
|
|
|
@ -57,10 +57,12 @@ class FetchStreamWorkerHolderShutdown final : public WorkerControlRunnable
|
|||
public:
|
||||
FetchStreamWorkerHolderShutdown(WorkerPrivate* aWorkerPrivate,
|
||||
UniquePtr<WorkerHolder>&& aHolder,
|
||||
nsCOMPtr<nsIGlobalObject>&& aGlobal)
|
||||
: WorkerControlRunnable(aWorkerPrivate)
|
||||
nsCOMPtr<nsIGlobalObject>&& aGlobal,
|
||||
RefPtr<FetchStreamHolder>&& aStreamHolder)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mHolder(Move(aHolder))
|
||||
, mGlobal(Move(aGlobal))
|
||||
, mStreamHolder(Move(aStreamHolder))
|
||||
{}
|
||||
|
||||
bool
|
||||
|
@ -68,12 +70,30 @@ public:
|
|||
{
|
||||
mHolder = nullptr;
|
||||
mGlobal = nullptr;
|
||||
|
||||
mStreamHolder->NullifyStream();
|
||||
mStreamHolder = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This runnable starts from a JS Thread. We need to disable a couple of
|
||||
// assertions overring the following methods.
|
||||
|
||||
bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{}
|
||||
|
||||
private:
|
||||
UniquePtr<WorkerHolder> mHolder;
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<FetchStreamHolder> mStreamHolder;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
@ -82,13 +102,16 @@ NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver,
|
|||
nsISupportsWeakReference)
|
||||
|
||||
/* static */ JSObject*
|
||||
FetchStream::Create(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
nsIInputStream* aInputStream, ErrorResult& aRv)
|
||||
FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCx);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream = new FetchStream(aGlobal, aInputStream);
|
||||
RefPtr<FetchStream> stream =
|
||||
new FetchStream(aGlobal, aStreamHolder, aInputStream);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
|
@ -224,7 +247,7 @@ FetchStream::RequestDataCallback(JSContext* aCx,
|
|||
|
||||
nsresult rv =
|
||||
stream->mInputStream->AsyncWait(stream, 0, 0,
|
||||
stream->mGlobal->EventTargetFor(TaskCategory::Other));
|
||||
stream->mOwningEventTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
|
@ -262,13 +285,12 @@ FetchStream::WriteIntoReadRequestCallback(JSContext* aCx,
|
|||
*aByteWritten = written;
|
||||
|
||||
if (written == 0) {
|
||||
stream->mState = eClosed;
|
||||
JS::ReadableStreamClose(aCx, aStream);
|
||||
stream->CloseAndReleaseObjects(aCx, aStream);
|
||||
return;
|
||||
}
|
||||
|
||||
rv = stream->mInputStream->AsyncWait(stream, 0, 0,
|
||||
stream->mGlobal->EventTargetFor(TaskCategory::Other));
|
||||
stream->mOwningEventTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
|
@ -291,8 +313,7 @@ FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream,
|
|||
stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
stream->mState = eClosed;
|
||||
|
||||
stream->ReleaseObjects();
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
|
@ -324,22 +345,21 @@ FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags)
|
|||
RefPtr<FetchStream> stream =
|
||||
dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource));
|
||||
|
||||
if (stream->mState == eClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream->CloseAndReleaseObjects();
|
||||
stream->ReleaseObjects();
|
||||
}
|
||||
|
||||
FetchStream::FetchStream(nsIGlobalObject* aGlobal,
|
||||
FetchStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream)
|
||||
: mState(eWaiting)
|
||||
, mGlobal(aGlobal)
|
||||
, mStreamHolder(aStreamHolder)
|
||||
, mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other))
|
||||
, mOriginalInputStream(aInputStream)
|
||||
, mOwningEventTarget(mGlobal->EventTargetFor(TaskCategory::Other))
|
||||
, mReadableStream(nullptr)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
}
|
||||
|
||||
FetchStream::~FetchStream()
|
||||
|
@ -355,12 +375,9 @@ FetchStream::ErrorPropagation(JSContext* aCx, JS::HandleObject aStream,
|
|||
return;
|
||||
}
|
||||
|
||||
// We cannot continue with any other operation.
|
||||
mState = eClosed;
|
||||
|
||||
// Let's close the stream.
|
||||
if (aError == NS_BASE_STREAM_CLOSED) {
|
||||
JS::ReadableStreamClose(aCx, aStream);
|
||||
CloseAndReleaseObjects(aCx, aStream);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -373,6 +390,8 @@ FetchStream::ErrorPropagation(JSContext* aCx, JS::HandleObject aStream,
|
|||
if (ToJSValue(aCx, error, &errorValue)) {
|
||||
JS::ReadableStreamError(aCx, aStream, errorValue);
|
||||
}
|
||||
|
||||
ReleaseObjects();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -427,6 +446,27 @@ FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
FetchStream::RetrieveInputStream(void* aUnderlyingReadableStreamSource,
|
||||
nsIInputStream** aInputStream)
|
||||
{
|
||||
MOZ_ASSERT(aUnderlyingReadableStreamSource);
|
||||
MOZ_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
static_cast<FetchStream*>(aUnderlyingReadableStreamSource);
|
||||
|
||||
// if mOriginalInputStream is null, the reading already started. We don't want
|
||||
// to expose the internal inputStream.
|
||||
if (NS_WARN_IF(!stream->mOriginalInputStream)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream = stream->mOriginalInputStream;
|
||||
inputStream.forget(aInputStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::Close()
|
||||
{
|
||||
|
@ -436,42 +476,58 @@ FetchStream::Close()
|
|||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
ReleaseObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mReadableStream);
|
||||
JS::ReadableStreamClose(cx, stream);
|
||||
|
||||
CloseAndReleaseObjects();
|
||||
CloseAndReleaseObjects(cx, stream);
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::CloseAndReleaseObjects()
|
||||
FetchStream::CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aStream)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState != eClosed);
|
||||
|
||||
ReleaseObjects();
|
||||
|
||||
if (JS::ReadableStreamIsReadable(aStream)) {
|
||||
JS::ReadableStreamClose(aCx, aStream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::ReleaseObjects()
|
||||
{
|
||||
if (mState == eClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = eClosed;
|
||||
|
||||
if (mWorkerHolder) {
|
||||
RefPtr<FetchStreamWorkerHolderShutdown> r =
|
||||
new FetchStreamWorkerHolderShutdown(
|
||||
static_cast<FetchStreamWorkerHolder*>(mWorkerHolder.get())->GetWorkerPrivate(),
|
||||
Move(mWorkerHolder), Move(mGlobal));
|
||||
Move(mWorkerHolder), Move(mGlobal), Move(mStreamHolder));
|
||||
r->Dispatch();
|
||||
} else {
|
||||
RefPtr<FetchStream> self = this;
|
||||
RefPtr<Runnable> r = NS_NewRunnableFunction(
|
||||
"FetchStream::Finalize",
|
||||
"FetchStream::ReleaseObjects",
|
||||
[self] () {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
os->RemoveObserver(self, DOM_WINDOW_DESTROYED_TOPIC);
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(self, DOM_WINDOW_DESTROYED_TOPIC);
|
||||
}
|
||||
self->mGlobal = nullptr;
|
||||
|
||||
self->mStreamHolder->NullifyStream();
|
||||
self->mStreamHolder = nullptr;
|
||||
});
|
||||
|
||||
mGlobal->Dispatch("FetchStream::FinalizeCallback",
|
||||
TaskCategory::Other, r.forget());
|
||||
mOwningEventTarget->Dispatch(r.forget());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace workers {
|
|||
class WorkerHolder;
|
||||
}
|
||||
|
||||
class FetchStreamHolder;
|
||||
|
||||
class FetchStream final : public nsIInputStreamCallback
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
|
@ -35,14 +37,20 @@ public:
|
|||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
nsIInputStream* aInputStream, ErrorResult& aRv);
|
||||
Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Close();
|
||||
|
||||
static nsresult
|
||||
RetrieveInputStream(void* aUnderlyingReadableStreamSource,
|
||||
nsIInputStream** aInputStream);
|
||||
|
||||
private:
|
||||
FetchStream(nsIGlobalObject* aGlobal, nsIInputStream* aInputStream);
|
||||
FetchStream(nsIGlobalObject* aGlobal, FetchStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream);
|
||||
~FetchStream();
|
||||
|
||||
static void
|
||||
|
@ -77,7 +85,10 @@ private:
|
|||
ErrorPropagation(JSContext* aCx, JS::HandleObject aStream, nsresult aRv);
|
||||
|
||||
void
|
||||
CloseAndReleaseObjects();
|
||||
CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aSteam);
|
||||
|
||||
void
|
||||
ReleaseObjects();
|
||||
|
||||
// Common methods
|
||||
|
||||
|
@ -105,6 +116,8 @@ private:
|
|||
State mState;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<FetchStreamHolder> mStreamHolder;
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
// This is the original inputStream received during the CTOR. It will be
|
||||
// converted into an nsIAsyncInputStream and stored into mInputStream at the
|
||||
|
@ -112,8 +125,6 @@ private:
|
|||
nsCOMPtr<nsIInputStream> mOriginalInputStream;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
UniquePtr<workers::WorkerHolder> mWorkerHolder;
|
||||
|
||||
JS::Heap<JSObject*> mReadableStream;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/dom/InternalHeaders.h"
|
||||
#include "mozilla/dom/ResponseBinding.h"
|
||||
#include "mozilla/dom/ChannelInfo.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
|
|
@ -8,16 +8,20 @@
|
|||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#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 "BodyExtractor.h"
|
||||
#include "FetchStream.h"
|
||||
#include "InternalResponse.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
|
@ -126,7 +130,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<fetch::BodyInit> body;
|
||||
Optional<fetch::ResponseBodyInit> body;
|
||||
ResponseInit init;
|
||||
init.mStatus = aStatus;
|
||||
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
|
||||
|
@ -147,7 +151,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
|||
|
||||
/*static*/ already_AddRefed<Response>
|
||||
Response::Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<fetch::BodyInit>& aBody,
|
||||
const Optional<fetch::ResponseBodyInit>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
@ -219,16 +223,60 @@ Response::Constructor(const GlobalObject& aGlobal,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
nsCString contentTypeWithCharset;
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
uint64_t bodySize = 0;
|
||||
aRv = ExtractByteStreamFromBody(aBody.Value(),
|
||||
getter_AddRefs(bodyStream),
|
||||
contentTypeWithCharset,
|
||||
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);
|
||||
|
||||
// TODO: see next patches
|
||||
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));
|
||||
|
||||
// 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 {
|
||||
aRv = ExtractByteStreamFromBody(aBody.Value(),
|
||||
getter_AddRefs(bodyStream),
|
||||
contentTypeWithCharset,
|
||||
bodySize);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
internalResponse->SetBody(bodyStream, bodySize);
|
||||
|
||||
if (!contentTypeWithCharset.IsVoid() &&
|
||||
|
@ -251,7 +299,7 @@ Response::Constructor(const GlobalObject& aGlobal,
|
|||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Response::Clone(ErrorResult& aRv) const
|
||||
Response::Clone(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
|
@ -260,11 +308,26 @@ Response::Clone(ErrorResult& aRv) const
|
|||
|
||||
RefPtr<InternalResponse> ir = mInternalResponse->Clone();
|
||||
RefPtr<Response> response = new Response(mOwner, ir);
|
||||
|
||||
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)
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
|
@ -274,6 +337,21 @@ Response::CloneUnfiltered(ErrorResult& aRv) const
|
|||
RefPtr<InternalResponse> clone = mInternalResponse->Clone();
|
||||
RefPtr<InternalResponse> ir = clone->Unfiltered();
|
||||
RefPtr<Response> ref = new Response(mOwner, ir);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ public:
|
|||
|
||||
static already_AddRefed<Response>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<fetch::BodyInit>& aBody,
|
||||
const Optional<fetch::ResponseBodyInit>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& rv);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
|
@ -125,10 +125,10 @@ public:
|
|||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Clone(ErrorResult& aRv) const;
|
||||
Clone(JSContext* aCx, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Response>
|
||||
CloneUnfiltered(ErrorResult& aRv) const;
|
||||
CloneUnfiltered(JSContext* aCx, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aBody, int64_t aBodySize);
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
* https://fetch.spec.whatwg.org/#response-class
|
||||
*/
|
||||
|
||||
[Constructor(optional BodyInit body, optional ResponseInit init),
|
||||
// This should be Constructor(optional BodyInit... but BodyInit doesn't include
|
||||
// ReadableStream yet because we don't want to expose Streams API to Request.
|
||||
[Constructor(optional (Blob or BufferSource or FormData or URLSearchParams or ReadableStream or USVString) body, optional ResponseInit init),
|
||||
Exposed=(Window,Worker)]
|
||||
interface Response {
|
||||
[NewObject] static Response error();
|
||||
|
|
Загрузка…
Ссылка в новой задаче