зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1128959 - Implement the WHATWG Streams spec - part 4 - Fetch implementation, r=bkelly
This commit is contained in:
Родитель
99819a81c8
Коммит
3b86092f16
|
@ -740,10 +740,12 @@ DOMInterfaces = {
|
|||
'headers': 'headers_',
|
||||
'referrerPolicy': 'referrerPolicy_'
|
||||
},
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
|
||||
},
|
||||
|
||||
'Response': {
|
||||
'binaryNames': { 'headers': 'headers_' },
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
|
||||
},
|
||||
|
||||
'RGBColor': {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Fetch.h"
|
||||
#include "FetchConsumer.h"
|
||||
#include "FetchStream.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/BodyUtil.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/FetchDriver.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FormData.h"
|
||||
|
@ -841,6 +843,7 @@ template <class Derived>
|
|||
FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
|
||||
: mOwner(aOwner)
|
||||
, mWorkerPrivate(nullptr)
|
||||
, mReadableStreamBody(nullptr)
|
||||
, mBodyUsed(false)
|
||||
{
|
||||
MOZ_ASSERT(aOwner);
|
||||
|
@ -867,9 +870,45 @@ FetchBody<Derived>::~FetchBody()
|
|||
{
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
bool
|
||||
FetchBody<Derived>::BodyUsed() const
|
||||
{
|
||||
if (mBodyUsed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this object is disturbed or locked, return false.
|
||||
if (mReadableStreamBody) {
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mOwner)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
|
||||
if (JS::ReadableStreamIsDisturbed(body) ||
|
||||
JS::ReadableStreamIsLocked(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template
|
||||
bool
|
||||
FetchBody<Request>::BodyUsed() const;
|
||||
|
||||
template
|
||||
bool
|
||||
FetchBody<Response>::BodyUsed() const;
|
||||
|
||||
template <class Derived>
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
|
||||
FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
|
@ -878,10 +917,17 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
|
|||
|
||||
SetBodyUsed();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
|
||||
|
||||
// If we already created a ReadableStreamBody we have to close it now.
|
||||
if (mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
|
||||
JS::ReadableStreamClose(aCx, body);
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise =
|
||||
FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(),
|
||||
mMainThreadEventTarget, this, aType,
|
||||
aRv);
|
||||
FetchBodyConsumer<Derived>::Create(global, mMainThreadEventTarget, this,
|
||||
aType, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -891,11 +937,13 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
|
|||
|
||||
template
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
FetchBody<Request>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
FetchBody<Response>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
|
@ -928,20 +976,52 @@ FetchBody<Response>::SetMimeType();
|
|||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage)
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// TODO
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
DerivedClass()->GetBody(getter_AddRefs(inputStream));
|
||||
|
||||
if (!inputStream) {
|
||||
aBodyOut.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx,
|
||||
FetchStream::Create(aCx,
|
||||
DerivedClass()->GetParentObject(),
|
||||
inputStream,
|
||||
aRv));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(body);
|
||||
|
||||
// If the body has been already consumed, we close the stream.
|
||||
if (BodyUsed() && !JS::ReadableStreamClose(aCx, body)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
mReadableStreamBody = body;
|
||||
}
|
||||
|
||||
aBodyOut.set(mReadableStreamBody);
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage);
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage);
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -123,41 +123,42 @@ public:
|
|||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
bool
|
||||
BodyUsed() const { return mBodyUsed; }
|
||||
BodyUsed() const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ArrayBuffer(ErrorResult& aRv)
|
||||
ArrayBuffer(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
|
||||
return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Blob(ErrorResult& aRv)
|
||||
Blob(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_BLOB, aRv);
|
||||
return ConsumeBody(aCx, CONSUME_BLOB, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FormData(ErrorResult& aRv)
|
||||
FormData(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_FORMDATA, aRv);
|
||||
return ConsumeBody(aCx, CONSUME_FORMDATA, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Json(ErrorResult& aRv)
|
||||
Json(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_JSON, aRv);
|
||||
return ConsumeBody(aCx, CONSUME_JSON, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Text(ErrorResult& aRv)
|
||||
Text(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_TEXT, aRv);
|
||||
return ConsumeBody(aCx, CONSUME_TEXT, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage);
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Utility public methods accessed by various runnables.
|
||||
|
||||
|
@ -179,6 +180,10 @@ protected:
|
|||
// Always set whenever the FetchBody is created on the worker thread.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// This is the ReadableStream exposed to content. It's underlying source is a
|
||||
// FetchStream object.
|
||||
JS::Heap<JSObject*> mReadableStreamBody;
|
||||
|
||||
explicit FetchBody(nsIGlobalObject* aOwner);
|
||||
|
||||
virtual ~FetchBody();
|
||||
|
@ -194,7 +199,7 @@ private:
|
|||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
IsOnTargetThread()
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
/* -*- 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 "FetchStream.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
#define FETCH_STREAM_FLAG 0
|
||||
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID,
|
||||
NS_STREAMTRANSPORTSERVICE_CID);
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback)
|
||||
|
||||
/* static */ JSObject*
|
||||
FetchStream::Create(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
nsIInputStream* aInputStream, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCx);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream = new FetchStream(aGlobal, aInputStream);
|
||||
|
||||
if (!JS::HasReadableStreamCallbacks(aCx)) {
|
||||
JS::SetReadableStreamCallbacks(aCx,
|
||||
&FetchStream::RequestDataCallback,
|
||||
&FetchStream::WriteIntoReadRequestCallback,
|
||||
&FetchStream::CancelCallback,
|
||||
&FetchStream::ClosedCallback,
|
||||
&FetchStream::ErroredCallback,
|
||||
&FetchStream::FinalizeCallback);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> body(aCx,
|
||||
JS::NewReadableExternalSourceStreamObject(aCx, stream, FETCH_STREAM_FLAG));
|
||||
if (!body) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->mReadableStream = body;
|
||||
|
||||
// JS engine will call the finalize callback.
|
||||
NS_ADDREF(stream.get());
|
||||
return body;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::RequestDataCallback(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
void* aUnderlyingSource,
|
||||
uint8_t aFlags,
|
||||
size_t aDesiredSize)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
MOZ_DIAGNOSTIC_ASSERT(JS::ReadableStreamIsDisturbed(aStream));
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWaiting ||
|
||||
stream->mState == eChecking);
|
||||
|
||||
if (stream->mState == eChecking) {
|
||||
// If we are looking for more data, there is nothing else we should do:
|
||||
// let's move this checking operation in a reading.
|
||||
MOZ_ASSERT(stream->mInputStream);
|
||||
stream->mState = eReading;
|
||||
return;
|
||||
}
|
||||
|
||||
stream->mState = eReading;
|
||||
|
||||
if (!stream->mInputStream) {
|
||||
// This is the first use of the stream. Let's convert the
|
||||
// mOriginalInputStream into an nsIAsyncInputStream.
|
||||
MOZ_ASSERT(stream->mOriginalInputStream);
|
||||
|
||||
bool nonBlocking = false;
|
||||
nsresult rv = stream->mOriginalInputStream->IsNonBlocking(&nonBlocking);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncStream =
|
||||
do_QueryInterface(stream->mOriginalInputStream);
|
||||
if (!nonBlocking || !asyncStream) {
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransport> transport;
|
||||
rv = sts->CreateInputTransport(stream->mOriginalInputStream,
|
||||
/* aStartOffset */ 0,
|
||||
/* aReadLimit */ -1,
|
||||
/* aCloseWhenDone */ true,
|
||||
getter_AddRefs(transport));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapper;
|
||||
rv = transport->OpenInputStream(/* aFlags */ 0,
|
||||
/* aSegmentSize */ 0,
|
||||
/* aSegmentCount */ 0,
|
||||
getter_AddRefs(wrapper));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
asyncStream = do_QueryInterface(wrapper);
|
||||
}
|
||||
|
||||
stream->mInputStream = asyncStream;
|
||||
stream->mOriginalInputStream = nullptr;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream);
|
||||
|
||||
nsresult rv =
|
||||
stream->mInputStream->AsyncWait(stream, 0, 0,
|
||||
stream->mGlobal->EventTargetFor(TaskCategory::Other));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// All good.
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::WriteIntoReadRequestCallback(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
void* aUnderlyingSource,
|
||||
uint8_t aFlags, void* aBuffer,
|
||||
size_t aLength, size_t* aByteWritten)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aBuffer);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aByteWritten);
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWriting);
|
||||
stream->mState = eChecking;
|
||||
|
||||
uint32_t written;
|
||||
nsresult rv =
|
||||
stream->mInputStream->Read(static_cast<char*>(aBuffer), aLength, &written);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
*aByteWritten = written;
|
||||
|
||||
if (written == 0) {
|
||||
stream->mState = eClosed;
|
||||
JS::ReadableStreamClose(aCx, aStream);
|
||||
return;
|
||||
}
|
||||
|
||||
rv = stream->mInputStream->AsyncWait(stream, 0, 0,
|
||||
stream->mGlobal->EventTargetFor(TaskCategory::Other));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// All good.
|
||||
}
|
||||
|
||||
/* static */ JS::Value
|
||||
FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
if (stream->mInputStream) {
|
||||
stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
stream->mState = eClosed;
|
||||
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource));
|
||||
|
||||
stream->mState = eClosed;
|
||||
stream->mReadableStream = nullptr;
|
||||
}
|
||||
|
||||
FetchStream::FetchStream(nsIGlobalObject* aGlobal,
|
||||
nsIInputStream* aInputStream)
|
||||
: mState(eWaiting)
|
||||
, mGlobal(aGlobal)
|
||||
, mOriginalInputStream(aInputStream)
|
||||
, mOwningEventTarget(mGlobal->EventTargetFor(TaskCategory::Other))
|
||||
, mReadableStream(nullptr)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
}
|
||||
|
||||
FetchStream::~FetchStream()
|
||||
{
|
||||
NS_ProxyRelease("FetchStream::mGlobal", mOwningEventTarget, mGlobal.forget());
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::ErrorPropagation(JSContext* aCx, JS::HandleObject aStream,
|
||||
nsresult aError)
|
||||
{
|
||||
// Nothing to do.
|
||||
if (mState == eClosed) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
||||
|
||||
// Let's use a generic error.
|
||||
RefPtr<DOMError> error = new DOMError(window, NS_ERROR_DOM_TYPE_ERR);
|
||||
|
||||
JS::Rooted<JS::Value> errorValue(aCx);
|
||||
if (ToJSValue(aCx, error, &errorValue)) {
|
||||
JS::ReadableStreamError(aCx, aStream, errorValue);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStream);
|
||||
|
||||
// Already closed. We have nothing else to do here.
|
||||
if (mState == eClosed) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
// Without JSContext we are not able to close the stream or to propagate the
|
||||
// error.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mReadableStream);
|
||||
|
||||
uint64_t size = 0;
|
||||
nsresult rv = mInputStream->Available(&size);
|
||||
if (NS_SUCCEEDED(rv) && size == 0) {
|
||||
// In theory this should not happen. If size is 0, the stream should be
|
||||
// considered closed.
|
||||
rv = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
// No warning for stream closed.
|
||||
if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ErrorPropagation(cx, stream, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This extra checking is completed. Let's wait for the next read request.
|
||||
if (mState == eChecking) {
|
||||
mState = eWaiting;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mState = eWriting;
|
||||
JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size);
|
||||
|
||||
// The WriteInto callback changes mState to eChecking.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == eChecking);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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_FetchStream_h
|
||||
#define mozilla_dom_FetchStream_h
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchStream final : public nsIInputStreamCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, nsIGlobalObject* aGlobal,
|
||||
nsIInputStream* aInputStream, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
FetchStream(nsIGlobalObject* aGlobal, nsIInputStream* aInputStream);
|
||||
~FetchStream();
|
||||
|
||||
static void
|
||||
RequestDataCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
size_t aDesiredSize);
|
||||
|
||||
static void
|
||||
WriteIntoReadRequestCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
void* aBuffer, size_t aLength,
|
||||
size_t* aByteWritten);
|
||||
|
||||
static JS::Value
|
||||
CancelCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason);
|
||||
|
||||
static void
|
||||
ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags);
|
||||
|
||||
static void
|
||||
ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue reason);
|
||||
|
||||
static void
|
||||
FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags);
|
||||
|
||||
void
|
||||
ErrorPropagation(JSContext* aCx, JS::HandleObject aStream, nsresult aRv);
|
||||
|
||||
// Common methods
|
||||
|
||||
enum State {
|
||||
// RequestDataCallback has not been called yet. We haven't started to read
|
||||
// data from the stream yet.
|
||||
eWaiting,
|
||||
|
||||
// We are reading data in a separate I/O thread.
|
||||
eReading,
|
||||
|
||||
// We are ready to write something in the JS Buffer.
|
||||
eWriting,
|
||||
|
||||
// After a writing, we want to check if the stream is closed. After the
|
||||
// check, we go back to eWaiting. If a reading request happens in the
|
||||
// meantime, we move to eReading state.
|
||||
eChecking,
|
||||
|
||||
// Operation completed.
|
||||
eClosed,
|
||||
};
|
||||
|
||||
// Touched only on the target thread.
|
||||
State mState;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
|
||||
// This is the original inputStream received during the CTOR. It will be
|
||||
// converted into an nsIAsyncInputStream and stored into mInputStream at the
|
||||
// first use.
|
||||
nsCOMPtr<nsIInputStream> mOriginalInputStream;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
JS::Heap<JSObject*> mReadableStream;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_FetchStream_h
|
|
@ -25,7 +25,24 @@ namespace dom {
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Request)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
|
|
|
@ -26,7 +26,27 @@ namespace dom {
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Response)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
|
||||
|
||||
tmp->mReadableStreamBody = nullptr;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
|
||||
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_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
|
@ -40,10 +60,13 @@ Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse
|
|||
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
|
||||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
|
||||
SetMimeType();
|
||||
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
Response::~Response()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Response>
|
||||
|
|
|
@ -34,6 +34,7 @@ UNIFIED_SOURCES += [
|
|||
'FetchDriver.cpp',
|
||||
'FetchObserver.cpp',
|
||||
'FetchSignal.cpp',
|
||||
'FetchStream.cpp',
|
||||
'FetchUtil.cpp',
|
||||
'Headers.cpp',
|
||||
'InternalHeaders.cpp',
|
||||
|
|
|
@ -33,6 +33,7 @@ Response implements Body;
|
|||
// This should be part of Body but we don't want to expose body to request yet.
|
||||
// See bug 1387483.
|
||||
partial interface Response {
|
||||
[GetterThrows]
|
||||
readonly attribute ReadableStream? body;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче