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
10 #include "mozilla/Attributes.h"
13 #include "nsProxyRelease.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/dom/AbortSignal.h"
18 #include "mozilla/dom/BodyConsumer.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/FetchStreamReader.h"
21 #include "mozilla/dom/ReadableStream.h"
22 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
23 #include "mozilla/dom/RequestBinding.h"
24 #include "mozilla/dom/workerinternals/RuntimeService.h"
26 class nsIGlobalObject
;
38 class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
40 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString
;
42 class InternalRequest
;
44 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
46 class ReadableStreamDefaultReader
;
47 class RequestOrUTF8String
;
50 enum class CallerType
: uint32_t;
52 already_AddRefed
<Promise
> FetchRequest(nsIGlobalObject
* aGlobal
,
53 const RequestOrUTF8String
& aInput
,
54 const RequestInit
& aInit
,
55 CallerType aCallerType
,
58 nsresult
UpdateRequestReferrer(nsIGlobalObject
* aGlobal
,
59 InternalRequest
* aRequest
);
63 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
64 using ResponseBodyInit
=
65 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString
;
66 using OwningBodyInit
=
67 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString
;
71 * Creates an nsIInputStream based on the fetch specifications 'extract a byte
72 * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
73 * Stores content type in out param aContentType.
75 nsresult
ExtractByteStreamFromBody(const fetch::OwningBodyInit
& aBodyInit
,
76 nsIInputStream
** aStream
,
77 nsCString
& aContentType
,
78 uint64_t& aContentLength
);
83 nsresult
ExtractByteStreamFromBody(const fetch::BodyInit
& aBodyInit
,
84 nsIInputStream
** aStream
,
85 nsCString
& aContentType
,
86 uint64_t& aContentLength
);
89 * Non-owning version. This method should go away when BodyInit will contain
92 nsresult
ExtractByteStreamFromBody(const fetch::ResponseBodyInit
& aBodyInit
,
93 nsIInputStream
** aStream
,
94 nsCString
& aContentType
,
95 uint64_t& aContentLength
);
98 * FetchBody's body consumption uses nsIInputStreamPump to read from the
99 * underlying stream to a block of memory, which is then adopted by
100 * ContinueConsumeBody() and converted to the right type based on the JS
103 * Use of the nsIInputStreamPump complicates things on the worker thread.
104 * The solution used here is similar to WebSockets.
105 * The difference is that we are only interested in completion and not data
106 * events, and nsIInputStreamPump can only deliver completion on the main
109 * Before starting the pump on the main thread, we addref the FetchBody to keep
110 * it alive. Then we add a feature, to track the status of the worker.
112 * ContinueConsumeBody() is the function that cleans things up in both success
113 * and error conditions and so all callers call it with the appropriate status.
115 * Once the read is initiated on the main thread there are two possibilities.
117 * 1) Pump finishes before worker has finished Running.
118 * In this case we adopt the data and dispatch a runnable to the worker,
119 * which derefs FetchBody and removes the feature and resolves the Promise.
121 * 2) Pump still working while worker has stopped Running.
122 * The feature is Notify()ed and ContinueConsumeBody() is called with
123 * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
124 * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
125 * held by it) until pump->Cancel() is called. OnStreamComplete() will not
126 * do anything if the error code is NS_BINDING_ABORTED, so we don't have to
127 * worry about keeping anything alive.
129 * The pump is always released on the main thread.
132 class FetchBodyBase
: public nsISupports
{
134 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
135 NS_DECL_CYCLE_COLLECTION_CLASS(FetchBodyBase
)
138 virtual ~FetchBodyBase() = default;
140 RefPtr
<ReadableStream
> mReadableStreamBody
;
143 template <class Derived
>
144 class FetchBody
: public FetchBodyBase
, public AbortFollower
{
146 using FetchBodyBase::QueryInterface
;
148 NS_INLINE_DECL_REFCOUNTING_INHERITED(FetchBody
, FetchBodyBase
)
150 bool BodyUsed() const;
152 already_AddRefed
<Promise
> ArrayBuffer(JSContext
* aCx
, ErrorResult
& aRv
) {
153 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::ArrayBuffer
, aRv
);
156 already_AddRefed
<Promise
> Blob(JSContext
* aCx
, ErrorResult
& aRv
) {
157 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::Blob
, aRv
);
160 already_AddRefed
<Promise
> Bytes(JSContext
* aCx
, ErrorResult
& aRv
) {
161 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::Bytes
, aRv
);
164 already_AddRefed
<Promise
> FormData(JSContext
* aCx
, ErrorResult
& aRv
) {
165 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::FormData
, aRv
);
168 already_AddRefed
<Promise
> Json(JSContext
* aCx
, ErrorResult
& aRv
) {
169 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::JSON
, aRv
);
172 already_AddRefed
<Promise
> Text(JSContext
* aCx
, ErrorResult
& aRv
) {
173 return ConsumeBody(aCx
, BodyConsumer::ConsumeType::Text
, aRv
);
176 already_AddRefed
<ReadableStream
> GetBody(JSContext
* aCx
, ErrorResult
& aRv
);
177 void GetMimeType(nsACString
& aMimeType
, nsACString
& aMixedCaseMimeType
);
179 const nsACString
& BodyBlobURISpec() const;
181 const nsAString
& BodyLocalPath() const;
183 // If the body contains a ReadableStream body object, this method produces a
186 // This is marked as a script boundary minimize changes required for
187 // annotation while we work out how to correctly annotate this code.
188 // Tracked in Bug 1750650.
189 MOZ_CAN_RUN_SCRIPT_BOUNDARY
190 void MaybeTeeReadableStreamBody(JSContext
* aCx
, ReadableStream
** aBodyOut
,
191 FetchStreamReader
** aStreamReader
,
192 nsIInputStream
** aInputStream
,
195 // Utility public methods accessed by various runnables.
197 // This method _must_ be called in order to set the body as used. If the body
198 // is a ReadableStream, this method will start reading the stream.
199 // More in details, this method does:
200 // 1) It uses an internal flag to track if the body is used. This is tracked
201 // separately from the ReadableStream disturbed state due to purely native
203 // 2) If there is a ReadableStream reflector for the native stream it is
205 // 3) If there is a JS ReadableStream then we begin pumping it into the native
206 // body stream. This effectively locks and disturbs the stream.
208 // Note that JSContext is used only if there is a ReadableStream (this can
209 // happen because the body is a ReadableStream or because attribute body has
210 // already been used by content). If something goes wrong using
211 // ReadableStream, errors will be reported via ErrorResult and not as JS
212 // exceptions in JSContext. This is done in order to have a centralized error
215 // Exceptions generated when reading from the ReadableStream are directly sent
217 void SetBodyUsed(JSContext
* aCx
, ErrorResult
& aRv
);
219 virtual AbortSignalImpl
* GetSignalImpl() const = 0;
221 virtual AbortSignalImpl
* GetSignalImplToConsumeBody() const = 0;
224 void RunAbortAlgorithm() override
;
226 already_AddRefed
<Promise
> ConsumeBody(JSContext
* aCx
,
227 BodyConsumer::ConsumeType aType
,
231 nsCOMPtr
<nsIGlobalObject
> mOwner
;
233 // This is the Reader used to retrieve data from the body. This needs to be
234 // traversed by subclasses.
235 RefPtr
<FetchStreamReader
> mFetchStreamReader
;
237 explicit FetchBody(nsIGlobalObject
* aOwner
);
239 virtual ~FetchBody();
241 void SetReadableStreamBody(JSContext
* aCx
, ReadableStream
* aBody
);
244 Derived
* DerivedClass() const {
245 return static_cast<Derived
*>(const_cast<FetchBody
*>(this));
248 void LockStream(JSContext
* aCx
, ReadableStream
* aStream
, ErrorResult
& aRv
);
250 void AssertIsOnTargetThread() {
251 MOZ_ASSERT(NS_IsMainThread() == !GetCurrentThreadWorkerPrivate());
254 // Only ever set once, always on target thread.
257 // The main-thread event target for runnable dispatching.
258 nsCOMPtr
<nsISerialEventTarget
> mMainThreadEventTarget
;
261 class EmptyBody final
: public FetchBody
<EmptyBody
> {
262 NS_DECL_ISUPPORTS_INHERITED
263 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(EmptyBody
,
264 FetchBody
<EmptyBody
>)
267 static already_AddRefed
<EmptyBody
> Create(
268 nsIGlobalObject
* aGlobal
, mozilla::ipc::PrincipalInfo
* aPrincipalInfo
,
269 AbortSignalImpl
* aAbortSignalImpl
, const nsACString
& aMimeType
,
270 const nsACString
& aMixedCaseMimeType
, ErrorResult
& aRv
);
272 nsIGlobalObject
* GetParentObject() const { return mOwner
; }
274 AbortSignalImpl
* GetSignalImpl() const override
{ return mAbortSignalImpl
; }
275 AbortSignalImpl
* GetSignalImplToConsumeBody() const final
{ return nullptr; }
277 const UniquePtr
<mozilla::ipc::PrincipalInfo
>& GetPrincipalInfo() const {
278 return mPrincipalInfo
;
281 void GetMimeType(nsACString
& aMimeType
, nsACString
& aMixedCaseMimeType
) {
282 aMimeType
= mMimeType
;
283 aMixedCaseMimeType
= mMixedCaseMimeType
;
286 void GetBody(nsIInputStream
** aStream
, int64_t* aBodyLength
= nullptr);
288 using FetchBody::BodyBlobURISpec
;
290 const nsACString
& BodyBlobURISpec() const { return EmptyCString(); }
292 using FetchBody::BodyLocalPath
;
294 const nsAString
& BodyLocalPath() const { return EmptyString(); }
297 EmptyBody(nsIGlobalObject
* aGlobal
,
298 mozilla::ipc::PrincipalInfo
* aPrincipalInfo
,
299 AbortSignalImpl
* aAbortSignalImpl
, const nsACString
& aMimeType
,
300 const nsACString
& aMixedCaseMimeType
,
301 already_AddRefed
<nsIInputStream
> aBodyStream
);
305 UniquePtr
<mozilla::ipc::PrincipalInfo
> mPrincipalInfo
;
306 RefPtr
<AbortSignalImpl
> mAbortSignalImpl
;
308 nsCString mMixedCaseMimeType
;
309 nsCOMPtr
<nsIInputStream
> mBodyStream
;
312 } // namespace mozilla
314 #endif // mozilla_dom_Fetch_h