1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_Fetch_h
8 #define mozilla_dom_Fetch_h
12 #include "nsProxyRelease.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/dom/AbortSignal.h"
17 #include "mozilla/dom/BodyConsumer.h"
18 #include "mozilla/dom/BodyStream.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/FetchStreamReader.h"
21 #include "mozilla/dom/RequestBinding.h"
23 class nsIGlobalObject
;
35 class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
37 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString
;
39 class InternalRequest
;
41 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
43 class RequestOrUSVString
;
46 enum class CallerType
: uint32_t;
48 already_AddRefed
<Promise
> FetchRequest(nsIGlobalObject
* aGlobal
,
49 const RequestOrUSVString
& aInput
,
50 const RequestInit
& aInit
,
51 CallerType aCallerType
,
54 nsresult
UpdateRequestReferrer(nsIGlobalObject
* aGlobal
,
55 InternalRequest
* aRequest
);
59 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
60 using ResponseBodyInit
=
61 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString
;
62 using OwningBodyInit
=
63 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
67 * Creates an nsIInputStream based on the fetch specifications 'extract a byte
68 * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
69 * Stores content type in out param aContentType.
71 nsresult
ExtractByteStreamFromBody(const fetch::OwningBodyInit
& aBodyInit
,
72 nsIInputStream
** aStream
,
73 nsCString
& aContentType
,
74 uint64_t& aContentLength
);
79 nsresult
ExtractByteStreamFromBody(const fetch::BodyInit
& aBodyInit
,
80 nsIInputStream
** aStream
,
81 nsCString
& aContentType
,
82 uint64_t& aContentLength
);
85 * Non-owning version. This method should go away when BodyInit will contain
88 nsresult
ExtractByteStreamFromBody(const fetch::ResponseBodyInit
& aBodyInit
,
89 nsIInputStream
** aStream
,
90 nsCString
& aContentType
,
91 uint64_t& aContentLength
);
94 * FetchBody's body consumption uses nsIInputStreamPump to read from the
95 * underlying stream to a block of memory, which is then adopted by
96 * ContinueConsumeBody() and converted to the right type based on the JS
99 * Use of the nsIInputStreamPump complicates things on the worker thread.
100 * The solution used here is similar to WebSockets.
101 * The difference is that we are only interested in completion and not data
102 * events, and nsIInputStreamPump can only deliver completion on the main
105 * Before starting the pump on the main thread, we addref the FetchBody to keep
106 * it alive. Then we add a feature, to track the status of the worker.
108 * ContinueConsumeBody() is the function that cleans things up in both success
109 * and error conditions and so all callers call it with the appropriate status.
111 * Once the read is initiated on the main thread there are two possibilities.
113 * 1) Pump finishes before worker has finished Running.
114 * In this case we adopt the data and dispatch a runnable to the worker,
115 * which derefs FetchBody and removes the feature and resolves the Promise.
117 * 2) Pump still working while worker has stopped Running.
118 * The feature is Notify()ed and ContinueConsumeBody() is called with
119 * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
120 * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
121 * held by it) until pump->Cancel() is called. OnStreamComplete() will not
122 * do anything if the error code is NS_BINDING_ABORTED, so we don't have to
123 * worry about keeping anything alive.
125 * The pump is always released on the main thread.
127 template <class Derived
>
128 class FetchBody
: public BodyStreamHolder
, public AbortFollower
{
130 using BodyStreamHolder::QueryInterface
;
132 NS_INLINE_DECL_REFCOUNTING_INHERITED(FetchBody
, BodyStreamHolder
)
134 bool GetBodyUsed(ErrorResult
& aRv
) const;
136 // For use in assertions. On success, returns true if the body is used, false
137 // if not. On error, this sweeps the error under the rug and returns true.
138 bool CheckBodyUsed() const;
140 already_AddRefed
<Promise
> ArrayBuffer(JSContext
* aCx
, ErrorResult
& aRv
) {
141 return ConsumeBody(aCx
, BodyConsumer::CONSUME_ARRAYBUFFER
, aRv
);
144 already_AddRefed
<Promise
> Blob(JSContext
* aCx
, ErrorResult
& aRv
) {
145 return ConsumeBody(aCx
, BodyConsumer::CONSUME_BLOB
, aRv
);
148 already_AddRefed
<Promise
> FormData(JSContext
* aCx
, ErrorResult
& aRv
) {
149 return ConsumeBody(aCx
, BodyConsumer::CONSUME_FORMDATA
, aRv
);
152 already_AddRefed
<Promise
> Json(JSContext
* aCx
, ErrorResult
& aRv
) {
153 return ConsumeBody(aCx
, BodyConsumer::CONSUME_JSON
, aRv
);
156 already_AddRefed
<Promise
> Text(JSContext
* aCx
, ErrorResult
& aRv
) {
157 return ConsumeBody(aCx
, BodyConsumer::CONSUME_TEXT
, aRv
);
160 #ifdef MOZ_DOM_STREAMS
161 already_AddRefed
<ReadableStream
> GetBody(ErrorResult
& aRv
) {
162 MOZ_CRASH("MOZ_DOM_STREAMS:NYI");
165 void GetBody(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aBodyOut
,
168 void GetMimeType(nsACString
& aMimeType
);
170 const nsACString
& BodyBlobURISpec() const;
172 const nsAString
& BodyLocalPath() const;
174 // If the body contains a ReadableStream body object, this method produces a
176 void MaybeTeeReadableStreamBody(JSContext
* aCx
,
177 JS::MutableHandle
<JSObject
*> aBodyOut
,
178 FetchStreamReader
** aStreamReader
,
179 nsIInputStream
** aInputStream
,
182 // Utility public methods accessed by various runnables.
184 // This method _must_ be called in order to set the body as used. If the body
185 // is a ReadableStream, this method will start reading the stream.
186 // More in details, this method does:
187 // 1) It uses an internal flag to track if the body is used. This is tracked
188 // separately from the ReadableStream disturbed state due to purely native
190 // 2) If there is a ReadableStream reflector for the native stream it is
192 // 3) If there is a JS ReadableStream then we begin pumping it into the native
193 // body stream. This effectively locks and disturbs the stream.
195 // Note that JSContext is used only if there is a ReadableStream (this can
196 // happen because the body is a ReadableStream or because attribute body has
197 // already been used by content). If something goes wrong using
198 // ReadableStream, errors will be reported via ErrorResult and not as JS
199 // exceptions in JSContext. This is done in order to have a centralized error
202 // Exceptions generated when reading from the ReadableStream are directly sent
204 void SetBodyUsed(JSContext
* aCx
, ErrorResult
& aRv
);
207 void NullifyStream() override
{
208 mReadableStreamBody
= nullptr;
209 mReadableStreamReader
= nullptr;
210 mFetchStreamReader
= nullptr;
213 void SetReadableStreamBody(JSObject
* aBody
) override
{
214 mReadableStreamBody
= aBody
;
217 JSObject
* GetReadableStreamBody() override
{ return mReadableStreamBody
; }
219 void MarkAsRead() override
{ mBodyUsed
= true; }
221 virtual AbortSignalImpl
* GetSignalImpl() const = 0;
224 void RunAbortAlgorithm() override
;
226 already_AddRefed
<Promise
> ConsumeBody(JSContext
* aCx
,
227 BodyConsumer::ConsumeType aType
,
231 nsCOMPtr
<nsIGlobalObject
> mOwner
;
233 // Always set whenever the FetchBody is created on the worker thread.
234 WorkerPrivate
* mWorkerPrivate
;
236 // This is the ReadableStream exposed to content. It's underlying source is a
237 // BodyStream object.
238 JS::Heap
<JSObject
*> mReadableStreamBody
;
240 // This is the Reader used to retrieve data from the body.
241 JS::Heap
<JSObject
*> mReadableStreamReader
;
242 RefPtr
<FetchStreamReader
> mFetchStreamReader
;
244 explicit FetchBody(nsIGlobalObject
* aOwner
);
246 virtual ~FetchBody();
248 void SetReadableStreamBody(JSContext
* aCx
, JSObject
* aBody
);
251 Derived
* DerivedClass() const {
252 return static_cast<Derived
*>(const_cast<FetchBody
*>(this));
255 void LockStream(JSContext
* aCx
, JS::HandleObject aStream
, ErrorResult
& aRv
);
257 bool IsOnTargetThread() { return NS_IsMainThread() == !mWorkerPrivate
; }
259 void AssertIsOnTargetThread() { MOZ_ASSERT(IsOnTargetThread()); }
261 // Only ever set once, always on target thread.
264 // The main-thread event target for runnable dispatching.
265 nsCOMPtr
<nsIEventTarget
> mMainThreadEventTarget
;
268 class EmptyBody final
: public FetchBody
<EmptyBody
> {
269 NS_DECL_ISUPPORTS_INHERITED
270 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(EmptyBody
,
271 FetchBody
<EmptyBody
>)
274 static already_AddRefed
<EmptyBody
> Create(
275 nsIGlobalObject
* aGlobal
, mozilla::ipc::PrincipalInfo
* aPrincipalInfo
,
276 AbortSignalImpl
* aAbortSignalImpl
, const nsACString
& aMimeType
,
279 nsIGlobalObject
* GetParentObject() const { return mOwner
; }
281 AbortSignalImpl
* GetSignalImpl() const override
{ return mAbortSignalImpl
; }
283 const UniquePtr
<mozilla::ipc::PrincipalInfo
>& GetPrincipalInfo() const {
284 return mPrincipalInfo
;
287 void GetMimeType(nsACString
& aMimeType
) { aMimeType
= mMimeType
; }
289 void GetBody(nsIInputStream
** aStream
, int64_t* aBodyLength
= nullptr);
291 using FetchBody::BodyBlobURISpec
;
293 const nsACString
& BodyBlobURISpec() const { return EmptyCString(); }
295 using FetchBody::BodyLocalPath
;
297 const nsAString
& BodyLocalPath() const { return EmptyString(); }
300 EmptyBody(nsIGlobalObject
* aGlobal
,
301 mozilla::ipc::PrincipalInfo
* aPrincipalInfo
,
302 AbortSignalImpl
* aAbortSignalImpl
, const nsACString
& aMimeType
,
303 already_AddRefed
<nsIInputStream
> aBodyStream
);
307 UniquePtr
<mozilla::ipc::PrincipalInfo
> mPrincipalInfo
;
308 RefPtr
<AbortSignalImpl
> mAbortSignalImpl
;
310 nsCOMPtr
<nsIInputStream
> mBodyStream
;
313 } // namespace mozilla
315 #endif // mozilla_dom_Fetch_h