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 #include "Fetch.h"
8
9 #include "mozilla/dom/Document.h"
10 #include "nsIGlobalObject.h"
11
12 #include "nsDOMString.h"
13 #include "nsJSUtils.h"
14 #include "nsNetUtil.h"
15 #include "nsReadableUtils.h"
16 #include "nsStreamUtils.h"
17 #include "nsStringStream.h"
18 #include "nsProxyRelease.h"
19
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/dom/BindingDeclarations.h"
22 #include "mozilla/dom/BodyConsumer.h"
23 #include "mozilla/dom/Exceptions.h"
24 #include "mozilla/dom/DOMException.h"
25 #include "mozilla/dom/FetchDriver.h"
26 #include "mozilla/dom/File.h"
27 #include "mozilla/dom/FormData.h"
28 #include "mozilla/dom/Headers.h"
29 #include "mozilla/dom/Promise.h"
30 #include "mozilla/dom/PromiseWorkerProxy.h"
31 #include "mozilla/dom/RemoteWorkerChild.h"
32 #include "mozilla/dom/Request.h"
33 #include "mozilla/dom/Response.h"
34 #include "mozilla/dom/ScriptSettings.h"
35 #include "mozilla/dom/URLSearchParams.h"
36 #include "mozilla/net/CookieJarSettings.h"
37
38 #include "BodyExtractor.h"
39 #include "EmptyBody.h"
40 #include "FetchObserver.h"
41 #include "InternalRequest.h"
42 #include "InternalResponse.h"
43
44 #include "mozilla/dom/WorkerCommon.h"
45 #include "mozilla/dom/WorkerPrivate.h"
46 #include "mozilla/dom/WorkerRef.h"
47 #include "mozilla/dom/WorkerRunnable.h"
48 #include "mozilla/dom/WorkerScope.h"
49
50 namespace mozilla::dom {
51
52 namespace {
53
AbortStream(JSContext * aCx,JS::Handle<JSObject * > aStream,ErrorResult & aRv)54 void AbortStream(JSContext* aCx, JS::Handle<JSObject*> aStream,
55 ErrorResult& aRv) {
56 aRv.MightThrowJSException();
57
58 bool isReadable;
59 if (!JS::ReadableStreamIsReadable(aCx, aStream, &isReadable)) {
60 aRv.StealExceptionFromJSContext(aCx);
61 return;
62 }
63 if (!isReadable) {
64 return;
65 }
66
67 RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
68
69 JS::Rooted<JS::Value> value(aCx);
70 if (!GetOrCreateDOMReflector(aCx, e, &value)) {
71 return;
72 }
73
74 if (!JS::ReadableStreamError(aCx, aStream, value)) {
75 aRv.StealExceptionFromJSContext(aCx);
76 }
77 }
78
79 } // namespace
80
81 class AbortSignalMainThread final : public AbortSignalImpl {
82 public:
83 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)84 NS_DECL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)
85
86 explicit AbortSignalMainThread(bool aAborted) : AbortSignalImpl(aAborted) {}
87
88 private:
89 ~AbortSignalMainThread() = default;
90 };
91
92 NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)
93
94 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortSignalMainThread)
95 AbortSignalImpl::Unlink(static_cast<AbortSignalImpl*>(tmp));
96 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
97
98 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortSignalMainThread)
99 AbortSignalImpl::Traverse(static_cast<AbortSignalImpl*>(tmp), cb);
100 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
101
102 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignalMainThread)
103 NS_INTERFACE_MAP_ENTRY(nsISupports)
104 NS_INTERFACE_MAP_END
105
106 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortSignalMainThread)
107 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortSignalMainThread)
108
109 class AbortSignalProxy;
110
111 class WorkerSignalFollower final : public AbortFollower {
112 public:
113 // This runnable propagates changes from the AbortSignalImpl on workers to the
114 // AbortSignalImpl on main-thread.
115 class AbortSignalProxyRunnable final : public Runnable {
116 RefPtr<AbortSignalProxy> mProxy;
117
118 public:
AbortSignalProxyRunnable(AbortSignalProxy * aProxy)119 explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy)
120 : Runnable("dom::WorkerSignalFollower::AbortSignalProxyRunnable"),
121 mProxy(aProxy) {}
122
123 NS_IMETHOD Run() override;
124 };
125
126 public:
127 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(WorkerSignalFollower)128 NS_DECL_CYCLE_COLLECTION_CLASS(WorkerSignalFollower)
129
130 void RunAbortAlgorithm() override {}
131
132 private:
133 ~WorkerSignalFollower() = default;
134 };
135
136 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerSignalFollower)
137
138 NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerSignalFollower)
139 NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerSignalFollower)
140
141 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkerSignalFollower)
142 AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
144
145 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkerSignalFollower)
146 AbortFollower::Traverse(static_cast<AbortFollower*>(tmp), cb);
147 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
148
149 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerSignalFollower)
150 NS_INTERFACE_MAP_ENTRY(nsISupports)
151 NS_INTERFACE_MAP_END
152
153 // This class orchestrates the proxying of AbortSignal operations between the
154 // main thread and a worker thread.
155 class AbortSignalProxy final : public AbortFollower {
156 // This is created and released on the main-thread.
157 RefPtr<AbortSignalImpl> mSignalImplMainThread;
158
159 // The main-thread event target for runnable dispatching.
160 nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
161
162 // This value is used only when creating mSignalImplMainThread on the main
163 // thread, to create it in already-aborted state if necessary. It does *not*
164 // reflect the instantaneous is-aborted status of the worker thread's
165 // AbortSignal.
166 const bool mAborted;
167
168 public:
169 NS_DECL_THREADSAFE_ISUPPORTS
170
AbortSignalProxy(AbortSignalImpl * aSignalImpl,nsIEventTarget * aMainThreadEventTarget)171 AbortSignalProxy(AbortSignalImpl* aSignalImpl,
172 nsIEventTarget* aMainThreadEventTarget)
173 : mMainThreadEventTarget(aMainThreadEventTarget),
174 mAborted(aSignalImpl->Aborted()) {
175 MOZ_ASSERT(!NS_IsMainThread());
176 MOZ_ASSERT(mMainThreadEventTarget);
177 Follow(aSignalImpl);
178 }
179
180 // AbortFollower
181 void RunAbortAlgorithm() override;
182
GetOrCreateSignalImplForMainThread()183 AbortSignalImpl* GetOrCreateSignalImplForMainThread() {
184 MOZ_ASSERT(NS_IsMainThread());
185 if (!mSignalImplMainThread) {
186 mSignalImplMainThread = new AbortSignalMainThread(mAborted);
187 }
188 return mSignalImplMainThread;
189 }
190
GetSignalImplForTargetThread()191 AbortSignalImpl* GetSignalImplForTargetThread() {
192 MOZ_ASSERT(!NS_IsMainThread());
193 return Signal();
194 }
195
MainThreadEventTarget()196 nsIEventTarget* MainThreadEventTarget() { return mMainThreadEventTarget; }
197
Shutdown()198 void Shutdown() {
199 MOZ_ASSERT(!NS_IsMainThread());
200 Unfollow();
201 }
202
203 private:
~AbortSignalProxy()204 ~AbortSignalProxy() {
205 NS_ProxyRelease("AbortSignalProxy::mSignalImplMainThread",
206 mMainThreadEventTarget, mSignalImplMainThread.forget());
207 }
208 };
209
NS_IMPL_ISUPPORTS0(AbortSignalProxy)210 NS_IMPL_ISUPPORTS0(AbortSignalProxy)
211
212 NS_IMETHODIMP WorkerSignalFollower::AbortSignalProxyRunnable::Run() {
213 MOZ_ASSERT(NS_IsMainThread());
214 AbortSignalImpl* signalImpl = mProxy->GetOrCreateSignalImplForMainThread();
215 signalImpl->SignalAbort();
216 return NS_OK;
217 }
218
RunAbortAlgorithm()219 void AbortSignalProxy::RunAbortAlgorithm() {
220 MOZ_ASSERT(!NS_IsMainThread());
221 using AbortSignalProxyRunnable =
222 WorkerSignalFollower::AbortSignalProxyRunnable;
223 RefPtr<AbortSignalProxyRunnable> runnable =
224 new AbortSignalProxyRunnable(this);
225 MainThreadEventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
226 }
227
228 class WorkerFetchResolver final : public FetchDriverObserver {
229 // Thread-safe:
230 RefPtr<PromiseWorkerProxy> mPromiseProxy;
231 RefPtr<AbortSignalProxy> mSignalProxy;
232
233 // Touched only on the worker thread.
234 RefPtr<FetchObserver> mFetchObserver;
235 RefPtr<WeakWorkerRef> mWorkerRef;
236 bool mIsShutdown;
237
238 Atomic<bool> mNeedOnDataAvailable;
239
240 public:
241 // Returns null if worker is shutting down.
Create(WorkerPrivate * aWorkerPrivate,Promise * aPromise,AbortSignalImpl * aSignalImpl,FetchObserver * aObserver)242 static already_AddRefed<WorkerFetchResolver> Create(
243 WorkerPrivate* aWorkerPrivate, Promise* aPromise,
244 AbortSignalImpl* aSignalImpl, FetchObserver* aObserver) {
245 MOZ_ASSERT(aWorkerPrivate);
246 aWorkerPrivate->AssertIsOnWorkerThread();
247 RefPtr<PromiseWorkerProxy> proxy =
248 PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
249 if (!proxy) {
250 return nullptr;
251 }
252
253 RefPtr<AbortSignalProxy> signalProxy;
254 if (aSignalImpl) {
255 signalProxy = new AbortSignalProxy(
256 aSignalImpl, aWorkerPrivate->MainThreadEventTarget());
257 }
258
259 RefPtr<WorkerFetchResolver> r =
260 new WorkerFetchResolver(proxy, signalProxy, aObserver);
261
262 RefPtr<WeakWorkerRef> workerRef = WeakWorkerRef::Create(
263 aWorkerPrivate, [r]() { r->Shutdown(r->mWorkerRef->GetPrivate()); });
264 if (NS_WARN_IF(!workerRef)) {
265 return nullptr;
266 }
267
268 r->mWorkerRef = std::move(workerRef);
269
270 return r.forget();
271 }
272
GetAbortSignalForMainThread()273 AbortSignalImpl* GetAbortSignalForMainThread() {
274 MOZ_ASSERT(NS_IsMainThread());
275
276 if (!mSignalProxy) {
277 return nullptr;
278 }
279
280 return mSignalProxy->GetOrCreateSignalImplForMainThread();
281 }
282
GetAbortSignalForTargetThread()283 AbortSignalImpl* GetAbortSignalForTargetThread() {
284 mPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
285
286 if (!mSignalProxy) {
287 return nullptr;
288 }
289
290 return mSignalProxy->GetSignalImplForTargetThread();
291 }
292
PromiseProxy() const293 PromiseWorkerProxy* PromiseProxy() const {
294 MOZ_ASSERT(NS_IsMainThread());
295 return mPromiseProxy;
296 }
297
WorkerPromise(WorkerPrivate * aWorkerPrivate) const298 Promise* WorkerPromise(WorkerPrivate* aWorkerPrivate) const {
299 MOZ_ASSERT(aWorkerPrivate);
300 aWorkerPrivate->AssertIsOnWorkerThread();
301 MOZ_ASSERT(!mIsShutdown);
302
303 return mPromiseProxy->WorkerPromise();
304 }
305
GetFetchObserver(WorkerPrivate * aWorkerPrivate) const306 FetchObserver* GetFetchObserver(WorkerPrivate* aWorkerPrivate) const {
307 MOZ_ASSERT(aWorkerPrivate);
308 aWorkerPrivate->AssertIsOnWorkerThread();
309
310 return mFetchObserver;
311 }
312
313 void OnResponseAvailableInternal(InternalResponse* aResponse) override;
314
315 void OnResponseEnd(FetchDriverObserver::EndReason eReason) override;
316
317 bool NeedOnDataAvailable() override;
318
319 void OnDataAvailable() override;
320
Shutdown(WorkerPrivate * aWorkerPrivate)321 void Shutdown(WorkerPrivate* aWorkerPrivate) {
322 MOZ_ASSERT(aWorkerPrivate);
323 aWorkerPrivate->AssertIsOnWorkerThread();
324
325 mIsShutdown = true;
326 mPromiseProxy->CleanUp();
327
328 mNeedOnDataAvailable = false;
329 mFetchObserver = nullptr;
330
331 if (mSignalProxy) {
332 mSignalProxy->Shutdown();
333 }
334
335 mWorkerRef = nullptr;
336 }
337
IsShutdown(WorkerPrivate * aWorkerPrivate) const338 bool IsShutdown(WorkerPrivate* aWorkerPrivate) const {
339 MOZ_ASSERT(aWorkerPrivate);
340 aWorkerPrivate->AssertIsOnWorkerThread();
341 return mIsShutdown;
342 }
343
344 private:
WorkerFetchResolver(PromiseWorkerProxy * aProxy,AbortSignalProxy * aSignalProxy,FetchObserver * aObserver)345 WorkerFetchResolver(PromiseWorkerProxy* aProxy,
346 AbortSignalProxy* aSignalProxy, FetchObserver* aObserver)
347 : mPromiseProxy(aProxy),
348 mSignalProxy(aSignalProxy),
349 mFetchObserver(aObserver),
350 mIsShutdown(false),
351 mNeedOnDataAvailable(!!aObserver) {
352 MOZ_ASSERT(!NS_IsMainThread());
353 MOZ_ASSERT(mPromiseProxy);
354 }
355
356 ~WorkerFetchResolver() = default;
357
358 virtual void FlushConsoleReport() override;
359 };
360
361 class MainThreadFetchResolver final : public FetchDriverObserver {
362 RefPtr<Promise> mPromise;
363 RefPtr<Response> mResponse;
364 RefPtr<FetchObserver> mFetchObserver;
365 RefPtr<AbortSignalImpl> mSignalImpl;
366 const bool mMozErrors;
367
368 nsCOMPtr<nsILoadGroup> mLoadGroup;
369
370 NS_DECL_OWNINGTHREAD
371 public:
MainThreadFetchResolver(Promise * aPromise,FetchObserver * aObserver,AbortSignalImpl * aSignalImpl,bool aMozErrors)372 MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver,
373 AbortSignalImpl* aSignalImpl, bool aMozErrors)
374 : mPromise(aPromise),
375 mFetchObserver(aObserver),
376 mSignalImpl(aSignalImpl),
377 mMozErrors(aMozErrors) {}
378
379 void OnResponseAvailableInternal(InternalResponse* aResponse) override;
380
SetLoadGroup(nsILoadGroup * aLoadGroup)381 void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; }
382
OnResponseEnd(FetchDriverObserver::EndReason aReason)383 void OnResponseEnd(FetchDriverObserver::EndReason aReason) override {
384 if (aReason == eAborted) {
385 mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
386 }
387
388 mFetchObserver = nullptr;
389
390 FlushConsoleReport();
391 }
392
393 bool NeedOnDataAvailable() override;
394
395 void OnDataAvailable() override;
396
397 private:
398 ~MainThreadFetchResolver();
399
FlushConsoleReport()400 void FlushConsoleReport() override {
401 mReporter->FlushConsoleReports(mLoadGroup);
402 }
403 };
404
405 class MainThreadFetchRunnable : public Runnable {
406 RefPtr<WorkerFetchResolver> mResolver;
407 const ClientInfo mClientInfo;
408 const Maybe<ServiceWorkerDescriptor> mController;
409 nsCOMPtr<nsICSPEventListener> mCSPEventListener;
410 SafeRefPtr<InternalRequest> mRequest;
411 UniquePtr<SerializedStackHolder> mOriginStack;
412
413 public:
MainThreadFetchRunnable(WorkerFetchResolver * aResolver,const ClientInfo & aClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,nsICSPEventListener * aCSPEventListener,SafeRefPtr<InternalRequest> aRequest,UniquePtr<SerializedStackHolder> && aOriginStack)414 MainThreadFetchRunnable(WorkerFetchResolver* aResolver,
415 const ClientInfo& aClientInfo,
416 const Maybe<ServiceWorkerDescriptor>& aController,
417 nsICSPEventListener* aCSPEventListener,
418 SafeRefPtr<InternalRequest> aRequest,
419 UniquePtr<SerializedStackHolder>&& aOriginStack)
420 : Runnable("dom::MainThreadFetchRunnable"),
421 mResolver(aResolver),
422 mClientInfo(aClientInfo),
423 mController(aController),
424 mCSPEventListener(aCSPEventListener),
425 mRequest(std::move(aRequest)),
426 mOriginStack(std::move(aOriginStack)) {
427 MOZ_ASSERT(mResolver);
428 }
429
430 NS_IMETHOD
Run()431 Run() override {
432 AssertIsOnMainThread();
433 RefPtr<FetchDriver> fetch;
434 RefPtr<PromiseWorkerProxy> proxy = mResolver->PromiseProxy();
435
436 {
437 // Acquire the proxy mutex while getting data from the WorkerPrivate...
438 MutexAutoLock lock(proxy->Lock());
439 if (proxy->CleanedUp()) {
440 NS_WARNING("Aborting Fetch because worker already shut down");
441 return NS_OK;
442 }
443
444 WorkerPrivate* workerPrivate = proxy->GetWorkerPrivate();
445 MOZ_ASSERT(workerPrivate);
446 nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
447 MOZ_ASSERT(principal);
448 nsCOMPtr<nsILoadGroup> loadGroup = workerPrivate->GetLoadGroup();
449 MOZ_ASSERT(loadGroup);
450 // We don't track if a worker is spawned from a tracking script for now,
451 // so pass false as the last argument to FetchDriver().
452 fetch = new FetchDriver(mRequest.clonePtr(), principal, loadGroup,
453 workerPrivate->MainThreadEventTarget(),
454 workerPrivate->CookieJarSettings(),
455 workerPrivate->GetPerformanceStorage(), false);
456 nsAutoCString spec;
457 if (proxy->GetWorkerPrivate()->GetBaseURI()) {
458 proxy->GetWorkerPrivate()->GetBaseURI()->GetAsciiSpec(spec);
459 }
460 fetch->SetWorkerScript(spec);
461
462 fetch->SetClientInfo(mClientInfo);
463 fetch->SetController(mController);
464 fetch->SetCSPEventListener(mCSPEventListener);
465 }
466
467 fetch->SetOriginStack(std::move(mOriginStack));
468
469 RefPtr<AbortSignalImpl> signalImpl =
470 mResolver->GetAbortSignalForMainThread();
471
472 // ...but release it before calling Fetch, because mResolver's callback can
473 // be called synchronously and they want the mutex, too.
474 return fetch->Fetch(signalImpl, mResolver);
475 }
476 };
477
FetchRequest(nsIGlobalObject * aGlobal,const RequestOrUSVString & aInput,const RequestInit & aInit,CallerType aCallerType,ErrorResult & aRv)478 already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
479 const RequestOrUSVString& aInput,
480 const RequestInit& aInit,
481 CallerType aCallerType,
482 ErrorResult& aRv) {
483 RefPtr<Promise> p = Promise::Create(aGlobal, aRv);
484 if (NS_WARN_IF(aRv.Failed())) {
485 return nullptr;
486 }
487
488 MOZ_ASSERT(aGlobal);
489
490 // Double check that we have chrome privileges if the Request's content
491 // policy type has been overridden.
492 MOZ_ASSERT_IF(aInput.IsRequest() &&
493 aInput.GetAsRequest().IsContentPolicyTypeOverridden(),
494 aCallerType == CallerType::System);
495
496 AutoJSAPI jsapi;
497 if (!jsapi.Init(aGlobal)) {
498 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
499 return nullptr;
500 }
501
502 JSContext* cx = jsapi.cx();
503 JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
504 GlobalObject global(cx, jsGlobal);
505
506 SafeRefPtr<Request> request =
507 Request::Constructor(global, aInput, aInit, aRv);
508 if (aRv.Failed()) {
509 return nullptr;
510 }
511
512 SafeRefPtr<InternalRequest> r = request->GetInternalRequest();
513 RefPtr<AbortSignalImpl> signalImpl = request->GetSignalImpl();
514
515 if (signalImpl && signalImpl->Aborted()) {
516 // Already aborted signal rejects immediately.
517 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
518 return nullptr;
519 }
520
521 RefPtr<FetchObserver> observer;
522 if (aInit.mObserve.WasPassed()) {
523 observer = new FetchObserver(aGlobal, signalImpl);
524 aInit.mObserve.Value().HandleEvent(*observer);
525 }
526
527 if (NS_IsMainThread()) {
528 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
529 nsCOMPtr<Document> doc;
530 nsCOMPtr<nsILoadGroup> loadGroup;
531 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
532 nsIPrincipal* principal;
533 bool isTrackingFetch = false;
534 if (window) {
535 doc = window->GetExtantDoc();
536 if (!doc) {
537 aRv.Throw(NS_ERROR_FAILURE);
538 return nullptr;
539 }
540 principal = doc->NodePrincipal();
541 loadGroup = doc->GetDocumentLoadGroup();
542 cookieJarSettings = doc->CookieJarSettings();
543
544 isTrackingFetch = doc->IsScriptTracking(cx);
545 } else {
546 principal = aGlobal->PrincipalOrNull();
547 if (NS_WARN_IF(!principal)) {
548 aRv.Throw(NS_ERROR_FAILURE);
549 return nullptr;
550 }
551
552 cookieJarSettings = mozilla::net::CookieJarSettings::Create(principal);
553 }
554
555 if (!loadGroup) {
556 nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
557 if (NS_WARN_IF(NS_FAILED(rv))) {
558 aRv.Throw(rv);
559 return nullptr;
560 }
561 }
562
563 RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(
564 p, observer, signalImpl, request->MozErrors());
565 RefPtr<FetchDriver> fetch =
566 new FetchDriver(std::move(r), principal, loadGroup,
567 aGlobal->EventTargetFor(TaskCategory::Other),
568 cookieJarSettings, nullptr, // PerformanceStorage
569 isTrackingFetch);
570 fetch->SetDocument(doc);
571 resolver->SetLoadGroup(loadGroup);
572 aRv = fetch->Fetch(signalImpl, resolver);
573 if (NS_WARN_IF(aRv.Failed())) {
574 return nullptr;
575 }
576 } else {
577 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
578 MOZ_ASSERT(worker);
579
580 if (worker->IsServiceWorker()) {
581 r->SetSkipServiceWorker();
582 }
583
584 RefPtr<WorkerFetchResolver> resolver =
585 WorkerFetchResolver::Create(worker, p, signalImpl, observer);
586 if (!resolver) {
587 NS_WARNING("Could not keep the worker alive.");
588 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
589 return nullptr;
590 }
591
592 Maybe<ClientInfo> clientInfo(worker->GlobalScope()->GetClientInfo());
593 if (clientInfo.isNothing()) {
594 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
595 return nullptr;
596 }
597
598 UniquePtr<SerializedStackHolder> stack;
599 if (worker->IsWatchedByDevTools()) {
600 stack = GetCurrentStackForNetMonitor(cx);
601 }
602
603 RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(
604 resolver, clientInfo.ref(), worker->GlobalScope()->GetController(),
605 worker->CSPEventListener(), std::move(r), std::move(stack));
606 worker->DispatchToMainThread(run.forget());
607 }
608
609 return p.forget();
610 }
611
612 class ResolveFetchPromise : public Runnable {
613 public:
ResolveFetchPromise(Promise * aPromise,Response * aResponse)614 ResolveFetchPromise(Promise* aPromise, Response* aResponse)
615 : Runnable("ResolveFetchPromise"),
616 mPromise(aPromise),
617 mResponse(aResponse) {}
618
Run()619 NS_IMETHOD Run() {
620 mPromise->MaybeResolve(mResponse);
621 return NS_OK;
622 }
623 RefPtr<Promise> mPromise;
624 RefPtr<Response> mResponse;
625 };
626
OnResponseAvailableInternal(InternalResponse * aResponse)627 void MainThreadFetchResolver::OnResponseAvailableInternal(
628 InternalResponse* aResponse) {
629 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
630 AssertIsOnMainThread();
631
632 if (aResponse->Type() != ResponseType::Error) {
633 nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
634 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(go);
635
636 // Notify the document when a fetch completes successfully. This is
637 // used by the password manager as a hint to observe DOM mutations.
638 // Call this prior to setting state to Complete so we can set up the
639 // observer before mutations occurs.
640 Document* doc = inner ? inner->GetExtantDoc() : nullptr;
641 if (doc) {
642 doc->NotifyFetchOrXHRSuccess();
643 }
644
645 if (mFetchObserver) {
646 mFetchObserver->SetState(FetchState::Complete);
647 }
648
649 mResponse = new Response(go, aResponse, mSignalImpl);
650 BrowsingContext* bc = inner ? inner->GetBrowsingContext() : nullptr;
651 bc = bc ? bc->Top() : nullptr;
652 if (bc && bc->IsLoading()) {
653 bc->AddDeprioritizedLoadRunner(
654 new ResolveFetchPromise(mPromise, mResponse));
655 } else {
656 mPromise->MaybeResolve(mResponse);
657 }
658 } else {
659 if (mFetchObserver) {
660 mFetchObserver->SetState(FetchState::Errored);
661 }
662
663 if (mMozErrors) {
664 mPromise->MaybeReject(aResponse->GetErrorCode());
665 return;
666 }
667
668 mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
669 }
670 }
671
NeedOnDataAvailable()672 bool MainThreadFetchResolver::NeedOnDataAvailable() {
673 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
674 return !!mFetchObserver;
675 }
676
OnDataAvailable()677 void MainThreadFetchResolver::OnDataAvailable() {
678 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
679 AssertIsOnMainThread();
680
681 if (!mFetchObserver) {
682 return;
683 }
684
685 if (mFetchObserver->State() == FetchState::Requesting) {
686 mFetchObserver->SetState(FetchState::Responding);
687 }
688 }
689
~MainThreadFetchResolver()690 MainThreadFetchResolver::~MainThreadFetchResolver() {
691 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
692 }
693
694 class WorkerFetchResponseRunnable final : public MainThreadWorkerRunnable {
695 RefPtr<WorkerFetchResolver> mResolver;
696 // Passed from main thread to worker thread after being initialized.
697 RefPtr<InternalResponse> mInternalResponse;
698
699 public:
WorkerFetchResponseRunnable(WorkerPrivate * aWorkerPrivate,WorkerFetchResolver * aResolver,InternalResponse * aResponse)700 WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate,
701 WorkerFetchResolver* aResolver,
702 InternalResponse* aResponse)
703 : MainThreadWorkerRunnable(aWorkerPrivate),
704 mResolver(aResolver),
705 mInternalResponse(aResponse) {
706 MOZ_ASSERT(mResolver);
707 }
708
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)709 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
710 MOZ_ASSERT(aWorkerPrivate);
711 aWorkerPrivate->AssertIsOnWorkerThread();
712
713 RefPtr<Promise> promise = mResolver->WorkerPromise(aWorkerPrivate);
714 RefPtr<FetchObserver> fetchObserver =
715 mResolver->GetFetchObserver(aWorkerPrivate);
716
717 if (mInternalResponse->Type() != ResponseType::Error) {
718 if (fetchObserver) {
719 fetchObserver->SetState(FetchState::Complete);
720 }
721
722 RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
723 RefPtr<Response> response =
724 new Response(global, mInternalResponse,
725 mResolver->GetAbortSignalForTargetThread());
726 promise->MaybeResolve(response);
727 } else {
728 if (fetchObserver) {
729 fetchObserver->SetState(FetchState::Errored);
730 }
731
732 promise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
733 }
734 return true;
735 }
736 };
737
738 class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable {
739 RefPtr<WorkerFetchResolver> mResolver;
740
741 public:
WorkerDataAvailableRunnable(WorkerPrivate * aWorkerPrivate,WorkerFetchResolver * aResolver)742 WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate,
743 WorkerFetchResolver* aResolver)
744 : MainThreadWorkerRunnable(aWorkerPrivate), mResolver(aResolver) {}
745
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)746 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
747 MOZ_ASSERT(aWorkerPrivate);
748 aWorkerPrivate->AssertIsOnWorkerThread();
749
750 RefPtr<FetchObserver> fetchObserver =
751 mResolver->GetFetchObserver(aWorkerPrivate);
752
753 if (fetchObserver && fetchObserver->State() == FetchState::Requesting) {
754 fetchObserver->SetState(FetchState::Responding);
755 }
756
757 return true;
758 }
759 };
760
761 class WorkerFetchResponseEndBase {
762 protected:
763 RefPtr<WorkerFetchResolver> mResolver;
764
765 public:
WorkerFetchResponseEndBase(WorkerFetchResolver * aResolver)766 explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver)
767 : mResolver(aResolver) {
768 MOZ_ASSERT(aResolver);
769 }
770
WorkerRunInternal(WorkerPrivate * aWorkerPrivate)771 void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) {
772 mResolver->Shutdown(aWorkerPrivate);
773 }
774 };
775
776 class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable,
777 public WorkerFetchResponseEndBase {
778 FetchDriverObserver::EndReason mReason;
779
780 public:
WorkerFetchResponseEndRunnable(WorkerPrivate * aWorkerPrivate,WorkerFetchResolver * aResolver,FetchDriverObserver::EndReason aReason)781 WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate,
782 WorkerFetchResolver* aResolver,
783 FetchDriverObserver::EndReason aReason)
784 : MainThreadWorkerRunnable(aWorkerPrivate),
785 WorkerFetchResponseEndBase(aResolver),
786 mReason(aReason) {}
787
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)788 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
789 if (mResolver->IsShutdown(aWorkerPrivate)) {
790 return true;
791 }
792
793 if (mReason == FetchDriverObserver::eAborted) {
794 mResolver->WorkerPromise(aWorkerPrivate)
795 ->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
796 }
797
798 WorkerRunInternal(aWorkerPrivate);
799 return true;
800 }
801
Cancel()802 nsresult Cancel() override {
803 // Execute Run anyway to make sure we cleanup our promise proxy to avoid
804 // leaking the worker thread
805 Run();
806 return WorkerRunnable::Cancel();
807 }
808 };
809
810 class WorkerFetchResponseEndControlRunnable final
811 : public MainThreadWorkerControlRunnable,
812 public WorkerFetchResponseEndBase {
813 public:
WorkerFetchResponseEndControlRunnable(WorkerPrivate * aWorkerPrivate,WorkerFetchResolver * aResolver)814 WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate,
815 WorkerFetchResolver* aResolver)
816 : MainThreadWorkerControlRunnable(aWorkerPrivate),
817 WorkerFetchResponseEndBase(aResolver) {}
818
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)819 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
820 WorkerRunInternal(aWorkerPrivate);
821 return true;
822 }
823
824 // Control runnable cancel already calls Run().
825 };
826
OnResponseAvailableInternal(InternalResponse * aResponse)827 void WorkerFetchResolver::OnResponseAvailableInternal(
828 InternalResponse* aResponse) {
829 AssertIsOnMainThread();
830
831 MutexAutoLock lock(mPromiseProxy->Lock());
832 if (mPromiseProxy->CleanedUp()) {
833 return;
834 }
835
836 RefPtr<WorkerFetchResponseRunnable> r = new WorkerFetchResponseRunnable(
837 mPromiseProxy->GetWorkerPrivate(), this, aResponse);
838
839 if (!r->Dispatch()) {
840 NS_WARNING("Could not dispatch fetch response");
841 }
842 }
843
NeedOnDataAvailable()844 bool WorkerFetchResolver::NeedOnDataAvailable() {
845 AssertIsOnMainThread();
846 return mNeedOnDataAvailable;
847 }
848
OnDataAvailable()849 void WorkerFetchResolver::OnDataAvailable() {
850 AssertIsOnMainThread();
851
852 MutexAutoLock lock(mPromiseProxy->Lock());
853 if (mPromiseProxy->CleanedUp()) {
854 return;
855 }
856
857 RefPtr<WorkerDataAvailableRunnable> r =
858 new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this);
859 Unused << r->Dispatch();
860 }
861
OnResponseEnd(FetchDriverObserver::EndReason aReason)862 void WorkerFetchResolver::OnResponseEnd(
863 FetchDriverObserver::EndReason aReason) {
864 AssertIsOnMainThread();
865 MutexAutoLock lock(mPromiseProxy->Lock());
866 if (mPromiseProxy->CleanedUp()) {
867 return;
868 }
869
870 FlushConsoleReport();
871
872 RefPtr<WorkerFetchResponseEndRunnable> r = new WorkerFetchResponseEndRunnable(
873 mPromiseProxy->GetWorkerPrivate(), this, aReason);
874
875 if (!r->Dispatch()) {
876 RefPtr<WorkerFetchResponseEndControlRunnable> cr =
877 new WorkerFetchResponseEndControlRunnable(
878 mPromiseProxy->GetWorkerPrivate(), this);
879 // This can fail if the worker thread is canceled or killed causing
880 // the PromiseWorkerProxy to give up its WorkerRef immediately,
881 // allowing the worker thread to become Dead.
882 if (!cr->Dispatch()) {
883 NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
884 }
885 }
886 }
887
FlushConsoleReport()888 void WorkerFetchResolver::FlushConsoleReport() {
889 AssertIsOnMainThread();
890 MOZ_ASSERT(mPromiseProxy);
891
892 if (!mReporter) {
893 return;
894 }
895
896 WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
897 if (!worker) {
898 mReporter->FlushReportsToConsole(0);
899 return;
900 }
901
902 if (worker->IsServiceWorker()) {
903 // Flush to service worker
904 mReporter->FlushReportsToConsoleForServiceWorkerScope(
905 worker->ServiceWorkerScope());
906 return;
907 }
908
909 if (worker->IsSharedWorker()) {
910 // Flush to shared worker
911 worker->GetRemoteWorkerController()->FlushReportsOnMainThread(mReporter);
912 return;
913 }
914
915 // Flush to dedicated worker
916 mReporter->FlushConsoleReports(worker->GetLoadGroup());
917 }
918
ExtractByteStreamFromBody(const fetch::OwningBodyInit & aBodyInit,nsIInputStream ** aStream,nsCString & aContentTypeWithCharset,uint64_t & aContentLength)919 nsresult ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
920 nsIInputStream** aStream,
921 nsCString& aContentTypeWithCharset,
922 uint64_t& aContentLength) {
923 MOZ_ASSERT(aStream);
924 nsAutoCString charset;
925 aContentTypeWithCharset.SetIsVoid(true);
926
927 if (aBodyInit.IsArrayBuffer()) {
928 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
929 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
930 charset);
931 }
932
933 if (aBodyInit.IsArrayBufferView()) {
934 BodyExtractor<const ArrayBufferView> body(
935 &aBodyInit.GetAsArrayBufferView());
936 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
937 charset);
938 }
939
940 if (aBodyInit.IsBlob()) {
941 Blob& blob = aBodyInit.GetAsBlob();
942 BodyExtractor<const Blob> body(&blob);
943 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
944 charset);
945 }
946
947 if (aBodyInit.IsFormData()) {
948 FormData& formData = aBodyInit.GetAsFormData();
949 BodyExtractor<const FormData> body(&formData);
950 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
951 charset);
952 }
953
954 if (aBodyInit.IsUSVString()) {
955 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
956 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
957 charset);
958 }
959
960 if (aBodyInit.IsURLSearchParams()) {
961 URLSearchParams& usp = aBodyInit.GetAsURLSearchParams();
962 BodyExtractor<const URLSearchParams> body(&usp);
963 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
964 charset);
965 }
966
967 MOZ_ASSERT_UNREACHABLE("Should never reach here");
968 return NS_ERROR_FAILURE;
969 }
970
ExtractByteStreamFromBody(const fetch::BodyInit & aBodyInit,nsIInputStream ** aStream,nsCString & aContentTypeWithCharset,uint64_t & aContentLength)971 nsresult ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
972 nsIInputStream** aStream,
973 nsCString& aContentTypeWithCharset,
974 uint64_t& aContentLength) {
975 MOZ_ASSERT(aStream);
976 MOZ_ASSERT(!*aStream);
977
978 nsAutoCString charset;
979 aContentTypeWithCharset.SetIsVoid(true);
980
981 if (aBodyInit.IsArrayBuffer()) {
982 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
983 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
984 charset);
985 }
986
987 if (aBodyInit.IsArrayBufferView()) {
988 BodyExtractor<const ArrayBufferView> body(
989 &aBodyInit.GetAsArrayBufferView());
990 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
991 charset);
992 }
993
994 if (aBodyInit.IsBlob()) {
995 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob());
996 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
997 charset);
998 }
999
1000 if (aBodyInit.IsFormData()) {
1001 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData());
1002 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1003 charset);
1004 }
1005
1006 if (aBodyInit.IsUSVString()) {
1007 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
1008 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1009 charset);
1010 }
1011
1012 if (aBodyInit.IsURLSearchParams()) {
1013 BodyExtractor<const URLSearchParams> body(
1014 &aBodyInit.GetAsURLSearchParams());
1015 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1016 charset);
1017 }
1018
1019 MOZ_ASSERT_UNREACHABLE("Should never reach here");
1020 return NS_ERROR_FAILURE;
1021 }
1022
ExtractByteStreamFromBody(const fetch::ResponseBodyInit & aBodyInit,nsIInputStream ** aStream,nsCString & aContentTypeWithCharset,uint64_t & aContentLength)1023 nsresult ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
1024 nsIInputStream** aStream,
1025 nsCString& aContentTypeWithCharset,
1026 uint64_t& aContentLength) {
1027 MOZ_ASSERT(aStream);
1028 MOZ_ASSERT(!*aStream);
1029
1030 // ReadableStreams should be handled by
1031 // BodyExtractorReadableStream::GetAsStream.
1032 MOZ_ASSERT(!aBodyInit.IsReadableStream());
1033
1034 nsAutoCString charset;
1035 aContentTypeWithCharset.SetIsVoid(true);
1036
1037 if (aBodyInit.IsArrayBuffer()) {
1038 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
1039 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1040 charset);
1041 }
1042
1043 if (aBodyInit.IsArrayBufferView()) {
1044 BodyExtractor<const ArrayBufferView> body(
1045 &aBodyInit.GetAsArrayBufferView());
1046 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1047 charset);
1048 }
1049
1050 if (aBodyInit.IsBlob()) {
1051 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob());
1052 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1053 charset);
1054 }
1055
1056 if (aBodyInit.IsFormData()) {
1057 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData());
1058 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1059 charset);
1060 }
1061
1062 if (aBodyInit.IsUSVString()) {
1063 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
1064 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1065 charset);
1066 }
1067
1068 if (aBodyInit.IsURLSearchParams()) {
1069 BodyExtractor<const URLSearchParams> body(
1070 &aBodyInit.GetAsURLSearchParams());
1071 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1072 charset);
1073 }
1074
1075 MOZ_ASSERT_UNREACHABLE("Should never reach here");
1076 return NS_ERROR_FAILURE;
1077 }
1078
1079 template <class Derived>
FetchBody(nsIGlobalObject * aOwner)1080 FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
1081 : mOwner(aOwner),
1082 mWorkerPrivate(nullptr),
1083 mReadableStreamBody(nullptr),
1084 mReadableStreamReader(nullptr),
1085 mBodyUsed(false) {
1086 MOZ_ASSERT(aOwner);
1087
1088 if (!NS_IsMainThread()) {
1089 mWorkerPrivate = GetCurrentThreadWorkerPrivate();
1090 MOZ_ASSERT(mWorkerPrivate);
1091 mMainThreadEventTarget = mWorkerPrivate->MainThreadEventTarget();
1092 } else {
1093 mMainThreadEventTarget = aOwner->EventTargetFor(TaskCategory::Other);
1094 }
1095
1096 MOZ_ASSERT(mMainThreadEventTarget);
1097 }
1098
1099 template FetchBody<Request>::FetchBody(nsIGlobalObject* aOwner);
1100
1101 template FetchBody<Response>::FetchBody(nsIGlobalObject* aOwner);
1102
1103 template <class Derived>
~FetchBody()1104 FetchBody<Derived>::~FetchBody() {
1105 Unfollow();
1106 }
1107
1108 template FetchBody<Request>::~FetchBody();
1109
1110 template FetchBody<Response>::~FetchBody();
1111
1112 template <class Derived>
GetBodyUsed(ErrorResult & aRv) const1113 bool FetchBody<Derived>::GetBodyUsed(ErrorResult& aRv) const {
1114 if (mBodyUsed) {
1115 return true;
1116 }
1117
1118 // If this stream is disturbed, return true.
1119 if (mReadableStreamBody) {
1120 aRv.MightThrowJSException();
1121
1122 AutoJSAPI jsapi;
1123 if (!jsapi.Init(mOwner)) {
1124 aRv.Throw(NS_ERROR_FAILURE);
1125 return true;
1126 }
1127
1128 JSContext* cx = jsapi.cx();
1129 JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
1130 bool disturbed;
1131 if (!JS::ReadableStreamIsDisturbed(cx, body, &disturbed)) {
1132 aRv.StealExceptionFromJSContext(cx);
1133 return false;
1134 }
1135
1136 return disturbed;
1137 }
1138
1139 return false;
1140 }
1141
1142 template bool FetchBody<Request>::GetBodyUsed(ErrorResult&) const;
1143
1144 template bool FetchBody<Response>::GetBodyUsed(ErrorResult&) const;
1145
1146 template <class Derived>
CheckBodyUsed() const1147 bool FetchBody<Derived>::CheckBodyUsed() const {
1148 IgnoredErrorResult result;
1149 bool bodyUsed = GetBodyUsed(result);
1150 if (result.Failed()) {
1151 // Ignore the error.
1152 return true;
1153 }
1154 return bodyUsed;
1155 }
1156
1157 template <class Derived>
SetBodyUsed(JSContext * aCx,ErrorResult & aRv)1158 void FetchBody<Derived>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv) {
1159 MOZ_ASSERT(aCx);
1160 MOZ_ASSERT(mOwner->EventTargetFor(TaskCategory::Other)->IsOnCurrentThread());
1161
1162 if (mBodyUsed) {
1163 return;
1164 }
1165
1166 mBodyUsed = true;
1167
1168 // If we already have a ReadableStreamBody and it has been created by DOM, we
1169 // have to lock it now because it can have been shared with other objects.
1170 if (mReadableStreamBody) {
1171 aRv.MightThrowJSException();
1172
1173 JSAutoRealm ar(aCx, mOwner->GetGlobalJSObject());
1174
1175 JS::Rooted<JSObject*> readableStreamObj(aCx, mReadableStreamBody);
1176
1177 JS::ReadableStreamMode mode;
1178 if (!JS::ReadableStreamGetMode(aCx, readableStreamObj, &mode)) {
1179 aRv.StealExceptionFromJSContext(aCx);
1180 return;
1181 }
1182
1183 if (mode == JS::ReadableStreamMode::ExternalSource) {
1184 LockStream(aCx, readableStreamObj, aRv);
1185 if (NS_WARN_IF(aRv.Failed())) {
1186 return;
1187 }
1188 } else {
1189 // If this is not a native ReadableStream, let's activate the
1190 // FetchStreamReader.
1191 MOZ_ASSERT(mFetchStreamReader);
1192 JS::Rooted<JSObject*> reader(aCx);
1193 mFetchStreamReader->StartConsuming(aCx, readableStreamObj, &reader, aRv);
1194 if (NS_WARN_IF(aRv.Failed())) {
1195 return;
1196 }
1197
1198 mReadableStreamReader = reader;
1199 }
1200 }
1201 }
1202
1203 template void FetchBody<Request>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
1204
1205 template void FetchBody<Response>::SetBodyUsed(JSContext* aCx,
1206 ErrorResult& aRv);
1207
1208 template <class Derived>
ConsumeBody(JSContext * aCx,BodyConsumer::ConsumeType aType,ErrorResult & aRv)1209 already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
1210 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv) {
1211 aRv.MightThrowJSException();
1212
1213 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
1214 if (signalImpl && signalImpl->Aborted()) {
1215 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
1216 return nullptr;
1217 }
1218
1219 bool bodyUsed = GetBodyUsed(aRv);
1220 if (NS_WARN_IF(aRv.Failed())) {
1221 return nullptr;
1222 }
1223 if (bodyUsed) {
1224 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
1225 return nullptr;
1226 }
1227
1228 nsAutoCString mimeType;
1229 DerivedClass()->GetMimeType(mimeType);
1230
1231 // Null bodies are a special-case in the fetch spec. The Body mix-in can only
1232 // be "disturbed" or "locked" if its associated "body" is non-null.
1233 // Additionally, the Body min-in's "consume body" algorithm explicitly creates
1234 // a fresh empty ReadableStream object in step 2. This means that `bodyUsed`
1235 // will never return true for a null body.
1236 //
1237 // To this end, we create a fresh (empty) body every time a request is made
1238 // and consume its body here, without marking this FetchBody consumed via
1239 // SetBodyUsed.
1240 nsCOMPtr<nsIInputStream> bodyStream;
1241 DerivedClass()->GetBody(getter_AddRefs(bodyStream));
1242 if (!bodyStream) {
1243 RefPtr<EmptyBody> emptyBody = EmptyBody::Create(
1244 DerivedClass()->GetParentObject(),
1245 DerivedClass()->GetPrincipalInfo().get(), signalImpl, mimeType, aRv);
1246 if (NS_WARN_IF(aRv.Failed())) {
1247 return nullptr;
1248 }
1249
1250 return emptyBody->ConsumeBody(aCx, aType, aRv);
1251 }
1252
1253 SetBodyUsed(aCx, aRv);
1254 if (NS_WARN_IF(aRv.Failed())) {
1255 return nullptr;
1256 }
1257
1258 nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
1259
1260 MutableBlobStorage::MutableBlobStorageType blobStorageType =
1261 MutableBlobStorage::eOnlyInMemory;
1262 const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
1263 DerivedClass()->GetPrincipalInfo();
1264 // We support temporary file for blobs only if the principal is known and
1265 // it's system or content not in private Browsing.
1266 if (principalInfo &&
1267 (principalInfo->type() ==
1268 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
1269 (principalInfo->type() ==
1270 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1271 principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId ==
1272 0))) {
1273 blobStorageType = MutableBlobStorage::eCouldBeInTemporaryFile;
1274 }
1275
1276 RefPtr<Promise> promise = BodyConsumer::Create(
1277 global, mMainThreadEventTarget, bodyStream, signalImpl, aType,
1278 BodyBlobURISpec(), BodyLocalPath(), mimeType, blobStorageType, aRv);
1279 if (NS_WARN_IF(aRv.Failed())) {
1280 return nullptr;
1281 }
1282
1283 return promise.forget();
1284 }
1285
1286 template already_AddRefed<Promise> FetchBody<Request>::ConsumeBody(
1287 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1288
1289 template already_AddRefed<Promise> FetchBody<Response>::ConsumeBody(
1290 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1291
1292 template already_AddRefed<Promise> FetchBody<EmptyBody>::ConsumeBody(
1293 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1294
1295 template <class Derived>
GetMimeType(nsACString & aMimeType)1296 void FetchBody<Derived>::GetMimeType(nsACString& aMimeType) {
1297 // Extract mime type.
1298 ErrorResult result;
1299 nsCString contentTypeValues;
1300 MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
1301 DerivedClass()->GetInternalHeaders()->Get("Content-Type"_ns,
1302 contentTypeValues, result);
1303 MOZ_ALWAYS_TRUE(!result.Failed());
1304
1305 // HTTP ABNF states Content-Type may have only one value.
1306 // This is from the "parse a header value" of the fetch spec.
1307 if (!contentTypeValues.IsVoid() && contentTypeValues.Find(",") == -1) {
1308 // Convert from a bytestring to a UTF8 CString.
1309 CopyLatin1toUTF8(contentTypeValues, aMimeType);
1310 ToLowerCase(aMimeType);
1311 }
1312 }
1313
1314 template void FetchBody<Request>::GetMimeType(nsACString& aMimeType);
1315 template void FetchBody<Response>::GetMimeType(nsACString& aMimeType);
1316
1317 template <class Derived>
BodyBlobURISpec() const1318 const nsACString& FetchBody<Derived>::BodyBlobURISpec() const {
1319 return DerivedClass()->BodyBlobURISpec();
1320 }
1321
1322 template const nsACString& FetchBody<Request>::BodyBlobURISpec() const;
1323
1324 template const nsACString& FetchBody<Response>::BodyBlobURISpec() const;
1325
1326 template const nsACString& FetchBody<EmptyBody>::BodyBlobURISpec() const;
1327
1328 template <class Derived>
BodyLocalPath() const1329 const nsAString& FetchBody<Derived>::BodyLocalPath() const {
1330 return DerivedClass()->BodyLocalPath();
1331 }
1332
1333 template const nsAString& FetchBody<Request>::BodyLocalPath() const;
1334
1335 template const nsAString& FetchBody<Response>::BodyLocalPath() const;
1336
1337 template const nsAString& FetchBody<EmptyBody>::BodyLocalPath() const;
1338
1339 template <class Derived>
SetReadableStreamBody(JSContext * aCx,JSObject * aBody)1340 void FetchBody<Derived>::SetReadableStreamBody(JSContext* aCx,
1341 JSObject* aBody) {
1342 MOZ_ASSERT(!mReadableStreamBody);
1343 MOZ_ASSERT(aBody);
1344 mReadableStreamBody = aBody;
1345
1346 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
1347 if (!signalImpl) {
1348 return;
1349 }
1350
1351 bool aborted = signalImpl->Aborted();
1352 if (aborted) {
1353 JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
1354 IgnoredErrorResult result;
1355 AbortStream(aCx, body, result);
1356 if (NS_WARN_IF(result.Failed())) {
1357 return;
1358 }
1359 } else if (!IsFollowing()) {
1360 Follow(signalImpl);
1361 }
1362 }
1363
1364 template void FetchBody<Request>::SetReadableStreamBody(JSContext* aCx,
1365 JSObject* aBody);
1366
1367 template void FetchBody<Response>::SetReadableStreamBody(JSContext* aCx,
1368 JSObject* aBody);
1369
1370 template <class Derived>
GetBody(JSContext * aCx,JS::MutableHandle<JSObject * > aBodyOut,ErrorResult & aRv)1371 void FetchBody<Derived>::GetBody(JSContext* aCx,
1372 JS::MutableHandle<JSObject*> aBodyOut,
1373 ErrorResult& aRv) {
1374 if (mReadableStreamBody) {
1375 aBodyOut.set(mReadableStreamBody);
1376 return;
1377 }
1378
1379 nsCOMPtr<nsIInputStream> inputStream;
1380 DerivedClass()->GetBody(getter_AddRefs(inputStream));
1381
1382 if (!inputStream) {
1383 aBodyOut.set(nullptr);
1384 return;
1385 }
1386
1387 BodyStream::Create(aCx, this, DerivedClass()->GetParentObject(), inputStream,
1388 aRv);
1389 if (NS_WARN_IF(aRv.Failed())) {
1390 return;
1391 }
1392
1393 MOZ_ASSERT(mReadableStreamBody);
1394
1395 JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
1396
1397 // If the body has been already consumed, we lock the stream.
1398 bool bodyUsed = GetBodyUsed(aRv);
1399 if (NS_WARN_IF(aRv.Failed())) {
1400 return;
1401 }
1402 if (bodyUsed) {
1403 LockStream(aCx, body, aRv);
1404 if (NS_WARN_IF(aRv.Failed())) {
1405 return;
1406 }
1407 }
1408
1409 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
1410 if (signalImpl) {
1411 if (signalImpl->Aborted()) {
1412 AbortStream(aCx, body, aRv);
1413 if (NS_WARN_IF(aRv.Failed())) {
1414 return;
1415 }
1416 } else if (!IsFollowing()) {
1417 Follow(signalImpl);
1418 }
1419 }
1420
1421 aBodyOut.set(mReadableStreamBody);
1422 }
1423
1424 template void FetchBody<Request>::GetBody(JSContext* aCx,
1425 JS::MutableHandle<JSObject*> aMessage,
1426 ErrorResult& aRv);
1427
1428 template void FetchBody<Response>::GetBody(
1429 JSContext* aCx, JS::MutableHandle<JSObject*> aMessage, ErrorResult& aRv);
1430
1431 template <class Derived>
LockStream(JSContext * aCx,JS::HandleObject aStream,ErrorResult & aRv)1432 void FetchBody<Derived>::LockStream(JSContext* aCx, JS::HandleObject aStream,
1433 ErrorResult& aRv) {
1434 aRv.MightThrowJSException();
1435
1436 #if DEBUG
1437 JS::ReadableStreamMode streamMode;
1438 if (!JS::ReadableStreamGetMode(aCx, aStream, &streamMode)) {
1439 aRv.StealExceptionFromJSContext(aCx);
1440 return;
1441 }
1442 MOZ_ASSERT(streamMode == JS::ReadableStreamMode::ExternalSource);
1443 #endif // DEBUG
1444
1445 // This is native stream, creating a reader will not execute any JS code.
1446 JS::Rooted<JSObject*> reader(
1447 aCx, JS::ReadableStreamGetReader(aCx, aStream,
1448 JS::ReadableStreamReaderMode::Default));
1449 if (!reader) {
1450 aRv.StealExceptionFromJSContext(aCx);
1451 return;
1452 }
1453
1454 mReadableStreamReader = reader;
1455 }
1456
1457 template void FetchBody<Request>::LockStream(JSContext* aCx,
1458 JS::HandleObject aStream,
1459 ErrorResult& aRv);
1460
1461 template void FetchBody<Response>::LockStream(JSContext* aCx,
1462 JS::HandleObject aStream,
1463 ErrorResult& aRv);
1464
1465 template <class Derived>
MaybeTeeReadableStreamBody(JSContext * aCx,JS::MutableHandle<JSObject * > aBodyOut,FetchStreamReader ** aStreamReader,nsIInputStream ** aInputStream,ErrorResult & aRv)1466 void FetchBody<Derived>::MaybeTeeReadableStreamBody(
1467 JSContext* aCx, JS::MutableHandle<JSObject*> aBodyOut,
1468 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1469 ErrorResult& aRv) {
1470 MOZ_DIAGNOSTIC_ASSERT(aStreamReader);
1471 MOZ_DIAGNOSTIC_ASSERT(aInputStream);
1472 MOZ_DIAGNOSTIC_ASSERT(!CheckBodyUsed());
1473
1474 aBodyOut.set(nullptr);
1475 *aStreamReader = nullptr;
1476 *aInputStream = nullptr;
1477
1478 if (!mReadableStreamBody) {
1479 return;
1480 }
1481
1482 aRv.MightThrowJSException();
1483
1484 JSAutoRealm ar(aCx, mOwner->GetGlobalJSObject());
1485
1486 JS::Rooted<JSObject*> stream(aCx, mReadableStreamBody);
1487
1488 // If this is a ReadableStream with an external source, this has been
1489 // generated by a Fetch. In this case, Fetch will be able to recreate it
1490 // again when GetBody() is called.
1491 JS::ReadableStreamMode streamMode;
1492 if (!JS::ReadableStreamGetMode(aCx, stream, &streamMode)) {
1493 aRv.StealExceptionFromJSContext(aCx);
1494 return;
1495 }
1496 if (streamMode == JS::ReadableStreamMode::ExternalSource) {
1497 aBodyOut.set(nullptr);
1498 return;
1499 }
1500
1501 JS::Rooted<JSObject*> branch1(aCx);
1502 JS::Rooted<JSObject*> branch2(aCx);
1503
1504 if (!JS::ReadableStreamTee(aCx, stream, &branch1, &branch2)) {
1505 aRv.StealExceptionFromJSContext(aCx);
1506 return;
1507 }
1508
1509 mReadableStreamBody = branch1;
1510 aBodyOut.set(branch2);
1511
1512 aRv = FetchStreamReader::Create(aCx, mOwner, aStreamReader, aInputStream);
1513 if (NS_WARN_IF(aRv.Failed())) {
1514 return;
1515 }
1516 }
1517
1518 template void FetchBody<Request>::MaybeTeeReadableStreamBody(
1519 JSContext* aCx, JS::MutableHandle<JSObject*> aMessage,
1520 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1521 ErrorResult& aRv);
1522
1523 template void FetchBody<Response>::MaybeTeeReadableStreamBody(
1524 JSContext* aCx, JS::MutableHandle<JSObject*> aMessage,
1525 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1526 ErrorResult& aRv);
1527
1528 template <class Derived>
RunAbortAlgorithm()1529 void FetchBody<Derived>::RunAbortAlgorithm() {
1530 if (!mReadableStreamBody) {
1531 return;
1532 }
1533
1534 AutoJSAPI jsapi;
1535 if (!jsapi.Init(mOwner)) {
1536 return;
1537 }
1538
1539 JSContext* cx = jsapi.cx();
1540
1541 JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
1542 IgnoredErrorResult result;
1543 AbortStream(cx, body, result);
1544 }
1545
1546 template void FetchBody<Request>::RunAbortAlgorithm();
1547
1548 template void FetchBody<Response>::RunAbortAlgorithm();
1549
1550 } // namespace mozilla::dom
1551