Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / fetch / Fetch.cpp
blob965daf35ebbd5f8731dabd211115b6670a454861
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 #include "Fetch.h"
9 #include "js/RootingAPI.h"
10 #include "js/Value.h"
11 #include "mozilla/CycleCollectedJSContext.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/ipc/BackgroundChild.h"
15 #include "mozilla/ipc/PBackgroundChild.h"
16 #include "mozilla/ipc/PBackgroundSharedTypes.h"
17 #include "mozilla/ipc/IPCStreamUtils.h"
18 #include "nsIGlobalObject.h"
20 #include "nsDOMString.h"
21 #include "nsJSUtils.h"
22 #include "nsNetUtil.h"
23 #include "nsReadableUtils.h"
24 #include "nsStreamUtils.h"
25 #include "nsStringStream.h"
26 #include "nsProxyRelease.h"
28 #include "mozilla/ErrorResult.h"
29 #include "mozilla/dom/BindingDeclarations.h"
30 #include "mozilla/dom/BodyConsumer.h"
31 #include "mozilla/dom/Exceptions.h"
32 #include "mozilla/dom/DOMException.h"
33 #include "mozilla/dom/FetchDriver.h"
34 #include "mozilla/dom/File.h"
35 #include "mozilla/dom/FormData.h"
36 #include "mozilla/dom/Headers.h"
37 #include "mozilla/dom/Promise.h"
38 #include "mozilla/dom/PromiseWorkerProxy.h"
39 #include "mozilla/dom/ReadableStreamDefaultReader.h"
40 #include "mozilla/dom/RemoteWorkerChild.h"
41 #include "mozilla/dom/Request.h"
42 #include "mozilla/dom/Response.h"
43 #include "mozilla/dom/ScriptSettings.h"
44 #include "mozilla/dom/URLSearchParams.h"
45 #include "mozilla/net/CookieJarSettings.h"
47 #include "BodyExtractor.h"
48 #include "FetchChild.h"
49 #include "FetchObserver.h"
50 #include "InternalRequest.h"
51 #include "InternalResponse.h"
53 #include "mozilla/dom/WorkerCommon.h"
54 #include "mozilla/dom/WorkerRef.h"
55 #include "mozilla/dom/WorkerRunnable.h"
56 #include "mozilla/dom/WorkerScope.h"
58 namespace mozilla::dom {
60 namespace {
62 // Step 17.2.1.2 and 17.2.2 of
63 // https://fetch.spec.whatwg.org/#concept-http-network-fetch
64 // If stream is readable, then error stream with ...
65 void AbortStream(JSContext* aCx, ReadableStream* aReadableStream,
66 ErrorResult& aRv, JS::Handle<JS::Value> aReasonDetails) {
67 if (aReadableStream->State() != ReadableStream::ReaderState::Readable) {
68 return;
71 JS::Rooted<JS::Value> value(aCx, aReasonDetails);
73 if (aReasonDetails.isUndefined()) {
74 RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
75 if (!GetOrCreateDOMReflector(aCx, e, &value)) {
76 return;
80 aReadableStream->ErrorNative(aCx, value, aRv);
83 } // namespace
85 class AbortSignalMainThread final : public AbortSignalImpl {
86 public:
87 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
88 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortSignalMainThread)
90 explicit AbortSignalMainThread(bool aAborted)
91 : AbortSignalImpl(aAborted, JS::UndefinedHandleValue) {
92 mozilla::HoldJSObjects(this);
95 private:
96 ~AbortSignalMainThread() { mozilla::DropJSObjects(this); };
99 NS_IMPL_CYCLE_COLLECTION_CLASS(AbortSignalMainThread)
101 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortSignalMainThread)
102 AbortSignalImpl::Unlink(static_cast<AbortSignalImpl*>(tmp));
103 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortSignalMainThread)
106 AbortSignalImpl::Traverse(static_cast<AbortSignalImpl*>(tmp), cb);
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
109 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortSignalMainThread)
110 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
111 NS_IMPL_CYCLE_COLLECTION_TRACE_END
113 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignalMainThread)
114 NS_INTERFACE_MAP_ENTRY(nsISupports)
115 NS_INTERFACE_MAP_END
117 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortSignalMainThread)
118 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortSignalMainThread)
120 class AbortSignalProxy;
122 // This runnable propagates changes from the AbortSignalImpl on workers to the
123 // AbortSignalImpl on main-thread.
124 class AbortSignalProxyRunnable final : public Runnable {
125 RefPtr<AbortSignalProxy> mProxy;
127 public:
128 explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy)
129 : Runnable("dom::AbortSignalProxyRunnable"), mProxy(aProxy) {}
131 NS_IMETHOD Run() override;
134 // This class orchestrates the proxying of AbortSignal operations between the
135 // main thread and a worker thread.
136 class AbortSignalProxy final : public AbortFollower {
137 // This is created and released on the main-thread.
138 RefPtr<AbortSignalImpl> mSignalImplMainThread;
140 // The main-thread event target for runnable dispatching.
141 nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
143 // This value is used only when creating mSignalImplMainThread on the main
144 // thread, to create it in already-aborted state if necessary. It does *not*
145 // reflect the instantaneous is-aborted status of the worker thread's
146 // AbortSignal.
147 const bool mAborted;
149 public:
150 NS_DECL_THREADSAFE_ISUPPORTS
152 AbortSignalProxy(AbortSignalImpl* aSignalImpl,
153 nsIEventTarget* aMainThreadEventTarget)
154 : mMainThreadEventTarget(aMainThreadEventTarget),
155 mAborted(aSignalImpl->Aborted()) {
156 MOZ_ASSERT(!NS_IsMainThread());
157 MOZ_ASSERT(mMainThreadEventTarget);
158 Follow(aSignalImpl);
161 // AbortFollower
162 void RunAbortAlgorithm() override;
164 AbortSignalImpl* GetOrCreateSignalImplForMainThread() {
165 MOZ_ASSERT(NS_IsMainThread());
166 if (!mSignalImplMainThread) {
167 mSignalImplMainThread = new AbortSignalMainThread(mAborted);
169 return mSignalImplMainThread;
172 AbortSignalImpl* GetSignalImplForTargetThread() {
173 MOZ_ASSERT(!NS_IsMainThread());
174 return Signal();
177 nsIEventTarget* MainThreadEventTarget() { return mMainThreadEventTarget; }
179 void Shutdown() {
180 MOZ_ASSERT(!NS_IsMainThread());
181 Unfollow();
184 private:
185 ~AbortSignalProxy() {
186 NS_ProxyRelease("AbortSignalProxy::mSignalImplMainThread",
187 mMainThreadEventTarget, mSignalImplMainThread.forget());
191 NS_IMPL_ISUPPORTS0(AbortSignalProxy)
193 NS_IMETHODIMP AbortSignalProxyRunnable::Run() {
194 MOZ_ASSERT(NS_IsMainThread());
195 AbortSignalImpl* signalImpl = mProxy->GetOrCreateSignalImplForMainThread();
196 signalImpl->SignalAbort(JS::UndefinedHandleValue);
197 return NS_OK;
200 void AbortSignalProxy::RunAbortAlgorithm() {
201 MOZ_ASSERT(!NS_IsMainThread());
202 RefPtr<AbortSignalProxyRunnable> runnable =
203 new AbortSignalProxyRunnable(this);
204 MainThreadEventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
207 class WorkerFetchResolver final : public FetchDriverObserver {
208 // Thread-safe:
209 RefPtr<PromiseWorkerProxy> mPromiseProxy;
210 RefPtr<AbortSignalProxy> mSignalProxy;
212 // Touched only on the worker thread.
213 RefPtr<FetchObserver> mFetchObserver;
214 RefPtr<WeakWorkerRef> mWorkerRef;
215 bool mIsShutdown;
217 Atomic<bool> mNeedOnDataAvailable;
219 public:
220 // Returns null if worker is shutting down.
221 static already_AddRefed<WorkerFetchResolver> Create(
222 WorkerPrivate* aWorkerPrivate, Promise* aPromise,
223 AbortSignalImpl* aSignalImpl, FetchObserver* aObserver) {
224 MOZ_ASSERT(aWorkerPrivate);
225 aWorkerPrivate->AssertIsOnWorkerThread();
226 RefPtr<PromiseWorkerProxy> proxy =
227 PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
228 if (!proxy) {
229 return nullptr;
232 RefPtr<AbortSignalProxy> signalProxy;
233 if (aSignalImpl) {
234 signalProxy = new AbortSignalProxy(
235 aSignalImpl, aWorkerPrivate->MainThreadEventTarget());
238 RefPtr<WorkerFetchResolver> r =
239 new WorkerFetchResolver(proxy, signalProxy, aObserver);
241 RefPtr<WeakWorkerRef> workerRef = WeakWorkerRef::Create(
242 aWorkerPrivate, [r]() { r->Shutdown(r->mWorkerRef->GetPrivate()); });
243 if (NS_WARN_IF(!workerRef)) {
244 return nullptr;
247 r->mWorkerRef = std::move(workerRef);
249 return r.forget();
252 AbortSignalImpl* GetAbortSignalForMainThread() {
253 MOZ_ASSERT(NS_IsMainThread());
255 if (!mSignalProxy) {
256 return nullptr;
259 return mSignalProxy->GetOrCreateSignalImplForMainThread();
262 AbortSignalImpl* GetAbortSignalForTargetThread() {
263 mPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
265 if (!mSignalProxy) {
266 return nullptr;
269 return mSignalProxy->GetSignalImplForTargetThread();
272 PromiseWorkerProxy* PromiseProxy() const {
273 MOZ_ASSERT(NS_IsMainThread());
274 return mPromiseProxy;
277 Promise* WorkerPromise(WorkerPrivate* aWorkerPrivate) const {
278 MOZ_ASSERT(aWorkerPrivate);
279 aWorkerPrivate->AssertIsOnWorkerThread();
280 MOZ_ASSERT(!mIsShutdown);
282 return mPromiseProxy->GetWorkerPromise();
285 FetchObserver* GetFetchObserver(WorkerPrivate* aWorkerPrivate) const {
286 MOZ_ASSERT(aWorkerPrivate);
287 aWorkerPrivate->AssertIsOnWorkerThread();
289 return mFetchObserver;
292 void OnResponseAvailableInternal(
293 SafeRefPtr<InternalResponse> aResponse) override;
295 void OnResponseEnd(FetchDriverObserver::EndReason aReason,
296 JS::Handle<JS::Value> aReasonDetails) override;
298 bool NeedOnDataAvailable() override;
300 void OnDataAvailable() override;
302 void Shutdown(WorkerPrivate* aWorkerPrivate) {
303 MOZ_ASSERT(aWorkerPrivate);
304 aWorkerPrivate->AssertIsOnWorkerThread();
306 mIsShutdown = true;
307 mPromiseProxy->CleanUp();
309 mNeedOnDataAvailable = false;
310 mFetchObserver = nullptr;
312 if (mSignalProxy) {
313 mSignalProxy->Shutdown();
316 mWorkerRef = nullptr;
319 bool IsShutdown(WorkerPrivate* aWorkerPrivate) const {
320 MOZ_ASSERT(aWorkerPrivate);
321 aWorkerPrivate->AssertIsOnWorkerThread();
322 return mIsShutdown;
325 private:
326 WorkerFetchResolver(PromiseWorkerProxy* aProxy,
327 AbortSignalProxy* aSignalProxy, FetchObserver* aObserver)
328 : mPromiseProxy(aProxy),
329 mSignalProxy(aSignalProxy),
330 mFetchObserver(aObserver),
331 mIsShutdown(false),
332 mNeedOnDataAvailable(!!aObserver) {
333 MOZ_ASSERT(!NS_IsMainThread());
334 MOZ_ASSERT(mPromiseProxy);
337 ~WorkerFetchResolver() = default;
339 virtual void FlushConsoleReport() override;
342 void FetchDriverObserver::OnResponseAvailable(
343 SafeRefPtr<InternalResponse> aResponse) {
344 MOZ_ASSERT(!mGotResponseAvailable);
345 mGotResponseAvailable = true;
346 OnResponseAvailableInternal(std::move(aResponse));
349 class MainThreadFetchResolver final : public FetchDriverObserver {
350 RefPtr<Promise> mPromise;
351 RefPtr<Response> mResponse;
352 RefPtr<FetchObserver> mFetchObserver;
353 RefPtr<AbortSignalImpl> mSignalImpl;
354 const bool mMozErrors;
356 nsCOMPtr<nsILoadGroup> mLoadGroup;
358 NS_DECL_OWNINGTHREAD
359 public:
360 MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver,
361 AbortSignalImpl* aSignalImpl, bool aMozErrors)
362 : mPromise(aPromise),
363 mFetchObserver(aObserver),
364 mSignalImpl(aSignalImpl),
365 mMozErrors(aMozErrors) {}
367 void OnResponseAvailableInternal(
368 SafeRefPtr<InternalResponse> aResponse) override;
370 void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; }
372 void OnResponseEnd(FetchDriverObserver::EndReason aReason,
373 JS::Handle<JS::Value> aReasonDetails) override {
374 if (aReason == eAborted) {
375 if (!aReasonDetails.isUndefined()) {
376 mPromise->MaybeReject(aReasonDetails);
377 } else {
378 mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
382 mFetchObserver = nullptr;
384 FlushConsoleReport();
387 bool NeedOnDataAvailable() override;
389 void OnDataAvailable() override;
391 private:
392 ~MainThreadFetchResolver();
394 void FlushConsoleReport() override {
395 mReporter->FlushConsoleReports(mLoadGroup);
399 class MainThreadFetchRunnable : public Runnable {
400 RefPtr<WorkerFetchResolver> mResolver;
401 const ClientInfo mClientInfo;
402 const Maybe<ServiceWorkerDescriptor> mController;
403 nsCOMPtr<nsICSPEventListener> mCSPEventListener;
404 SafeRefPtr<InternalRequest> mRequest;
405 UniquePtr<SerializedStackHolder> mOriginStack;
407 public:
408 MainThreadFetchRunnable(WorkerFetchResolver* aResolver,
409 const ClientInfo& aClientInfo,
410 const Maybe<ServiceWorkerDescriptor>& aController,
411 nsICSPEventListener* aCSPEventListener,
412 SafeRefPtr<InternalRequest> aRequest,
413 UniquePtr<SerializedStackHolder>&& aOriginStack)
414 : Runnable("dom::MainThreadFetchRunnable"),
415 mResolver(aResolver),
416 mClientInfo(aClientInfo),
417 mController(aController),
418 mCSPEventListener(aCSPEventListener),
419 mRequest(std::move(aRequest)),
420 mOriginStack(std::move(aOriginStack)) {
421 MOZ_ASSERT(mResolver);
424 NS_IMETHOD
425 Run() override {
426 AssertIsOnMainThread();
427 RefPtr<FetchDriver> fetch;
428 RefPtr<PromiseWorkerProxy> proxy = mResolver->PromiseProxy();
431 // Acquire the proxy mutex while getting data from the WorkerPrivate...
432 MutexAutoLock lock(proxy->Lock());
433 if (proxy->CleanedUp()) {
434 NS_WARNING("Aborting Fetch because worker already shut down");
435 return NS_OK;
438 WorkerPrivate* workerPrivate = proxy->GetWorkerPrivate();
439 MOZ_ASSERT(workerPrivate);
440 nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
441 MOZ_ASSERT(principal);
442 nsCOMPtr<nsILoadGroup> loadGroup = workerPrivate->GetLoadGroup();
443 MOZ_ASSERT(loadGroup);
444 // We don't track if a worker is spawned from a tracking script for now,
445 // so pass false as the last argument to FetchDriver().
446 fetch = new FetchDriver(mRequest.clonePtr(), principal, loadGroup,
447 workerPrivate->MainThreadEventTarget(),
448 workerPrivate->CookieJarSettings(),
449 workerPrivate->GetPerformanceStorage(), false);
450 nsAutoCString spec;
451 if (proxy->GetWorkerPrivate()->GetBaseURI()) {
452 proxy->GetWorkerPrivate()->GetBaseURI()->GetAsciiSpec(spec);
454 fetch->SetWorkerScript(spec);
456 fetch->SetClientInfo(mClientInfo);
457 fetch->SetController(mController);
458 fetch->SetCSPEventListener(mCSPEventListener);
461 fetch->SetOriginStack(std::move(mOriginStack));
463 RefPtr<AbortSignalImpl> signalImpl =
464 mResolver->GetAbortSignalForMainThread();
466 // ...but release it before calling Fetch, because mResolver's callback can
467 // be called synchronously and they want the mutex, too.
468 return fetch->Fetch(signalImpl, mResolver);
472 already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
473 const RequestOrUSVString& aInput,
474 const RequestInit& aInit,
475 CallerType aCallerType,
476 ErrorResult& aRv) {
477 RefPtr<Promise> p = Promise::Create(aGlobal, aRv);
478 if (NS_WARN_IF(aRv.Failed())) {
479 return nullptr;
482 MOZ_ASSERT(aGlobal);
484 // Double check that we have chrome privileges if the Request's content
485 // policy type has been overridden.
486 MOZ_ASSERT_IF(aInput.IsRequest() &&
487 aInput.GetAsRequest().IsContentPolicyTypeOverridden(),
488 aCallerType == CallerType::System);
490 AutoJSAPI jsapi;
491 if (!jsapi.Init(aGlobal)) {
492 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
493 return nullptr;
496 JSContext* cx = jsapi.cx();
497 JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
498 GlobalObject global(cx, jsGlobal);
500 SafeRefPtr<Request> request =
501 Request::Constructor(global, aInput, aInit, aRv);
502 if (aRv.Failed()) {
503 return nullptr;
506 SafeRefPtr<InternalRequest> r = request->GetInternalRequest();
508 // Restore information of InterceptedHttpChannel if they are passed with the
509 // Request. Since Request::Constructor would not copy these members.
510 if (aInput.IsRequest()) {
511 RefPtr<Request> inputReq = &aInput.GetAsRequest();
512 SafeRefPtr<InternalRequest> inputInReq = inputReq->GetInternalRequest();
513 if (inputInReq->GetInterceptionTriggeringPrincipalInfo()) {
514 r->SetInterceptionContentPolicyType(
515 inputInReq->InterceptionContentPolicyType());
516 r->SetInterceptionTriggeringPrincipalInfo(
517 MakeUnique<mozilla::ipc::PrincipalInfo>(
518 *(inputInReq->GetInterceptionTriggeringPrincipalInfo().get())));
519 if (!inputInReq->InterceptionRedirectChain().IsEmpty()) {
520 r->SetInterceptionRedirectChain(
521 inputInReq->InterceptionRedirectChain());
523 r->SetInterceptionFromThirdParty(
524 inputInReq->InterceptionFromThirdParty());
528 RefPtr<AbortSignalImpl> signalImpl = request->GetSignalImpl();
530 if (signalImpl && signalImpl->Aborted()) {
531 // Already aborted signal rejects immediately.
532 JS::Rooted<JS::Value> reason(cx, signalImpl->RawReason());
533 if (reason.get().isUndefined()) {
534 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
535 return nullptr;
538 p->MaybeReject(reason);
539 return p.forget();
542 JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
543 if (realm && JS::GetDebuggerObservesWasm(realm)) {
544 r->SetSkipWasmCaching();
547 RefPtr<FetchObserver> observer;
548 if (aInit.mObserve.WasPassed()) {
549 observer = new FetchObserver(aGlobal, signalImpl);
550 aInit.mObserve.Value().HandleEvent(*observer);
553 if (NS_IsMainThread()) {
554 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
555 nsCOMPtr<Document> doc;
556 nsCOMPtr<nsILoadGroup> loadGroup;
557 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
558 nsIPrincipal* principal;
559 bool isTrackingFetch = false;
560 if (window) {
561 doc = window->GetExtantDoc();
562 if (!doc) {
563 aRv.Throw(NS_ERROR_FAILURE);
564 return nullptr;
566 principal = doc->NodePrincipal();
567 loadGroup = doc->GetDocumentLoadGroup();
568 cookieJarSettings = doc->CookieJarSettings();
570 isTrackingFetch = doc->IsScriptTracking(cx);
571 } else {
572 principal = aGlobal->PrincipalOrNull();
573 if (NS_WARN_IF(!principal)) {
574 aRv.Throw(NS_ERROR_FAILURE);
575 return nullptr;
578 cookieJarSettings = mozilla::net::CookieJarSettings::Create(principal);
581 if (!loadGroup) {
582 nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
583 if (NS_WARN_IF(NS_FAILED(rv))) {
584 aRv.Throw(rv);
585 return nullptr;
589 RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(
590 p, observer, signalImpl, request->MozErrors());
591 RefPtr<FetchDriver> fetch = new FetchDriver(
592 std::move(r), principal, loadGroup, aGlobal->SerialEventTarget(),
593 cookieJarSettings, nullptr, // PerformanceStorage
594 isTrackingFetch);
595 fetch->SetDocument(doc);
596 resolver->SetLoadGroup(loadGroup);
597 aRv = fetch->Fetch(signalImpl, resolver);
598 if (NS_WARN_IF(aRv.Failed())) {
599 return nullptr;
601 } else {
602 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
603 MOZ_ASSERT(worker);
605 if (worker->IsServiceWorker()) {
606 r->SetSkipServiceWorker();
609 // PFetch gives no benefit for the fetch in the parent process.
610 // Dispatch fetch to the parent process main thread directly for that case.
611 // For child process, dispatch fetch op to the parent.
612 if (StaticPrefs::dom_workers_pFetch_enabled() && !XRE_IsParentProcess()) {
613 RefPtr<FetchChild> actor =
614 FetchChild::Create(worker, p, signalImpl, observer);
615 if (!actor) {
616 NS_WARNING("Could not keep the worker alive.");
617 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
618 return nullptr;
621 Maybe<ClientInfo> clientInfo(worker->GlobalScope()->GetClientInfo());
622 if (clientInfo.isNothing()) {
623 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
624 return nullptr;
627 auto* backgroundChild =
628 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
629 Unused << NS_WARN_IF(!backgroundChild->SendPFetchConstructor(actor));
631 FetchOpArgs ipcArgs;
633 ipcArgs.request() = IPCInternalRequest();
634 r->ToIPCInternalRequest(&(ipcArgs.request()), backgroundChild);
636 ipcArgs.principalInfo() = worker->GetPrincipalInfo();
637 ipcArgs.clientInfo() = clientInfo.ref().ToIPC();
638 if (worker->GetBaseURI()) {
639 worker->GetBaseURI()->GetAsciiSpec(ipcArgs.workerScript());
641 if (worker->GlobalScope()->GetController().isSome()) {
642 ipcArgs.controller() =
643 Some(worker->GlobalScope()->GetController().ref().ToIPC());
645 if (worker->CookieJarSettings()) {
646 ipcArgs.cookieJarSettings() = Some(worker->CookieJarSettingsArgs());
648 if (worker->CSPEventListener()) {
649 ipcArgs.hasCSPEventListener() = true;
650 actor->SetCSPEventListener(worker->CSPEventListener());
651 } else {
652 ipcArgs.hasCSPEventListener() = false;
655 ipcArgs.associatedBrowsingContextID() =
656 worker->AssociatedBrowsingContextID();
658 if (worker->IsWatchedByDevTools()) {
659 UniquePtr<SerializedStackHolder> stack;
660 stack = GetCurrentStackForNetMonitor(cx);
661 actor->SetOriginStack(std::move(stack));
664 actor->DoFetchOp(ipcArgs);
666 return p.forget();
669 RefPtr<WorkerFetchResolver> resolver =
670 WorkerFetchResolver::Create(worker, p, signalImpl, observer);
671 if (!resolver) {
672 NS_WARNING("Could not keep the worker alive.");
673 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
674 return nullptr;
677 Maybe<ClientInfo> clientInfo(worker->GlobalScope()->GetClientInfo());
678 if (clientInfo.isNothing()) {
679 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
680 return nullptr;
683 UniquePtr<SerializedStackHolder> stack;
684 if (worker->IsWatchedByDevTools()) {
685 stack = GetCurrentStackForNetMonitor(cx);
688 RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(
689 resolver, clientInfo.ref(), worker->GlobalScope()->GetController(),
690 worker->CSPEventListener(), std::move(r), std::move(stack));
691 worker->DispatchToMainThread(run.forget());
694 return p.forget();
697 class ResolveFetchPromise : public Runnable {
698 public:
699 ResolveFetchPromise(Promise* aPromise, Response* aResponse)
700 : Runnable("ResolveFetchPromise"),
701 mPromise(aPromise),
702 mResponse(aResponse) {}
704 NS_IMETHOD Run() override {
705 mPromise->MaybeResolve(mResponse);
706 return NS_OK;
708 RefPtr<Promise> mPromise;
709 RefPtr<Response> mResponse;
712 void MainThreadFetchResolver::OnResponseAvailableInternal(
713 SafeRefPtr<InternalResponse> aResponse) {
714 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
715 AssertIsOnMainThread();
717 if (aResponse->Type() != ResponseType::Error) {
718 nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
719 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(go);
721 // Notify the document when a fetch completes successfully. This is
722 // used by the password manager as a hint to observe DOM mutations.
723 // Call this prior to setting state to Complete so we can set up the
724 // observer before mutations occurs.
725 Document* doc = inner ? inner->GetExtantDoc() : nullptr;
726 if (doc) {
727 doc->NotifyFetchOrXHRSuccess();
730 if (mFetchObserver) {
731 mFetchObserver->SetState(FetchState::Complete);
734 mResponse = new Response(go, std::move(aResponse), mSignalImpl);
735 // response headers received from the network should be immutable
736 // all response header settings must be done before this point
737 // see Bug 1574174
738 ErrorResult result;
739 mResponse->Headers_()->SetGuard(HeadersGuardEnum::Immutable, result);
740 MOZ_ASSERT(!result.Failed());
742 BrowsingContext* bc = inner ? inner->GetBrowsingContext() : nullptr;
743 bc = bc ? bc->Top() : nullptr;
744 if (bc && bc->IsLoading()) {
745 bc->AddDeprioritizedLoadRunner(
746 new ResolveFetchPromise(mPromise, mResponse));
747 } else {
748 mPromise->MaybeResolve(mResponse);
750 } else {
751 if (mFetchObserver) {
752 mFetchObserver->SetState(FetchState::Errored);
755 if (mMozErrors) {
756 mPromise->MaybeReject(aResponse->GetErrorCode());
757 return;
760 mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
764 bool MainThreadFetchResolver::NeedOnDataAvailable() {
765 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
766 return !!mFetchObserver;
769 void MainThreadFetchResolver::OnDataAvailable() {
770 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
771 AssertIsOnMainThread();
773 if (!mFetchObserver) {
774 return;
777 if (mFetchObserver->State() == FetchState::Requesting) {
778 mFetchObserver->SetState(FetchState::Responding);
782 MainThreadFetchResolver::~MainThreadFetchResolver() {
783 NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
786 class WorkerFetchResponseRunnable final : public MainThreadWorkerRunnable {
787 RefPtr<WorkerFetchResolver> mResolver;
788 // Passed from main thread to worker thread after being initialized.
789 SafeRefPtr<InternalResponse> mInternalResponse;
791 public:
792 WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate,
793 WorkerFetchResolver* aResolver,
794 SafeRefPtr<InternalResponse> aResponse)
795 : MainThreadWorkerRunnable(aWorkerPrivate, "WorkerFetchResponseRunnable"),
796 mResolver(aResolver),
797 mInternalResponse(std::move(aResponse)) {
798 MOZ_ASSERT(mResolver);
801 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
802 MOZ_ASSERT(aWorkerPrivate);
803 aWorkerPrivate->AssertIsOnWorkerThread();
804 if (mResolver->IsShutdown(aWorkerPrivate)) {
805 return true;
808 RefPtr<Promise> promise = mResolver->WorkerPromise(aWorkerPrivate);
809 // Once Worker had already started shutdown, workerPromise would be nullptr
810 if (!promise) {
811 return true;
813 RefPtr<FetchObserver> fetchObserver =
814 mResolver->GetFetchObserver(aWorkerPrivate);
816 if (mInternalResponse->Type() != ResponseType::Error) {
817 if (fetchObserver) {
818 fetchObserver->SetState(FetchState::Complete);
821 RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
822 RefPtr<Response> response =
823 new Response(global, mInternalResponse.clonePtr(),
824 mResolver->GetAbortSignalForTargetThread());
826 // response headers received from the network should be immutable,
827 // all response header settings must be done before this point
828 // see Bug 1574174
829 ErrorResult result;
830 response->Headers_()->SetGuard(HeadersGuardEnum::Immutable, result);
831 MOZ_ASSERT(!result.Failed());
833 promise->MaybeResolve(response);
834 } else {
835 if (fetchObserver) {
836 fetchObserver->SetState(FetchState::Errored);
839 promise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
841 return true;
845 class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable {
846 RefPtr<WorkerFetchResolver> mResolver;
848 public:
849 WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate,
850 WorkerFetchResolver* aResolver)
851 : MainThreadWorkerRunnable(aWorkerPrivate, "WorkerDataAvailableRunnable"),
852 mResolver(aResolver) {}
854 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
855 MOZ_ASSERT(aWorkerPrivate);
856 aWorkerPrivate->AssertIsOnWorkerThread();
858 RefPtr<FetchObserver> fetchObserver =
859 mResolver->GetFetchObserver(aWorkerPrivate);
861 if (fetchObserver && fetchObserver->State() == FetchState::Requesting) {
862 fetchObserver->SetState(FetchState::Responding);
865 return true;
869 class WorkerFetchResponseEndBase {
870 protected:
871 RefPtr<WorkerFetchResolver> mResolver;
873 public:
874 explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver)
875 : mResolver(aResolver) {
876 MOZ_ASSERT(aResolver);
879 void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) {
880 mResolver->Shutdown(aWorkerPrivate);
884 class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable,
885 public WorkerFetchResponseEndBase {
886 FetchDriverObserver::EndReason mReason;
888 public:
889 WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate,
890 WorkerFetchResolver* aResolver,
891 FetchDriverObserver::EndReason aReason)
892 : MainThreadWorkerRunnable(aWorkerPrivate,
893 "WorkerFetchResponseEndRunnable"),
894 WorkerFetchResponseEndBase(aResolver),
895 mReason(aReason) {}
897 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
898 if (mResolver->IsShutdown(aWorkerPrivate)) {
899 return true;
902 if (mReason == FetchDriverObserver::eAborted) {
903 mResolver->WorkerPromise(aWorkerPrivate)
904 ->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
907 WorkerRunInternal(aWorkerPrivate);
908 return true;
911 nsresult Cancel() override { return Run(); }
914 class WorkerFetchResponseEndControlRunnable final
915 : public MainThreadWorkerControlRunnable,
916 public WorkerFetchResponseEndBase {
917 public:
918 WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate,
919 WorkerFetchResolver* aResolver)
920 : MainThreadWorkerControlRunnable(aWorkerPrivate),
921 WorkerFetchResponseEndBase(aResolver) {}
923 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
924 WorkerRunInternal(aWorkerPrivate);
925 return true;
928 // Control runnable cancel already calls Run().
931 void WorkerFetchResolver::OnResponseAvailableInternal(
932 SafeRefPtr<InternalResponse> aResponse) {
933 AssertIsOnMainThread();
935 MutexAutoLock lock(mPromiseProxy->Lock());
936 if (mPromiseProxy->CleanedUp()) {
937 return;
940 RefPtr<WorkerFetchResponseRunnable> r = new WorkerFetchResponseRunnable(
941 mPromiseProxy->GetWorkerPrivate(), this, std::move(aResponse));
943 if (!r->Dispatch()) {
944 NS_WARNING("Could not dispatch fetch response");
948 bool WorkerFetchResolver::NeedOnDataAvailable() {
949 AssertIsOnMainThread();
950 return mNeedOnDataAvailable;
953 void WorkerFetchResolver::OnDataAvailable() {
954 AssertIsOnMainThread();
956 MutexAutoLock lock(mPromiseProxy->Lock());
957 if (mPromiseProxy->CleanedUp()) {
958 return;
961 RefPtr<WorkerDataAvailableRunnable> r =
962 new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this);
963 Unused << r->Dispatch();
966 void WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason,
967 JS::Handle<JS::Value> aReasonDetails) {
968 AssertIsOnMainThread();
969 MutexAutoLock lock(mPromiseProxy->Lock());
970 if (mPromiseProxy->CleanedUp()) {
971 return;
974 FlushConsoleReport();
976 Unused << aReasonDetails;
978 RefPtr<WorkerFetchResponseEndRunnable> r = new WorkerFetchResponseEndRunnable(
979 mPromiseProxy->GetWorkerPrivate(), this, aReason);
981 if (!r->Dispatch()) {
982 RefPtr<WorkerFetchResponseEndControlRunnable> cr =
983 new WorkerFetchResponseEndControlRunnable(
984 mPromiseProxy->GetWorkerPrivate(), this);
985 // This can fail if the worker thread is canceled or killed causing
986 // the PromiseWorkerProxy to give up its WorkerRef immediately,
987 // allowing the worker thread to become Dead.
988 if (!cr->Dispatch()) {
989 NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
994 void WorkerFetchResolver::FlushConsoleReport() {
995 AssertIsOnMainThread();
996 MOZ_ASSERT(mPromiseProxy);
998 if (!mReporter) {
999 return;
1002 WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
1003 if (!worker) {
1004 mReporter->FlushReportsToConsole(0);
1005 return;
1008 if (worker->IsServiceWorker()) {
1009 // Flush to service worker
1010 mReporter->FlushReportsToConsoleForServiceWorkerScope(
1011 worker->ServiceWorkerScope());
1012 return;
1015 if (worker->IsSharedWorker()) {
1016 // Flush to shared worker
1017 worker->GetRemoteWorkerController()->FlushReportsOnMainThread(mReporter);
1018 return;
1021 // Flush to dedicated worker
1022 mReporter->FlushConsoleReports(worker->GetLoadGroup());
1025 nsresult ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
1026 nsIInputStream** aStream,
1027 nsCString& aContentTypeWithCharset,
1028 uint64_t& aContentLength) {
1029 MOZ_ASSERT(aStream);
1030 nsAutoCString charset;
1031 aContentTypeWithCharset.SetIsVoid(true);
1033 if (aBodyInit.IsArrayBuffer()) {
1034 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
1035 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1036 charset);
1039 if (aBodyInit.IsArrayBufferView()) {
1040 BodyExtractor<const ArrayBufferView> body(
1041 &aBodyInit.GetAsArrayBufferView());
1042 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1043 charset);
1046 if (aBodyInit.IsBlob()) {
1047 Blob& blob = aBodyInit.GetAsBlob();
1048 BodyExtractor<const Blob> body(&blob);
1049 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1050 charset);
1053 if (aBodyInit.IsFormData()) {
1054 FormData& formData = aBodyInit.GetAsFormData();
1055 BodyExtractor<const FormData> body(&formData);
1056 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1057 charset);
1060 if (aBodyInit.IsUSVString()) {
1061 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
1062 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1063 charset);
1066 if (aBodyInit.IsURLSearchParams()) {
1067 URLSearchParams& usp = aBodyInit.GetAsURLSearchParams();
1068 BodyExtractor<const URLSearchParams> body(&usp);
1069 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1070 charset);
1073 MOZ_ASSERT_UNREACHABLE("Should never reach here");
1074 return NS_ERROR_FAILURE;
1077 nsresult ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
1078 nsIInputStream** aStream,
1079 nsCString& aContentTypeWithCharset,
1080 uint64_t& aContentLength) {
1081 MOZ_ASSERT(aStream);
1082 MOZ_ASSERT(!*aStream);
1084 nsAutoCString charset;
1085 aContentTypeWithCharset.SetIsVoid(true);
1087 if (aBodyInit.IsArrayBuffer()) {
1088 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
1089 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1090 charset);
1093 if (aBodyInit.IsArrayBufferView()) {
1094 BodyExtractor<const ArrayBufferView> body(
1095 &aBodyInit.GetAsArrayBufferView());
1096 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1097 charset);
1100 if (aBodyInit.IsBlob()) {
1101 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob());
1102 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1103 charset);
1106 if (aBodyInit.IsFormData()) {
1107 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData());
1108 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1109 charset);
1112 if (aBodyInit.IsUSVString()) {
1113 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
1114 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1115 charset);
1118 if (aBodyInit.IsURLSearchParams()) {
1119 BodyExtractor<const URLSearchParams> body(
1120 &aBodyInit.GetAsURLSearchParams());
1121 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1122 charset);
1125 MOZ_ASSERT_UNREACHABLE("Should never reach here");
1126 return NS_ERROR_FAILURE;
1129 nsresult ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
1130 nsIInputStream** aStream,
1131 nsCString& aContentTypeWithCharset,
1132 uint64_t& aContentLength) {
1133 MOZ_ASSERT(aStream);
1134 MOZ_ASSERT(!*aStream);
1136 // ReadableStreams should be handled by
1137 // BodyExtractorReadableStream::GetAsStream.
1138 MOZ_ASSERT(!aBodyInit.IsReadableStream());
1140 nsAutoCString charset;
1141 aContentTypeWithCharset.SetIsVoid(true);
1143 if (aBodyInit.IsArrayBuffer()) {
1144 BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
1145 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1146 charset);
1149 if (aBodyInit.IsArrayBufferView()) {
1150 BodyExtractor<const ArrayBufferView> body(
1151 &aBodyInit.GetAsArrayBufferView());
1152 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1153 charset);
1156 if (aBodyInit.IsBlob()) {
1157 BodyExtractor<const Blob> body(&aBodyInit.GetAsBlob());
1158 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1159 charset);
1162 if (aBodyInit.IsFormData()) {
1163 BodyExtractor<const FormData> body(&aBodyInit.GetAsFormData());
1164 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1165 charset);
1168 if (aBodyInit.IsUSVString()) {
1169 BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
1170 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1171 charset);
1174 if (aBodyInit.IsURLSearchParams()) {
1175 BodyExtractor<const URLSearchParams> body(
1176 &aBodyInit.GetAsURLSearchParams());
1177 return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
1178 charset);
1181 MOZ_ASSERT_UNREACHABLE("Should never reach here");
1182 return NS_ERROR_FAILURE;
1185 NS_IMPL_CYCLE_COLLECTION(FetchBodyBase, mReadableStreamBody)
1187 NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchBodyBase)
1188 NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchBodyBase)
1190 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchBodyBase)
1191 NS_INTERFACE_MAP_ENTRY(nsISupports)
1192 NS_INTERFACE_MAP_END
1194 template <class Derived>
1195 FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
1196 : mOwner(aOwner), mBodyUsed(false) {
1197 MOZ_ASSERT(aOwner);
1199 if (!NS_IsMainThread()) {
1200 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
1201 MOZ_ASSERT(wp);
1202 mMainThreadEventTarget = wp->MainThreadEventTarget();
1203 } else {
1204 mMainThreadEventTarget = GetMainThreadSerialEventTarget();
1207 MOZ_ASSERT(mMainThreadEventTarget);
1210 template FetchBody<Request>::FetchBody(nsIGlobalObject* aOwner);
1212 template FetchBody<Response>::FetchBody(nsIGlobalObject* aOwner);
1214 template <class Derived>
1215 FetchBody<Derived>::~FetchBody() {
1216 Unfollow();
1219 template FetchBody<Request>::~FetchBody();
1221 template FetchBody<Response>::~FetchBody();
1223 template <class Derived>
1224 bool FetchBody<Derived>::BodyUsed() const {
1225 if (mBodyUsed) {
1226 return true;
1229 // If this stream is disturbed, return true.
1230 if (mReadableStreamBody) {
1231 return mReadableStreamBody->Disturbed();
1234 return false;
1237 template bool FetchBody<Request>::BodyUsed() const;
1239 template bool FetchBody<Response>::BodyUsed() const;
1241 template <class Derived>
1242 void FetchBody<Derived>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv) {
1243 MOZ_ASSERT(aCx);
1244 MOZ_ASSERT(mOwner->SerialEventTarget()->IsOnCurrentThread());
1246 MOZ_DIAGNOSTIC_ASSERT(!BodyUsed(), "Consuming already used body?");
1247 if (BodyUsed()) {
1248 return;
1251 mBodyUsed = true;
1253 // If we already have a ReadableStreamBody and it has been created by DOM, we
1254 // have to lock it now because it can have been shared with other objects.
1255 if (mReadableStreamBody) {
1256 if (mFetchStreamReader) {
1257 // Having FetchStreamReader means there's no nsIInputStream underlying it
1258 MOZ_ASSERT(!mReadableStreamBody->MaybeGetInputStreamIfUnread());
1259 mFetchStreamReader->StartConsuming(aCx, mReadableStreamBody, aRv);
1260 return;
1262 // We should have nsIInputStream at this point as long as it's still
1263 // readable
1264 MOZ_ASSERT_IF(
1265 mReadableStreamBody->State() == ReadableStream::ReaderState::Readable,
1266 mReadableStreamBody->MaybeGetInputStreamIfUnread());
1267 LockStream(aCx, mReadableStreamBody, aRv);
1271 template void FetchBody<Request>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
1273 template void FetchBody<Response>::SetBodyUsed(JSContext* aCx,
1274 ErrorResult& aRv);
1276 template <class Derived>
1277 already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
1278 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv) {
1279 aRv.MightThrowJSException();
1281 RefPtr<AbortSignalImpl> signalImpl =
1282 DerivedClass()->GetSignalImplToConsumeBody();
1284 if (signalImpl && signalImpl->Aborted()) {
1285 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
1287 if (abortReason.get().isUndefined()) {
1288 aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
1289 return nullptr;
1292 nsCOMPtr<nsIGlobalObject> go = DerivedClass()->GetParentObject();
1294 RefPtr<Promise> promise = Promise::Create(go, aRv);
1295 promise->MaybeReject(abortReason);
1296 return promise.forget();
1299 if (BodyUsed()) {
1300 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
1301 return nullptr;
1304 nsAutoCString mimeType;
1305 nsAutoCString mixedCaseMimeType;
1306 DerivedClass()->GetMimeType(mimeType, mixedCaseMimeType);
1308 // Null bodies are a special-case in the fetch spec. The Body mix-in can only
1309 // be "disturbed" or "locked" if its associated "body" is non-null.
1310 // Additionally, the Body min-in's "consume body" algorithm explicitly creates
1311 // a fresh empty ReadableStream object in step 2. This means that `bodyUsed`
1312 // will never return true for a null body.
1314 // To this end, we create a fresh (empty) body every time a request is made
1315 // and consume its body here, without marking this FetchBody consumed via
1316 // SetBodyUsed.
1317 nsCOMPtr<nsIInputStream> bodyStream;
1318 DerivedClass()->GetBody(getter_AddRefs(bodyStream));
1319 if (!bodyStream) {
1320 RefPtr<EmptyBody> emptyBody =
1321 EmptyBody::Create(DerivedClass()->GetParentObject(),
1322 DerivedClass()->GetPrincipalInfo().get(), signalImpl,
1323 mimeType, mixedCaseMimeType, aRv);
1324 if (NS_WARN_IF(aRv.Failed())) {
1325 return nullptr;
1328 return emptyBody->ConsumeBody(aCx, aType, aRv);
1331 SetBodyUsed(aCx, aRv);
1332 if (NS_WARN_IF(aRv.Failed())) {
1333 return nullptr;
1336 nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
1338 MutableBlobStorage::MutableBlobStorageType blobStorageType =
1339 MutableBlobStorage::eOnlyInMemory;
1340 const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
1341 DerivedClass()->GetPrincipalInfo();
1342 // We support temporary file for blobs only if the principal is known and
1343 // it's system or content not in private Browsing.
1344 if (principalInfo &&
1345 (principalInfo->type() ==
1346 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
1347 (principalInfo->type() ==
1348 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
1349 principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId ==
1350 0))) {
1351 blobStorageType = MutableBlobStorage::eCouldBeInTemporaryFile;
1354 RefPtr<Promise> promise = BodyConsumer::Create(
1355 global, mMainThreadEventTarget, bodyStream, signalImpl, aType,
1356 BodyBlobURISpec(), BodyLocalPath(), mimeType, mixedCaseMimeType,
1357 blobStorageType, aRv);
1358 if (NS_WARN_IF(aRv.Failed())) {
1359 return nullptr;
1362 return promise.forget();
1365 template already_AddRefed<Promise> FetchBody<Request>::ConsumeBody(
1366 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1368 template already_AddRefed<Promise> FetchBody<Response>::ConsumeBody(
1369 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1371 template already_AddRefed<Promise> FetchBody<EmptyBody>::ConsumeBody(
1372 JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
1374 template <class Derived>
1375 void FetchBody<Derived>::GetMimeType(nsACString& aMimeType,
1376 nsACString& aMixedCaseMimeType) {
1377 // Extract mime type.
1378 ErrorResult result;
1379 nsCString contentTypeValues;
1380 MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
1381 DerivedClass()->GetInternalHeaders()->Get("Content-Type"_ns,
1382 contentTypeValues, result);
1383 MOZ_ALWAYS_TRUE(!result.Failed());
1385 // HTTP ABNF states Content-Type may have only one value.
1386 // This is from the "parse a header value" of the fetch spec.
1387 if (!contentTypeValues.IsVoid() && contentTypeValues.Find(",") == -1) {
1388 // Convert from a bytestring to a UTF8 CString.
1389 CopyLatin1toUTF8(contentTypeValues, aMimeType);
1390 aMixedCaseMimeType = aMimeType;
1391 ToLowerCase(aMimeType);
1395 template void FetchBody<Request>::GetMimeType(nsACString& aMimeType,
1396 nsACString& aMixedCaseMimeType);
1397 template void FetchBody<Response>::GetMimeType(nsACString& aMimeType,
1398 nsACString& aMixedCaseMimeType);
1400 template <class Derived>
1401 const nsACString& FetchBody<Derived>::BodyBlobURISpec() const {
1402 return DerivedClass()->BodyBlobURISpec();
1405 template const nsACString& FetchBody<Request>::BodyBlobURISpec() const;
1407 template const nsACString& FetchBody<Response>::BodyBlobURISpec() const;
1409 template const nsACString& FetchBody<EmptyBody>::BodyBlobURISpec() const;
1411 template <class Derived>
1412 const nsAString& FetchBody<Derived>::BodyLocalPath() const {
1413 return DerivedClass()->BodyLocalPath();
1416 template const nsAString& FetchBody<Request>::BodyLocalPath() const;
1418 template const nsAString& FetchBody<Response>::BodyLocalPath() const;
1420 template const nsAString& FetchBody<EmptyBody>::BodyLocalPath() const;
1422 template <class Derived>
1423 void FetchBody<Derived>::SetReadableStreamBody(JSContext* aCx,
1424 ReadableStream* aBody) {
1425 MOZ_ASSERT(!mReadableStreamBody);
1426 MOZ_ASSERT(aBody);
1427 mReadableStreamBody = aBody;
1429 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
1430 if (!signalImpl) {
1431 return;
1434 bool aborted = signalImpl->Aborted();
1435 if (aborted) {
1436 IgnoredErrorResult result;
1437 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
1438 AbortStream(aCx, mReadableStreamBody, result, abortReason);
1439 if (NS_WARN_IF(result.Failed())) {
1440 return;
1442 } else if (!IsFollowing()) {
1443 Follow(signalImpl);
1447 template void FetchBody<Request>::SetReadableStreamBody(JSContext* aCx,
1448 ReadableStream* aBody);
1450 template void FetchBody<Response>::SetReadableStreamBody(JSContext* aCx,
1451 ReadableStream* aBody);
1453 template <class Derived>
1454 already_AddRefed<ReadableStream> FetchBody<Derived>::GetBody(JSContext* aCx,
1455 ErrorResult& aRv) {
1456 if (mReadableStreamBody) {
1457 return do_AddRef(mReadableStreamBody);
1460 nsCOMPtr<nsIInputStream> inputStream;
1461 DerivedClass()->GetBody(getter_AddRefs(inputStream));
1463 if (!inputStream) {
1464 return nullptr;
1467 // The spec immediately creates ReadableStream on Response/Request constructor
1468 // via https://fetch.spec.whatwg.org/#concept-bodyinit-extract, but Gecko
1469 // creates nsIInputStream there instead and creates ReadableStream only when
1470 // .body is accessed. Thus we only follow step 4 of it here.
1472 // Step 4: Otherwise, set stream to a new ReadableStream object, and set up
1473 // stream with byte reading support.
1474 auto algorithms =
1475 MakeRefPtr<NonAsyncInputToReadableStreamAlgorithms>(*inputStream);
1476 RefPtr<ReadableStream> body = ReadableStream::CreateByteNative(
1477 aCx, DerivedClass()->GetParentObject(), *algorithms, Nothing(), aRv);
1478 if (aRv.Failed()) {
1479 return nullptr;
1481 mReadableStreamBody = body;
1483 // If the body has been already consumed, we lock the stream.
1484 if (BodyUsed()) {
1485 LockStream(aCx, body, aRv);
1486 if (NS_WARN_IF(aRv.Failed())) {
1487 return nullptr;
1491 RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
1492 if (signalImpl) {
1493 if (signalImpl->Aborted()) {
1494 JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
1495 AbortStream(aCx, body, aRv, abortReason);
1496 if (NS_WARN_IF(aRv.Failed())) {
1497 return nullptr;
1499 } else if (!IsFollowing()) {
1500 Follow(signalImpl);
1504 return body.forget();
1507 template already_AddRefed<ReadableStream> FetchBody<Request>::GetBody(
1508 JSContext* aCx, ErrorResult& aRv);
1510 template already_AddRefed<ReadableStream> FetchBody<Response>::GetBody(
1511 JSContext* aCx, ErrorResult& aRv);
1513 template <class Derived>
1514 void FetchBody<Derived>::LockStream(JSContext* aCx, ReadableStream* aStream,
1515 ErrorResult& aRv) {
1516 // This is native stream, creating a reader will not execute any JS code.
1517 RefPtr<ReadableStreamDefaultReader> reader = aStream->GetReader(aRv);
1518 if (aRv.Failed()) {
1519 return;
1523 template void FetchBody<Request>::LockStream(JSContext* aCx,
1524 ReadableStream* aStream,
1525 ErrorResult& aRv);
1527 template void FetchBody<Response>::LockStream(JSContext* aCx,
1528 ReadableStream* aStream,
1529 ErrorResult& aRv);
1531 template <class Derived>
1532 void FetchBody<Derived>::MaybeTeeReadableStreamBody(
1533 JSContext* aCx, ReadableStream** aBodyOut,
1534 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1535 ErrorResult& aRv) {
1536 MOZ_DIAGNOSTIC_ASSERT(aStreamReader);
1537 MOZ_DIAGNOSTIC_ASSERT(aInputStream);
1538 MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
1540 *aBodyOut = nullptr;
1541 *aStreamReader = nullptr;
1542 *aInputStream = nullptr;
1544 if (!mReadableStreamBody) {
1545 return;
1548 // If this is a ReadableStream with an native source, this has been
1549 // generated by a Fetch. In this case, Fetch will be able to recreate it
1550 // again when GetBody() is called.
1551 if (mReadableStreamBody->MaybeGetInputStreamIfUnread()) {
1552 *aBodyOut = nullptr;
1553 return;
1556 nsTArray<RefPtr<ReadableStream> > branches;
1557 MOZ_KnownLive(mReadableStreamBody)->Tee(aCx, branches, aRv);
1558 if (aRv.Failed()) {
1559 return;
1562 mReadableStreamBody = branches[0];
1563 branches[1].forget(aBodyOut);
1565 aRv = FetchStreamReader::Create(aCx, mOwner, aStreamReader, aInputStream);
1566 if (NS_WARN_IF(aRv.Failed())) {
1567 return;
1571 template void FetchBody<Request>::MaybeTeeReadableStreamBody(
1572 JSContext* aCx, ReadableStream** aBodyOut,
1573 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1574 ErrorResult& aRv);
1576 template void FetchBody<Response>::MaybeTeeReadableStreamBody(
1577 JSContext* aCx, ReadableStream** aBodyOut,
1578 FetchStreamReader** aStreamReader, nsIInputStream** aInputStream,
1579 ErrorResult& aRv);
1581 template <class Derived>
1582 void FetchBody<Derived>::RunAbortAlgorithm() {
1583 if (!mReadableStreamBody) {
1584 return;
1587 AutoJSAPI jsapi;
1588 if (!jsapi.Init(mOwner)) {
1589 return;
1592 JSContext* cx = jsapi.cx();
1594 RefPtr<ReadableStream> body(mReadableStreamBody);
1595 IgnoredErrorResult result;
1597 JS::Rooted<JS::Value> abortReason(cx);
1599 AbortSignalImpl* signalImpl = Signal();
1600 if (signalImpl) {
1601 abortReason.set(signalImpl->RawReason());
1604 AbortStream(cx, body, result, abortReason);
1607 template void FetchBody<Request>::RunAbortAlgorithm();
1609 template void FetchBody<Response>::RunAbortAlgorithm();
1611 NS_IMPL_ADDREF_INHERITED(EmptyBody, FetchBody<EmptyBody>)
1612 NS_IMPL_RELEASE_INHERITED(EmptyBody, FetchBody<EmptyBody>)
1614 NS_IMPL_CYCLE_COLLECTION_CLASS(EmptyBody)
1616 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EmptyBody, FetchBody<EmptyBody>)
1617 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
1618 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortSignalImpl)
1619 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader)
1620 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1622 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EmptyBody,
1623 FetchBody<EmptyBody>)
1624 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
1625 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbortSignalImpl)
1626 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchStreamReader)
1627 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1629 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(EmptyBody, FetchBody<EmptyBody>)
1630 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1632 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EmptyBody)
1633 NS_INTERFACE_MAP_END_INHERITING(FetchBody<EmptyBody>)
1635 EmptyBody::EmptyBody(nsIGlobalObject* aGlobal,
1636 mozilla::ipc::PrincipalInfo* aPrincipalInfo,
1637 AbortSignalImpl* aAbortSignalImpl,
1638 const nsACString& aMimeType,
1639 const nsACString& aMixedCaseMimeType,
1640 already_AddRefed<nsIInputStream> aBodyStream)
1641 : FetchBody<EmptyBody>(aGlobal),
1642 mAbortSignalImpl(aAbortSignalImpl),
1643 mMimeType(aMimeType),
1644 mMixedCaseMimeType(aMixedCaseMimeType),
1645 mBodyStream(std::move(aBodyStream)) {
1646 if (aPrincipalInfo) {
1647 mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*aPrincipalInfo);
1651 EmptyBody::~EmptyBody() = default;
1653 /* static */
1654 already_AddRefed<EmptyBody> EmptyBody::Create(
1655 nsIGlobalObject* aGlobal, mozilla::ipc::PrincipalInfo* aPrincipalInfo,
1656 AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType,
1657 const nsACString& aMixedCaseMimeType, ErrorResult& aRv) {
1658 nsCOMPtr<nsIInputStream> bodyStream;
1659 aRv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), ""_ns);
1660 if (NS_WARN_IF(aRv.Failed())) {
1661 return nullptr;
1664 RefPtr<EmptyBody> emptyBody =
1665 new EmptyBody(aGlobal, aPrincipalInfo, aAbortSignalImpl, aMimeType,
1666 aMixedCaseMimeType, bodyStream.forget());
1667 return emptyBody.forget();
1670 void EmptyBody::GetBody(nsIInputStream** aStream, int64_t* aBodyLength) {
1671 MOZ_ASSERT(aStream);
1673 if (aBodyLength) {
1674 *aBodyLength = 0;
1677 nsCOMPtr<nsIInputStream> bodyStream = mBodyStream;
1678 bodyStream.forget(aStream);
1681 } // namespace mozilla::dom