diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 50cc781efc..130188ef98 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -8735,6 +8735,25 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj) return workerPrivate->PushEnabled(); } +// static +bool +nsContentUtils::StreamsEnabled(JSContext* aCx, JSObject* aObj) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.streams.enabled", false); + } + + using namespace workers; + + // Otherwise, check the pref via the WorkerPrivate + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return false; + } + + return workerPrivate->StreamsEnabled(); +} + // static bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index c0e44b3be4..c1f8885f73 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2679,6 +2679,8 @@ public: static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader); static bool PushEnabled(JSContext* aCx, JSObject* aObj); + + static bool StreamsEnabled(JSContext* aCx, JSObject* aObj); static bool IsNonSubresourceRequest(nsIChannel* aChannel); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index e1560bfa3f..39fa0d7dc9 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -722,6 +722,7 @@ DOMInterfaces = { 'headers': 'headers_', 'referrerPolicy': 'referrerPolicy_' }, + 'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ], }, 'ResizeObservation': { @@ -746,6 +747,7 @@ DOMInterfaces = { 'Response': { 'binaryNames': { 'headers': 'headers_' }, + 'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ], }, 'RGBColor': { diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 42c9fc74f9..63e2c83eaa 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -5,6 +5,7 @@ #include "Fetch.h" #include "FetchConsumer.h" +#include "FetchStream.h" #include "nsIDocument.h" #include "nsIGlobalObject.h" @@ -22,6 +23,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/BodyUtil.h" +#include "mozilla/dom/DOMError.h" #include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/FetchDriver.h" @@ -35,6 +37,7 @@ #include "mozilla/dom/Response.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/URLSearchParams.h" +#include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/workers/ServiceWorkerManager.h" #include "FetchObserver.h" @@ -918,6 +921,7 @@ ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUS template FetchBody::FetchBody() : mWorkerPrivate(nullptr) + , mReadableStreamBody(nullptr) , mBodyUsed(false) { if (!NS_IsMainThread()) { @@ -937,9 +941,45 @@ FetchBody::~FetchBody() { } +template +bool +FetchBody::BodyUsed() const +{ + if (mBodyUsed) { + return true; + } + + // If this object is disturbed or locked, return false. + if (mReadableStreamBody) { + AutoJSAPI jsapi; + // If we start tracking ownership, we need something like this. + //if (!jsapi.Init(mOwner)) { + // return true; + //} + + JSContext* cx = jsapi.cx(); + + JS::Rooted body(cx, mReadableStreamBody); + if (JS::ReadableStreamIsDisturbed(body) || + JS::ReadableStreamIsLocked(body)) { + return true; + } + } + + return false; +} + +template +bool +FetchBody::BodyUsed() const; + +template +bool +FetchBody::BodyUsed() const; + template already_AddRefed -FetchBody::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv) +FetchBody::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv) { RefPtr signal = DerivedClass()->GetSignal(); if (signal && signal->Aborted()) { @@ -954,9 +994,16 @@ FetchBody::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv) SetBodyUsed(); + nsCOMPtr global = DerivedClass()->GetParentObject(); + + // If we already created a ReadableStreamBody we have to close it now. + if (mReadableStreamBody) { + JS::Rooted body(aCx, mReadableStreamBody); + JS::ReadableStreamClose(aCx, body); + } + RefPtr promise = - FetchBodyConsumer::Create(DerivedClass()->GetParentObject(), - this, signal, aType, aRv); + FetchBodyConsumer::Create(global, this, signal, aType, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -966,11 +1013,11 @@ FetchBody::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv) template already_AddRefed -FetchBody::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); +FetchBody::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv); template already_AddRefed -FetchBody::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); +FetchBody::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv); template void @@ -1003,20 +1050,52 @@ FetchBody::SetMimeType(); template void FetchBody::GetBody(JSContext* aCx, - JS::MutableHandle aMessage) + JS::MutableHandle aBodyOut, + ErrorResult& aRv) { - // TODO + nsCOMPtr inputStream; + DerivedClass()->GetBody(getter_AddRefs(inputStream)); + + if (!inputStream) { + aBodyOut.set(nullptr); + return; + } + + if (!mReadableStreamBody) { + JS::Rooted 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::GetBody(JSContext* aCx, - JS::MutableHandle aMessage); + JS::MutableHandle aMessage, + ErrorResult& aRv); template void FetchBody::GetBody(JSContext* aCx, - JS::MutableHandle aMessage); + JS::MutableHandle aMessage, + ErrorResult& aRv); } // namespace dom diff --git a/dom/fetch/Fetch.h b/dom/fetch/Fetch.h index bc2df62689..821bc3643f 100644 --- a/dom/fetch/Fetch.h +++ b/dom/fetch/Fetch.h @@ -115,41 +115,42 @@ public: NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; bool - BodyUsed() const { return mBodyUsed; } + BodyUsed() const; already_AddRefed - ArrayBuffer(ErrorResult& aRv) + ArrayBuffer(JSContext* aCx, ErrorResult& aRv) { - return ConsumeBody(CONSUME_ARRAYBUFFER, aRv); + return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv); } already_AddRefed - Blob(ErrorResult& aRv) + Blob(JSContext* aCx, ErrorResult& aRv) { - return ConsumeBody(CONSUME_BLOB, aRv); + return ConsumeBody(aCx, CONSUME_BLOB, aRv); } already_AddRefed - FormData(ErrorResult& aRv) + FormData(JSContext* aCx, ErrorResult& aRv) { - return ConsumeBody(CONSUME_FORMDATA, aRv); + return ConsumeBody(aCx, CONSUME_FORMDATA, aRv); } already_AddRefed - Json(ErrorResult& aRv) + Json(JSContext* aCx, ErrorResult& aRv) { - return ConsumeBody(CONSUME_JSON, aRv); + return ConsumeBody(aCx, CONSUME_JSON, aRv); } already_AddRefed - 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 aMessage); + JS::MutableHandle aBodyOut, + ErrorResult& aRv); // Utility public methods accessed by various runnables. @@ -174,6 +175,9 @@ protected: // Always set whenever the FetchBody is created on the worker thread. workers::WorkerPrivate* mWorkerPrivate; + // This is the ReadableStream exposed to content. Its underlying source is a FetchStream object. + JS::Heap mReadableStreamBody; + virtual ~FetchBody(); void @@ -186,7 +190,7 @@ private: } already_AddRefed - ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); + ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv); bool IsOnTargetThread() diff --git a/dom/fetch/FetchStream.cpp b/dom/fetch/FetchStream.cpp new file mode 100644 index 0000000000..7b165b2a9d --- /dev/null +++ b/dom/fetch/FetchStream.cpp @@ -0,0 +1,336 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsNetCID.h" +#include "nsITransport.h" +#include "nsIStreamTransportService.h" +#include "nsProxyRelease.h" + +#include "mozilla/dom/DOMError.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 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 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 stream = static_cast(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 asyncStream = + do_QueryInterface(stream->mOriginalInputStream); + if (!nonBlocking || !asyncStream) { + nsCOMPtr sts = + do_GetService(kStreamTransportServiceCID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + stream->ErrorPropagation(aCx, aStream, rv); + return; + } + + nsCOMPtr 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 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, nullptr); + 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 stream = static_cast(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(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, nullptr); + 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 stream = static_cast(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 stream = + dont_AddRef(static_cast(aUnderlyingSource)); + + stream->mState = eClosed; + stream->mReadableStream = nullptr; +} + +FetchStream::FetchStream(nsIGlobalObject* aGlobal, + nsIInputStream* aInputStream) + : mState(eWaiting) + , mGlobal(aGlobal) + , mOriginalInputStream(aInputStream) + , mOwningEventTarget(nullptr) + , mReadableStream(nullptr) +{ + MOZ_DIAGNOSTIC_ASSERT(aInputStream); +} + +FetchStream::~FetchStream() +{ + NS_ProxyRelease(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 window = do_QueryInterface(mGlobal); + + // Let's use a generic error. + RefPtr error = new DOMError(window, NS_ERROR_DOM_TYPE_ERR); + + JS::Rooted 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 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 diff --git a/dom/fetch/FetchStream.h b/dom/fetch/FetchStream.h new file mode 100644 index 0000000000..ebe94e205b --- /dev/null +++ b/dom/fetch/FetchStream.h @@ -0,0 +1,107 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 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 mOriginalInputStream; + nsCOMPtr mInputStream; + + nsCOMPtr mOwningEventTarget; + + JS::Heap mReadableStream; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FetchStream_h diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index ab87a3215a..47fb62e885 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -29,7 +29,24 @@ using namespace workers; 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 diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index 42b25ae1d9..20487906ba 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -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 @@ -42,10 +62,14 @@ Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable || aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response); SetMimeType(); + + // Keep a firm grip! + mozilla::HoldJSObjects(this); } Response::~Response() { + mozilla::DropJSObjects(this); } /* static */ already_AddRefed diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build index d1a8a49323..cd41620f93 100644 --- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -24,6 +24,7 @@ UNIFIED_SOURCES += [ 'FetchConsumer.cpp', 'FetchDriver.cpp', 'FetchObserver.cpp', + 'FetchStream.cpp', 'FetchUtil.cpp', 'Headers.cpp', 'InternalHeaders.cpp', diff --git a/dom/webidl/Response.webidl b/dom/webidl/Response.webidl index adaa70d66d..d02b714f7d 100644 --- a/dom/webidl/Response.webidl +++ b/dom/webidl/Response.webidl @@ -32,6 +32,7 @@ Response implements Body; // This should be part of Body but we don't want to expose body to request yet. partial interface Response { + [GetterThrows, Func="nsContentUtils::StreamsEnabled"] readonly attribute ReadableStream? body; }; diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 215b375ddb..415435cf06 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEn WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED) WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED) WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) +WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED) WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index d1e2ddf1e2..3d6325655e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4585,6 +4585,9 @@ pref("layout.animated-image-layers.enabled", false); // Abort API pref("dom.abortController.enabled", true); +// Streams API +pref("dom.streams.enabled", false); + // Push pref("dom.push.enabled", false); pref("dom.push.loglevel", "error");