/work/obj-fuzz/dist/include/mozilla/dom/Fetch.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef mozilla_dom_Fetch_h |
8 | | #define mozilla_dom_Fetch_h |
9 | | |
10 | | #include "nsAutoPtr.h" |
11 | | #include "nsIStreamLoader.h" |
12 | | |
13 | | #include "nsCOMPtr.h" |
14 | | #include "nsError.h" |
15 | | #include "nsProxyRelease.h" |
16 | | #include "nsString.h" |
17 | | |
18 | | #include "mozilla/DebugOnly.h" |
19 | | #include "mozilla/ErrorResult.h" |
20 | | #include "mozilla/dom/AbortSignal.h" |
21 | | #include "mozilla/dom/Promise.h" |
22 | | #include "mozilla/dom/FetchStreamReader.h" |
23 | | #include "mozilla/dom/RequestBinding.h" |
24 | | |
25 | | class nsIGlobalObject; |
26 | | class nsIEventTarget; |
27 | | |
28 | | namespace mozilla { |
29 | | namespace dom { |
30 | | |
31 | | class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; |
32 | | class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString; |
33 | | class BlobImpl; |
34 | | class InternalRequest; |
35 | | class OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; |
36 | | struct ReadableStream; |
37 | | class RequestOrUSVString; |
38 | | class WorkerPrivate; |
39 | | |
40 | | enum class CallerType : uint32_t; |
41 | | |
42 | | already_AddRefed<Promise> |
43 | | FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, |
44 | | const RequestInit& aInit, CallerType aCallerType, |
45 | | ErrorResult& aRv); |
46 | | |
47 | | nsresult |
48 | | UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest); |
49 | | |
50 | | namespace fetch { |
51 | | typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString BodyInit; |
52 | | typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString ResponseBodyInit; |
53 | | typedef OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString OwningBodyInit; |
54 | | }; |
55 | | |
56 | | /* |
57 | | * Creates an nsIInputStream based on the fetch specifications 'extract a byte |
58 | | * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract. |
59 | | * Stores content type in out param aContentType. |
60 | | */ |
61 | | nsresult |
62 | | ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit, |
63 | | nsIInputStream** aStream, |
64 | | nsCString& aContentType, |
65 | | uint64_t& aContentLength); |
66 | | |
67 | | /* |
68 | | * Non-owning version. |
69 | | */ |
70 | | nsresult |
71 | | ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit, |
72 | | nsIInputStream** aStream, |
73 | | nsCString& aContentType, |
74 | | uint64_t& aContentLength); |
75 | | |
76 | | /* |
77 | | * Non-owning version. This method should go away when BodyInit will contain |
78 | | * ReadableStream. |
79 | | */ |
80 | | nsresult |
81 | | ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit, |
82 | | nsIInputStream** aStream, |
83 | | nsCString& aContentType, |
84 | | uint64_t& aContentLength); |
85 | | |
86 | | template <class Derived> class FetchBodyConsumer; |
87 | | |
88 | | enum FetchConsumeType |
89 | | { |
90 | | CONSUME_ARRAYBUFFER, |
91 | | CONSUME_BLOB, |
92 | | CONSUME_FORMDATA, |
93 | | CONSUME_JSON, |
94 | | CONSUME_TEXT, |
95 | | }; |
96 | | |
97 | | class FetchStreamHolder |
98 | | { |
99 | | public: |
100 | | NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING |
101 | | |
102 | | virtual void |
103 | | NullifyStream() = 0; |
104 | | |
105 | | virtual void |
106 | | MarkAsRead() = 0; |
107 | | |
108 | | virtual JSObject* |
109 | | ReadableStreamBody() = 0; |
110 | | }; |
111 | | |
112 | | /* |
113 | | * FetchBody's body consumption uses nsIInputStreamPump to read from the |
114 | | * underlying stream to a block of memory, which is then adopted by |
115 | | * ContinueConsumeBody() and converted to the right type based on the JS |
116 | | * function called. |
117 | | * |
118 | | * Use of the nsIInputStreamPump complicates things on the worker thread. |
119 | | * The solution used here is similar to WebSockets. |
120 | | * The difference is that we are only interested in completion and not data |
121 | | * events, and nsIInputStreamPump can only deliver completion on the main thread. |
122 | | * |
123 | | * Before starting the pump on the main thread, we addref the FetchBody to keep |
124 | | * it alive. Then we add a feature, to track the status of the worker. |
125 | | * |
126 | | * ContinueConsumeBody() is the function that cleans things up in both success |
127 | | * and error conditions and so all callers call it with the appropriate status. |
128 | | * |
129 | | * Once the read is initiated on the main thread there are two possibilities. |
130 | | * |
131 | | * 1) Pump finishes before worker has finished Running. |
132 | | * In this case we adopt the data and dispatch a runnable to the worker, |
133 | | * which derefs FetchBody and removes the feature and resolves the Promise. |
134 | | * |
135 | | * 2) Pump still working while worker has stopped Running. |
136 | | * The feature is Notify()ed and ContinueConsumeBody() is called with |
137 | | * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to |
138 | | * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly |
139 | | * held by it) until pump->Cancel() is called. OnStreamComplete() will not |
140 | | * do anything if the error code is NS_BINDING_ABORTED, so we don't have to |
141 | | * worry about keeping anything alive. |
142 | | * |
143 | | * The pump is always released on the main thread. |
144 | | */ |
145 | | template <class Derived> |
146 | | class FetchBody : public FetchStreamHolder |
147 | | , public AbortFollower |
148 | | { |
149 | | public: |
150 | | friend class FetchBodyConsumer<Derived>; |
151 | | |
152 | | bool |
153 | | BodyUsed() const; |
154 | | |
155 | | already_AddRefed<Promise> |
156 | | ArrayBuffer(JSContext* aCx, ErrorResult& aRv) |
157 | 0 | { |
158 | 0 | return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv); |
159 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::ArrayBuffer(JSContext*, mozilla::ErrorResult&) Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::ArrayBuffer(JSContext*, mozilla::ErrorResult&) |
160 | | |
161 | | already_AddRefed<Promise> |
162 | | Blob(JSContext* aCx, ErrorResult& aRv) |
163 | 0 | { |
164 | 0 | return ConsumeBody(aCx, CONSUME_BLOB, aRv); |
165 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::Blob(JSContext*, mozilla::ErrorResult&) Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::Blob(JSContext*, mozilla::ErrorResult&) |
166 | | |
167 | | already_AddRefed<Promise> |
168 | | FormData(JSContext* aCx, ErrorResult& aRv) |
169 | 0 | { |
170 | 0 | return ConsumeBody(aCx, CONSUME_FORMDATA, aRv); |
171 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::FormData(JSContext*, mozilla::ErrorResult&) Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::FormData(JSContext*, mozilla::ErrorResult&) |
172 | | |
173 | | already_AddRefed<Promise> |
174 | | Json(JSContext* aCx, ErrorResult& aRv) |
175 | 0 | { |
176 | 0 | return ConsumeBody(aCx, CONSUME_JSON, aRv); |
177 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::Json(JSContext*, mozilla::ErrorResult&) Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::Json(JSContext*, mozilla::ErrorResult&) |
178 | | |
179 | | already_AddRefed<Promise> |
180 | | Text(JSContext* aCx, ErrorResult& aRv) |
181 | 0 | { |
182 | 0 | return ConsumeBody(aCx, CONSUME_TEXT, aRv); |
183 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::Text(JSContext*, mozilla::ErrorResult&) Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::Text(JSContext*, mozilla::ErrorResult&) |
184 | | |
185 | | void |
186 | | GetBody(JSContext* aCx, |
187 | | JS::MutableHandle<JSObject*> aBodyOut, |
188 | | ErrorResult& aRv); |
189 | | |
190 | | const nsAString& |
191 | | BodyLocalPath() const; |
192 | | |
193 | | // If the body contains a ReadableStream body object, this method produces a |
194 | | // tee() of it. |
195 | | void |
196 | | MaybeTeeReadableStreamBody(JSContext* aCx, |
197 | | JS::MutableHandle<JSObject*> aBodyOut, |
198 | | FetchStreamReader** aStreamReader, |
199 | | nsIInputStream** aInputStream, |
200 | | ErrorResult& aRv); |
201 | | |
202 | | // Utility public methods accessed by various runnables. |
203 | | |
204 | | // This method _must_ be called in order to set the body as used. If the body |
205 | | // is a ReadableStream, this method will start reading the stream. |
206 | | // More in details, this method does: |
207 | | // 1) It uses an internal flag to track if the body is used. This is tracked |
208 | | // separately from the ReadableStream disturbed state due to purely native |
209 | | // streams. |
210 | | // 2) If there is a ReadableStream reflector for the native stream it is |
211 | | // Locked. |
212 | | // 3) If there is a JS ReadableStream then we begin pumping it into the native |
213 | | // body stream. This effectively locks and disturbs the stream. |
214 | | // |
215 | | // Note that JSContext is used only if there is a ReadableStream (this can |
216 | | // happen because the body is a ReadableStream or because attribute body has |
217 | | // already been used by content). If something goes wrong using |
218 | | // ReadableStream, errors will be reported via ErrorResult and not as JS |
219 | | // exceptions in JSContext. This is done in order to have a centralized error |
220 | | // reporting way. |
221 | | // |
222 | | // Exceptions generated when reading from the ReadableStream are directly sent |
223 | | // to the Console. |
224 | | void |
225 | | SetBodyUsed(JSContext* aCx, ErrorResult& aRv); |
226 | | |
227 | | const nsCString& |
228 | | MimeType() const |
229 | 0 | { |
230 | 0 | return mMimeType; |
231 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::MimeType() const Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::MimeType() const |
232 | | |
233 | | // FetchStreamHolder |
234 | | void |
235 | | NullifyStream() override |
236 | 0 | { |
237 | 0 | mReadableStreamBody = nullptr; |
238 | 0 | mReadableStreamReader = nullptr; |
239 | 0 | mFetchStreamReader = nullptr; |
240 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::NullifyStream() Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::NullifyStream() |
241 | | |
242 | | JSObject* |
243 | | ReadableStreamBody() override |
244 | 0 | { |
245 | 0 | MOZ_ASSERT(mReadableStreamBody); |
246 | 0 | return mReadableStreamBody; |
247 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::ReadableStreamBody() Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::ReadableStreamBody() |
248 | | |
249 | | void |
250 | | MarkAsRead() override |
251 | 0 | { |
252 | 0 | mBodyUsed = true; |
253 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::MarkAsRead() Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::MarkAsRead() |
254 | | |
255 | | virtual AbortSignalImpl* |
256 | | GetSignalImpl() const = 0; |
257 | | |
258 | | // AbortFollower |
259 | | void |
260 | | Abort() override; |
261 | | |
262 | | protected: |
263 | | nsCOMPtr<nsIGlobalObject> mOwner; |
264 | | |
265 | | // Always set whenever the FetchBody is created on the worker thread. |
266 | | WorkerPrivate* mWorkerPrivate; |
267 | | |
268 | | // This is the ReadableStream exposed to content. It's underlying source is a |
269 | | // FetchStream object. |
270 | | JS::Heap<JSObject*> mReadableStreamBody; |
271 | | |
272 | | // This is the Reader used to retrieve data from the body. |
273 | | JS::Heap<JSObject*> mReadableStreamReader; |
274 | | RefPtr<FetchStreamReader> mFetchStreamReader; |
275 | | |
276 | | explicit FetchBody(nsIGlobalObject* aOwner); |
277 | | |
278 | | virtual ~FetchBody(); |
279 | | |
280 | | void |
281 | | SetMimeType(); |
282 | | |
283 | | void |
284 | | SetReadableStreamBody(JSContext* aCx, JSObject* aBody); |
285 | | |
286 | | private: |
287 | | Derived* |
288 | | DerivedClass() const |
289 | 0 | { |
290 | 0 | return static_cast<Derived*>(const_cast<FetchBody*>(this)); |
291 | 0 | } Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Request>::DerivedClass() const Unexecuted instantiation: mozilla::dom::FetchBody<mozilla::dom::Response>::DerivedClass() const |
292 | | |
293 | | already_AddRefed<Promise> |
294 | | ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv); |
295 | | |
296 | | void |
297 | | LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv); |
298 | | |
299 | | bool |
300 | | IsOnTargetThread() |
301 | | { |
302 | | return NS_IsMainThread() == !mWorkerPrivate; |
303 | | } |
304 | | |
305 | | void |
306 | | AssertIsOnTargetThread() |
307 | | { |
308 | | MOZ_ASSERT(IsOnTargetThread()); |
309 | | } |
310 | | |
311 | | // Only ever set once, always on target thread. |
312 | | bool mBodyUsed; |
313 | | nsCString mMimeType; |
314 | | |
315 | | // The main-thread event target for runnable dispatching. |
316 | | nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; |
317 | | }; |
318 | | |
319 | | } // namespace dom |
320 | | } // namespace mozilla |
321 | | |
322 | | #endif // mozilla_dom_Fetch_h |