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/. */
9 #include "js/RootingAPI.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
{
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
) {
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
)) {
80 aReadableStream
->ErrorNative(aCx
, value
, aRv
);
85 class AbortSignalMainThread final
: public AbortSignalImpl
{
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);
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
)
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
;
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
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
);
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());
177 nsIEventTarget
* MainThreadEventTarget() { return mMainThreadEventTarget
; }
180 MOZ_ASSERT(!NS_IsMainThread());
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
);
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
{
209 RefPtr
<PromiseWorkerProxy
> mPromiseProxy
;
210 RefPtr
<AbortSignalProxy
> mSignalProxy
;
212 // Touched only on the worker thread.
213 RefPtr
<FetchObserver
> mFetchObserver
;
214 RefPtr
<WeakWorkerRef
> mWorkerRef
;
217 Atomic
<bool> mNeedOnDataAvailable
;
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
);
232 RefPtr
<AbortSignalProxy
> signalProxy
;
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
)) {
247 r
->mWorkerRef
= std::move(workerRef
);
252 AbortSignalImpl
* GetAbortSignalForMainThread() {
253 MOZ_ASSERT(NS_IsMainThread());
259 return mSignalProxy
->GetOrCreateSignalImplForMainThread();
262 AbortSignalImpl
* GetAbortSignalForTargetThread() {
263 mPromiseProxy
->GetWorkerPrivate()->AssertIsOnWorkerThread();
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();
307 mPromiseProxy
->CleanUp();
309 mNeedOnDataAvailable
= false;
310 mFetchObserver
= nullptr;
313 mSignalProxy
->Shutdown();
316 mWorkerRef
= nullptr;
319 bool IsShutdown(WorkerPrivate
* aWorkerPrivate
) const {
320 MOZ_ASSERT(aWorkerPrivate
);
321 aWorkerPrivate
->AssertIsOnWorkerThread();
326 WorkerFetchResolver(PromiseWorkerProxy
* aProxy
,
327 AbortSignalProxy
* aSignalProxy
, FetchObserver
* aObserver
)
328 : mPromiseProxy(aProxy
),
329 mSignalProxy(aSignalProxy
),
330 mFetchObserver(aObserver
),
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
;
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
);
378 mPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
382 mFetchObserver
= nullptr;
384 FlushConsoleReport();
387 bool NeedOnDataAvailable() override
;
389 void OnDataAvailable() override
;
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
;
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
);
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");
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);
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
,
477 RefPtr
<Promise
> p
= Promise::Create(aGlobal
, aRv
);
478 if (NS_WARN_IF(aRv
.Failed())) {
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
);
491 if (!jsapi
.Init(aGlobal
)) {
492 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
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
);
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
);
538 p
->MaybeReject(reason
);
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;
561 doc
= window
->GetExtantDoc();
563 aRv
.Throw(NS_ERROR_FAILURE
);
566 principal
= doc
->NodePrincipal();
567 loadGroup
= doc
->GetDocumentLoadGroup();
568 cookieJarSettings
= doc
->CookieJarSettings();
570 isTrackingFetch
= doc
->IsScriptTracking(cx
);
572 principal
= aGlobal
->PrincipalOrNull();
573 if (NS_WARN_IF(!principal
)) {
574 aRv
.Throw(NS_ERROR_FAILURE
);
578 cookieJarSettings
= mozilla::net::CookieJarSettings::Create(principal
);
582 nsresult rv
= NS_NewLoadGroup(getter_AddRefs(loadGroup
), principal
);
583 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
595 fetch
->SetDocument(doc
);
596 resolver
->SetLoadGroup(loadGroup
);
597 aRv
= fetch
->Fetch(signalImpl
, resolver
);
598 if (NS_WARN_IF(aRv
.Failed())) {
602 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
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
);
616 NS_WARNING("Could not keep the worker alive.");
617 aRv
.Throw(NS_ERROR_DOM_ABORT_ERR
);
621 Maybe
<ClientInfo
> clientInfo(worker
->GlobalScope()->GetClientInfo());
622 if (clientInfo
.isNothing()) {
623 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
627 auto* backgroundChild
=
628 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
629 Unused
<< NS_WARN_IF(!backgroundChild
->SendPFetchConstructor(actor
));
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());
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
);
669 RefPtr
<WorkerFetchResolver
> resolver
=
670 WorkerFetchResolver::Create(worker
, p
, signalImpl
, observer
);
672 NS_WARNING("Could not keep the worker alive.");
673 aRv
.Throw(NS_ERROR_DOM_ABORT_ERR
);
677 Maybe
<ClientInfo
> clientInfo(worker
->GlobalScope()->GetClientInfo());
678 if (clientInfo
.isNothing()) {
679 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
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());
697 class ResolveFetchPromise
: public Runnable
{
699 ResolveFetchPromise(Promise
* aPromise
, Response
* aResponse
)
700 : Runnable("ResolveFetchPromise"),
702 mResponse(aResponse
) {}
704 NS_IMETHOD
Run() override
{
705 mPromise
->MaybeResolve(mResponse
);
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;
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
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
));
748 mPromise
->MaybeResolve(mResponse
);
751 if (mFetchObserver
) {
752 mFetchObserver
->SetState(FetchState::Errored
);
756 mPromise
->MaybeReject(aResponse
->GetErrorCode());
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
) {
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
;
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
)) {
808 RefPtr
<Promise
> promise
= mResolver
->WorkerPromise(aWorkerPrivate
);
809 // Once Worker had already started shutdown, workerPromise would be nullptr
813 RefPtr
<FetchObserver
> fetchObserver
=
814 mResolver
->GetFetchObserver(aWorkerPrivate
);
816 if (mInternalResponse
->Type() != ResponseType::Error
) {
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
830 response
->Headers_()->SetGuard(HeadersGuardEnum::Immutable
, result
);
831 MOZ_ASSERT(!result
.Failed());
833 promise
->MaybeResolve(response
);
836 fetchObserver
->SetState(FetchState::Errored
);
839 promise
->MaybeRejectWithTypeError
<MSG_FETCH_FAILED
>();
845 class WorkerDataAvailableRunnable final
: public MainThreadWorkerRunnable
{
846 RefPtr
<WorkerFetchResolver
> mResolver
;
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
);
869 class WorkerFetchResponseEndBase
{
871 RefPtr
<WorkerFetchResolver
> mResolver
;
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
;
889 WorkerFetchResponseEndRunnable(WorkerPrivate
* aWorkerPrivate
,
890 WorkerFetchResolver
* aResolver
,
891 FetchDriverObserver::EndReason aReason
)
892 : MainThreadWorkerRunnable(aWorkerPrivate
,
893 "WorkerFetchResponseEndRunnable"),
894 WorkerFetchResponseEndBase(aResolver
),
897 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
898 if (mResolver
->IsShutdown(aWorkerPrivate
)) {
902 if (mReason
== FetchDriverObserver::eAborted
) {
903 mResolver
->WorkerPromise(aWorkerPrivate
)
904 ->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
907 WorkerRunInternal(aWorkerPrivate
);
911 nsresult
Cancel() override
{ return Run(); }
914 class WorkerFetchResponseEndControlRunnable final
915 : public MainThreadWorkerControlRunnable
,
916 public WorkerFetchResponseEndBase
{
918 WorkerFetchResponseEndControlRunnable(WorkerPrivate
* aWorkerPrivate
,
919 WorkerFetchResolver
* aResolver
)
920 : MainThreadWorkerControlRunnable(aWorkerPrivate
),
921 WorkerFetchResponseEndBase(aResolver
) {}
923 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
924 WorkerRunInternal(aWorkerPrivate
);
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()) {
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()) {
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()) {
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
);
1002 WorkerPrivate
* worker
= mPromiseProxy
->GetWorkerPrivate();
1004 mReporter
->FlushReportsToConsole(0);
1008 if (worker
->IsServiceWorker()) {
1009 // Flush to service worker
1010 mReporter
->FlushReportsToConsoleForServiceWorkerScope(
1011 worker
->ServiceWorkerScope());
1015 if (worker
->IsSharedWorker()) {
1016 // Flush to shared worker
1017 worker
->GetRemoteWorkerController()->FlushReportsOnMainThread(mReporter
);
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
,
1039 if (aBodyInit
.IsArrayBufferView()) {
1040 BodyExtractor
<const ArrayBufferView
> body(
1041 &aBodyInit
.GetAsArrayBufferView());
1042 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1046 if (aBodyInit
.IsBlob()) {
1047 Blob
& blob
= aBodyInit
.GetAsBlob();
1048 BodyExtractor
<const Blob
> body(&blob
);
1049 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1053 if (aBodyInit
.IsFormData()) {
1054 FormData
& formData
= aBodyInit
.GetAsFormData();
1055 BodyExtractor
<const FormData
> body(&formData
);
1056 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1060 if (aBodyInit
.IsUSVString()) {
1061 BodyExtractor
<const nsAString
> body(&aBodyInit
.GetAsUSVString());
1062 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1066 if (aBodyInit
.IsURLSearchParams()) {
1067 URLSearchParams
& usp
= aBodyInit
.GetAsURLSearchParams();
1068 BodyExtractor
<const URLSearchParams
> body(&usp
);
1069 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
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
,
1093 if (aBodyInit
.IsArrayBufferView()) {
1094 BodyExtractor
<const ArrayBufferView
> body(
1095 &aBodyInit
.GetAsArrayBufferView());
1096 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1100 if (aBodyInit
.IsBlob()) {
1101 BodyExtractor
<const Blob
> body(&aBodyInit
.GetAsBlob());
1102 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1106 if (aBodyInit
.IsFormData()) {
1107 BodyExtractor
<const FormData
> body(&aBodyInit
.GetAsFormData());
1108 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1112 if (aBodyInit
.IsUSVString()) {
1113 BodyExtractor
<const nsAString
> body(&aBodyInit
.GetAsUSVString());
1114 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1118 if (aBodyInit
.IsURLSearchParams()) {
1119 BodyExtractor
<const URLSearchParams
> body(
1120 &aBodyInit
.GetAsURLSearchParams());
1121 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
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
,
1149 if (aBodyInit
.IsArrayBufferView()) {
1150 BodyExtractor
<const ArrayBufferView
> body(
1151 &aBodyInit
.GetAsArrayBufferView());
1152 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1156 if (aBodyInit
.IsBlob()) {
1157 BodyExtractor
<const Blob
> body(&aBodyInit
.GetAsBlob());
1158 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1162 if (aBodyInit
.IsFormData()) {
1163 BodyExtractor
<const FormData
> body(&aBodyInit
.GetAsFormData());
1164 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1168 if (aBodyInit
.IsUSVString()) {
1169 BodyExtractor
<const nsAString
> body(&aBodyInit
.GetAsUSVString());
1170 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
1174 if (aBodyInit
.IsURLSearchParams()) {
1175 BodyExtractor
<const URLSearchParams
> body(
1176 &aBodyInit
.GetAsURLSearchParams());
1177 return body
.GetAsStream(aStream
, &aContentLength
, aContentTypeWithCharset
,
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) {
1199 if (!NS_IsMainThread()) {
1200 WorkerPrivate
* wp
= GetCurrentThreadWorkerPrivate();
1202 mMainThreadEventTarget
= wp
->MainThreadEventTarget();
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() {
1219 template FetchBody
<Request
>::~FetchBody();
1221 template FetchBody
<Response
>::~FetchBody();
1223 template <class Derived
>
1224 bool FetchBody
<Derived
>::BodyUsed() const {
1229 // If this stream is disturbed, return true.
1230 if (mReadableStreamBody
) {
1231 return mReadableStreamBody
->Disturbed();
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
) {
1244 MOZ_ASSERT(mOwner
->SerialEventTarget()->IsOnCurrentThread());
1246 MOZ_DIAGNOSTIC_ASSERT(!BodyUsed(), "Consuming already used body?");
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
);
1262 // We should have nsIInputStream at this point as long as it's still
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
,
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
);
1292 nsCOMPtr
<nsIGlobalObject
> go
= DerivedClass()->GetParentObject();
1294 RefPtr
<Promise
> promise
= Promise::Create(go
, aRv
);
1295 promise
->MaybeReject(abortReason
);
1296 return promise
.forget();
1300 aRv
.ThrowTypeError
<MSG_FETCH_BODY_CONSUMED_ERROR
>();
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
1317 nsCOMPtr
<nsIInputStream
> bodyStream
;
1318 DerivedClass()->GetBody(getter_AddRefs(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())) {
1328 return emptyBody
->ConsumeBody(aCx
, aType
, aRv
);
1331 SetBodyUsed(aCx
, aRv
);
1332 if (NS_WARN_IF(aRv
.Failed())) {
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
==
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())) {
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.
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
);
1427 mReadableStreamBody
= aBody
;
1429 RefPtr
<AbortSignalImpl
> signalImpl
= DerivedClass()->GetSignalImpl();
1434 bool aborted
= signalImpl
->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())) {
1442 } else if (!IsFollowing()) {
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
,
1456 if (mReadableStreamBody
) {
1457 return do_AddRef(mReadableStreamBody
);
1460 nsCOMPtr
<nsIInputStream
> inputStream
;
1461 DerivedClass()->GetBody(getter_AddRefs(inputStream
));
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.
1475 MakeRefPtr
<NonAsyncInputToReadableStreamAlgorithms
>(*inputStream
);
1476 RefPtr
<ReadableStream
> body
= ReadableStream::CreateByteNative(
1477 aCx
, DerivedClass()->GetParentObject(), *algorithms
, Nothing(), aRv
);
1481 mReadableStreamBody
= body
;
1483 // If the body has been already consumed, we lock the stream.
1485 LockStream(aCx
, body
, aRv
);
1486 if (NS_WARN_IF(aRv
.Failed())) {
1491 RefPtr
<AbortSignalImpl
> signalImpl
= DerivedClass()->GetSignalImpl();
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())) {
1499 } else if (!IsFollowing()) {
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
,
1516 // This is native stream, creating a reader will not execute any JS code.
1517 RefPtr
<ReadableStreamDefaultReader
> reader
= aStream
->GetReader(aRv
);
1523 template void FetchBody
<Request
>::LockStream(JSContext
* aCx
,
1524 ReadableStream
* aStream
,
1527 template void FetchBody
<Response
>::LockStream(JSContext
* aCx
,
1528 ReadableStream
* aStream
,
1531 template <class Derived
>
1532 void FetchBody
<Derived
>::MaybeTeeReadableStreamBody(
1533 JSContext
* aCx
, ReadableStream
** aBodyOut
,
1534 FetchStreamReader
** aStreamReader
, nsIInputStream
** aInputStream
,
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
) {
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;
1556 nsTArray
<RefPtr
<ReadableStream
> > branches
;
1557 MOZ_KnownLive(mReadableStreamBody
)->Tee(aCx
, branches
, aRv
);
1562 mReadableStreamBody
= branches
[0];
1563 branches
[1].forget(aBodyOut
);
1565 aRv
= FetchStreamReader::Create(aCx
, mOwner
, aStreamReader
, aInputStream
);
1566 if (NS_WARN_IF(aRv
.Failed())) {
1571 template void FetchBody
<Request
>::MaybeTeeReadableStreamBody(
1572 JSContext
* aCx
, ReadableStream
** aBodyOut
,
1573 FetchStreamReader
** aStreamReader
, nsIInputStream
** aInputStream
,
1576 template void FetchBody
<Response
>::MaybeTeeReadableStreamBody(
1577 JSContext
* aCx
, ReadableStream
** aBodyOut
,
1578 FetchStreamReader
** aStreamReader
, nsIInputStream
** aInputStream
,
1581 template <class Derived
>
1582 void FetchBody
<Derived
>::RunAbortAlgorithm() {
1583 if (!mReadableStreamBody
) {
1588 if (!jsapi
.Init(mOwner
)) {
1592 JSContext
* cx
= jsapi
.cx();
1594 RefPtr
<ReadableStream
> body(mReadableStreamBody
);
1595 IgnoredErrorResult result
;
1597 JS::Rooted
<JS::Value
> abortReason(cx
);
1599 AbortSignalImpl
* signalImpl
= Signal();
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;
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())) {
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
);
1677 nsCOMPtr
<nsIInputStream
> bodyStream
= mBodyStream
;
1678 bodyStream
.forget(aStream
);
1681 } // namespace mozilla::dom