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/. */
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/TaskQueue.h"
10 #include "mozilla/dom/FetchDriver.h"
12 #include "mozilla/dom/FetchPriority.h"
13 #include "mozilla/dom/ReferrerInfo.h"
14 #include "nsIAsyncVerifyRedirectCallback.h"
15 #include "mozilla/dom/Document.h"
16 #include "nsIBaseChannel.h"
17 #include "nsICookieJarSettings.h"
19 #include "nsIInputStream.h"
20 #include "nsIInterceptionInfo.h"
21 #include "nsIOutputStream.h"
22 #include "nsIFileChannel.h"
23 #include "nsIHttpChannel.h"
24 #include "nsIHttpChannelInternal.h"
25 #include "nsISupportsPriority.h"
26 #include "nsIThreadRetargetableRequest.h"
27 #include "nsIUploadChannel2.h"
28 #include "nsIInterfaceRequestorUtils.h"
30 #include "nsIRedirectHistoryEntry.h"
32 #include "nsContentPolicyUtils.h"
33 #include "nsDataChannel.h"
34 #include "nsDataHandler.h"
35 #include "nsNetUtil.h"
36 #include "nsPrintfCString.h"
37 #include "nsProxyRelease.h"
38 #include "nsStreamUtils.h"
39 #include "nsStringStream.h"
40 #include "nsHttpChannel.h"
42 #include "mozilla/dom/BlobURLProtocolHandler.h"
43 #include "mozilla/dom/File.h"
44 #include "mozilla/dom/PerformanceStorage.h"
45 #include "mozilla/dom/PerformanceTiming.h"
46 #include "mozilla/dom/ServiceWorkerInterceptController.h"
47 #include "mozilla/dom/UserActivation.h"
48 #include "mozilla/dom/WorkerCommon.h"
49 #include "mozilla/PreloaderBase.h"
50 #include "mozilla/net/ContentRange.h"
51 #include "mozilla/net/InterceptionInfo.h"
52 #include "mozilla/net/NeckoChannelParams.h"
53 #include "mozilla/ipc/PBackgroundSharedTypes.h"
54 #include "mozilla/StaticPrefs_browser.h"
55 #include "mozilla/StaticPrefs_network.h"
56 #include "mozilla/StaticPrefs_privacy.h"
57 #include "mozilla/StaticPrefs_javascript.h"
58 #include "mozilla/Unused.h"
61 #include "FetchUtil.h"
62 #include "InternalRequest.h"
63 #include "InternalResponse.h"
65 namespace mozilla::dom
{
69 void GetBlobURISpecFromChannel(nsIRequest
* aRequest
, nsCString
& aBlobURISpec
) {
72 aBlobURISpec
.SetIsVoid(true);
74 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
80 nsresult rv
= NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
));
85 if (!dom::IsBlobURI(uri
)) {
89 uri
->GetSpec(aBlobURISpec
);
92 bool ShouldCheckSRI(const InternalRequest
& aRequest
,
93 const InternalResponse
& aResponse
) {
94 return !aRequest
.GetIntegrity().IsEmpty() &&
95 aResponse
.Type() != ResponseType::Error
;
98 } // anonymous namespace
100 //-----------------------------------------------------------------------------
101 // AlternativeDataStreamListener
102 //-----------------------------------------------------------------------------
103 class AlternativeDataStreamListener final
104 : public nsIThreadRetargetableStreamListener
{
106 NS_DECL_THREADSAFE_ISUPPORTS
107 NS_DECL_NSIREQUESTOBSERVER
108 NS_DECL_NSISTREAMLISTENER
109 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
111 // The status of AlternativeDataStreamListener
112 // LOADING: is the initial status, loading the alternative data
113 // COMPLETED: Alternative data loading is completed
114 // CANCELED: Alternative data loading is canceled, this would make
115 // AlternativeDataStreamListener ignore all channel callbacks
116 // FALLBACK: fallback the channel callbacks to FetchDriver
117 // Depends on different situaions, the status transition could be followings
118 // 1. LOADING->COMPLETED
119 // This is the normal status transition for alternative data loading
121 // 2. LOADING->CANCELED
122 // LOADING->COMPLETED->CANCELED
123 // Alternative data loading could be canceled when cacheId from alternative
124 // data channel does not match with from main data channel(The cacheID
125 // checking is in FetchDriver::OnStartRequest).
126 // Notice the alternative data loading could finish before the cacheID
127 // checking, so the statust transition could be
128 // LOADING->COMPLETED->CANCELED
130 // 3. LOADING->FALLBACK
131 // For the case that alternative data loading could not be initialized,
132 // i.e. alternative data does not exist or no preferred alternative data
133 // type is requested. Once the status becomes FALLBACK,
134 // AlternativeDataStreamListener transits the channel callback request to
135 // FetchDriver, and the status should not go back to LOADING, COMPLETED, or
137 enum eStatus
{ LOADING
= 0, COMPLETED
, CANCELED
, FALLBACK
};
139 AlternativeDataStreamListener(FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
140 const nsACString
& aAlternativeDataType
);
143 uint64_t GetAlternativeDataCacheEntryId();
144 const nsACString
& GetAlternativeDataType() const;
145 already_AddRefed
<nsICacheInfoChannel
> GetCacheInfoChannel();
146 already_AddRefed
<nsIInputStream
> GetAlternativeInputStream();
149 ~AlternativeDataStreamListener() = default;
151 // This creates a strong reference cycle with FetchDriver and its
152 // mAltDataListener. We need to clear at least one reference of them once the
153 // data loading finishes.
154 RefPtr
<FetchDriver
> mFetchDriver
;
155 nsCString mAlternativeDataType
;
156 nsCOMPtr
<nsIInputStream
> mPipeAlternativeInputStream
;
157 nsCOMPtr
<nsIOutputStream
> mPipeAlternativeOutputStream
;
158 uint64_t mAlternativeDataCacheEntryId
;
159 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfoChannel
;
160 nsCOMPtr
<nsIChannel
> mChannel
;
161 Atomic
<eStatus
> mStatus
;
164 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener
, nsIStreamListener
,
165 nsIThreadRetargetableStreamListener
)
167 AlternativeDataStreamListener::AlternativeDataStreamListener(
168 FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
169 const nsACString
& aAlternativeDataType
)
170 : mFetchDriver(aFetchDriver
),
171 mAlternativeDataType(aAlternativeDataType
),
172 mAlternativeDataCacheEntryId(0),
174 mStatus(AlternativeDataStreamListener::LOADING
) {
175 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver
);
176 MOZ_DIAGNOSTIC_ASSERT(mChannel
);
179 AlternativeDataStreamListener::eStatus
AlternativeDataStreamListener::Status() {
183 void AlternativeDataStreamListener::Cancel() {
184 mAlternativeDataCacheEntryId
= 0;
185 mCacheInfoChannel
= nullptr;
186 mPipeAlternativeOutputStream
= nullptr;
187 mPipeAlternativeInputStream
= nullptr;
188 if (mChannel
&& mStatus
!= AlternativeDataStreamListener::FALLBACK
) {
189 // if mStatus is fallback, we need to keep channel to forward request back
191 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
192 "AlternativeDataStreamListener::Cancel"_ns
);
195 mStatus
= AlternativeDataStreamListener::CANCELED
;
198 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
199 return mAlternativeDataCacheEntryId
;
202 const nsACString
& AlternativeDataStreamListener::GetAlternativeDataType()
204 return mAlternativeDataType
;
207 already_AddRefed
<nsIInputStream
>
208 AlternativeDataStreamListener::GetAlternativeInputStream() {
209 nsCOMPtr
<nsIInputStream
> inputStream
= mPipeAlternativeInputStream
;
210 return inputStream
.forget();
213 already_AddRefed
<nsICacheInfoChannel
>
214 AlternativeDataStreamListener::GetCacheInfoChannel() {
215 nsCOMPtr
<nsICacheInfoChannel
> channel
= mCacheInfoChannel
;
216 return channel
.forget();
220 AlternativeDataStreamListener::OnStartRequest(nsIRequest
* aRequest
) {
221 AssertIsOnMainThread();
222 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty());
223 // Checking the alternative data type is the same between we asked and the
224 // saved in the channel.
225 nsAutoCString alternativeDataType
;
226 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
227 mStatus
= AlternativeDataStreamListener::LOADING
;
228 if (cic
&& NS_SUCCEEDED(cic
->GetAlternativeDataType(alternativeDataType
)) &&
229 mAlternativeDataType
.Equals(alternativeDataType
) &&
230 NS_SUCCEEDED(cic
->GetCacheEntryId(&mAlternativeDataCacheEntryId
))) {
231 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream
);
232 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream
);
233 NS_NewPipe(getter_AddRefs(mPipeAlternativeInputStream
),
234 getter_AddRefs(mPipeAlternativeOutputStream
),
235 0 /* default segment size */, UINT32_MAX
/* infinite pipe */,
236 true /* non-blocking input, otherwise you deadlock */,
237 false /* blocking output, since the pipe is 'in'finite */);
239 MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel
);
240 mCacheInfoChannel
= cic
;
242 // call FetchDriver::HttpFetch to load main body
243 MOZ_ASSERT(mFetchDriver
);
244 return mFetchDriver
->HttpFetch();
246 // Needn't load alternative data, since alternative data does not exist.
247 // Set status to FALLBACK to reuse the opened channel to load main body,
248 // then call FetchDriver::OnStartRequest to continue the work. Unfortunately
249 // can't change the stream listener to mFetchDriver, need to keep
250 // AlternativeDataStreamListener alive to redirect OnDataAvailable and
251 // OnStopRequest to mFetchDriver.
252 MOZ_ASSERT(alternativeDataType
.IsEmpty());
253 mStatus
= AlternativeDataStreamListener::FALLBACK
;
254 mAlternativeDataCacheEntryId
= 0;
255 MOZ_ASSERT(mFetchDriver
);
256 return mFetchDriver
->OnStartRequest(aRequest
);
260 AlternativeDataStreamListener::OnDataAvailable(nsIRequest
* aRequest
,
261 nsIInputStream
* aInputStream
,
264 if (mStatus
== AlternativeDataStreamListener::LOADING
) {
265 MOZ_ASSERT(mPipeAlternativeOutputStream
);
267 return aInputStream
->ReadSegments(
268 NS_CopySegmentToStream
, mPipeAlternativeOutputStream
, aCount
, &read
);
270 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
271 MOZ_ASSERT(mFetchDriver
);
272 return mFetchDriver
->OnDataAvailable(aRequest
, aInputStream
, aOffset
,
279 AlternativeDataStreamListener::OnStopRequest(nsIRequest
* aRequest
,
280 nsresult aStatusCode
) {
281 AssertIsOnMainThread();
283 // Alternative data loading is going to finish, breaking the reference cycle
284 // here by taking the ownership to a loacl variable.
285 RefPtr
<FetchDriver
> fetchDriver
= std::move(mFetchDriver
);
287 if (mStatus
== AlternativeDataStreamListener::CANCELED
) {
292 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
293 MOZ_ASSERT(fetchDriver
);
294 return fetchDriver
->OnStopRequest(aRequest
, aStatusCode
);
297 MOZ_DIAGNOSTIC_ASSERT(mStatus
== AlternativeDataStreamListener::LOADING
);
299 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty() && mPipeAlternativeOutputStream
&&
300 mPipeAlternativeInputStream
);
302 mPipeAlternativeOutputStream
->Close();
303 mPipeAlternativeOutputStream
= nullptr;
305 // Cleanup the states for alternative data if needed.
306 if (NS_FAILED(aStatusCode
)) {
307 mAlternativeDataCacheEntryId
= 0;
308 mCacheInfoChannel
= nullptr;
309 mPipeAlternativeInputStream
= nullptr;
311 mStatus
= AlternativeDataStreamListener::COMPLETED
;
312 // alternative data loading finish, call FetchDriver::FinishOnStopRequest to
313 // continue the final step for the case FetchDriver::OnStopRequest is called
314 // earlier than AlternativeDataStreamListener::OnStopRequest
315 MOZ_ASSERT(fetchDriver
);
316 fetchDriver
->FinishOnStopRequest(this);
321 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK
; }
324 AlternativeDataStreamListener::OnDataFinished(nsresult aStatus
) {
328 //-----------------------------------------------------------------------------
330 //-----------------------------------------------------------------------------
332 NS_IMPL_ISUPPORTS(FetchDriver
, nsIStreamListener
, nsIChannelEventSink
,
333 nsIInterfaceRequestor
, nsIThreadRetargetableStreamListener
,
334 nsINetworkInterceptController
)
336 FetchDriver::FetchDriver(SafeRefPtr
<InternalRequest
> aRequest
,
337 nsIPrincipal
* aPrincipal
, nsILoadGroup
* aLoadGroup
,
338 nsIEventTarget
* aMainThreadEventTarget
,
339 nsICookieJarSettings
* aCookieJarSettings
,
340 PerformanceStorage
* aPerformanceStorage
,
341 bool aIsTrackingFetch
)
342 : mPrincipal(aPrincipal
),
343 mLoadGroup(aLoadGroup
),
344 mRequest(std::move(aRequest
)),
345 mMainThreadEventTarget(aMainThreadEventTarget
),
346 mCookieJarSettings(aCookieJarSettings
),
347 mPerformanceStorage(aPerformanceStorage
),
348 mNeedToObserveOnDataAvailable(false),
349 mIsTrackingFetch(aIsTrackingFetch
),
350 mOnStopRequestCalled(false)
353 mResponseAvailableCalled(false),
357 AssertIsOnMainThread();
359 MOZ_ASSERT(mRequest
);
360 MOZ_ASSERT(aPrincipal
);
361 MOZ_ASSERT(aMainThreadEventTarget
);
364 FetchDriver::~FetchDriver() {
365 AssertIsOnMainThread();
367 // We assert this since even on failures, we should call
368 // FailWithNetworkError().
369 MOZ_ASSERT(mResponseAvailableCalled
);
375 already_AddRefed
<PreloaderBase
> FetchDriver::FindPreload(nsIURI
* aURI
) {
376 // Decide if we allow reuse of an existing <link rel=preload as=fetch>
377 // response for this request. First examine this fetch requets itself if it
378 // is 'pure' enough to use the response and then try to find a preload.
381 // Preloads are mapped on the document, no document, no preload.
385 switch (mRequest
->Mode()) {
386 case RequestMode::No_cors
:
387 cors
= CORSMode::CORS_NONE
;
389 case RequestMode::Cors
:
390 cors
= mRequest
->GetCredentialsMode() == RequestCredentials::Include
391 ? CORSMode::CORS_USE_CREDENTIALS
392 : CORSMode::CORS_ANONYMOUS
;
395 // Can't be satisfied by a preload because preload cannot define any of
399 if (!mRequest
->Headers()->HasOnlySimpleHeaders()) {
400 // Preload can't set any headers.
403 if (!mRequest
->GetIntegrity().IsEmpty()) {
404 // There is currently no support for SRI checking in the fetch preloader.
407 if (mRequest
->GetCacheMode() != RequestCache::Default
) {
408 // Preload can only go with the default caching mode.
411 if (mRequest
->SkipServiceWorker()) {
412 // Preload can't be forbidden interception.
415 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
416 // Preload always follows redirects.
419 nsAutoCString method
;
420 mRequest
->GetMethod(method
);
421 if (!method
.EqualsLiteral("GET")) {
422 // Preload can only do GET, this also eliminates the case we do upload, so
423 // no need to check if the request has any body to send out.
427 // OK, this request can be satisfied by a preloaded response, try to find one.
429 auto preloadKey
= PreloadHashKey::CreateAsFetch(aURI
, cors
);
430 return mDocument
->Preloads().LookupPreload(preloadKey
);
433 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel
* aChannel
) {
434 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
439 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
444 nsAutoString computedReferrerSpec
;
445 mRequest
->SetReferrerPolicy(referrerInfo
->ReferrerPolicy());
446 Unused
<< referrerInfo
->GetComputedReferrerSpec(computedReferrerSpec
);
447 mRequest
->SetReferrer(computedReferrerSpec
);
450 nsresult
FetchDriver::Fetch(AbortSignalImpl
* aSignalImpl
,
451 FetchDriverObserver
* aObserver
) {
452 AssertIsOnMainThread();
454 MOZ_ASSERT(!mFetchCalled
);
458 mObserver
= aObserver
;
460 // FIXME(nsm): Deal with HSTS.
462 MOZ_RELEASE_ASSERT(!mRequest
->IsSynchronous(),
463 "Synchronous fetch not supported");
465 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
466 new mozilla::ipc::PrincipalInfo());
467 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
468 if (NS_WARN_IF(NS_FAILED(rv
))) {
472 mRequest
->SetPrincipalInfo(std::move(principalInfo
));
474 // If the signal is aborted, it's time to inform the observer and terminate
477 if (aSignalImpl
->Aborted()) {
478 FetchDriverAbortActions(aSignalImpl
);
485 rv
= HttpFetch(mRequest
->GetPreferredAlternativeDataType());
487 FailWithNetworkError(rv
);
490 // Any failure is handled by FailWithNetworkError notifying the aObserver.
494 // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
495 // Functionality is often split between here, the CORS listener proxy and the
496 // Necko HTTP implementation.
497 nsresult
FetchDriver::HttpFetch(
498 const nsACString
& aPreferredAlternativeDataType
) {
499 MOZ_ASSERT(NS_IsMainThread());
501 // Step 1. "Let response be null."
503 mOnStopRequestCalled
= false;
506 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
507 NS_ENSURE_SUCCESS(rv
, rv
);
510 mRequest
->GetURL(url
);
511 nsCOMPtr
<nsIURI
> uri
;
512 rv
= NS_NewURI(getter_AddRefs(uri
), url
);
513 NS_ENSURE_SUCCESS(rv
, rv
);
515 // Unsafe requests aren't allowed with when using no-core mode.
516 if (mRequest
->Mode() == RequestMode::No_cors
&& mRequest
->UnsafeRequest() &&
517 (!mRequest
->HasSimpleMethod() ||
518 !mRequest
->Headers()->HasOnlySimpleHeaders())) {
519 MOZ_ASSERT(false, "The API should have caught this");
520 return NS_ERROR_DOM_BAD_URI
;
523 // non-GET requests aren't allowed for blob.
524 if (IsBlobURI(uri
)) {
525 nsAutoCString method
;
526 mRequest
->GetMethod(method
);
527 if (!method
.EqualsLiteral("GET")) {
528 return NS_ERROR_DOM_NETWORK_ERR
;
532 RefPtr
<PreloaderBase
> fetchPreload
= FindPreload(uri
);
534 fetchPreload
->RemoveSelf(mDocument
);
535 fetchPreload
->NotifyUsage(mDocument
, PreloaderBase::LoadBackground::Keep
);
537 rv
= fetchPreload
->AsyncConsume(this);
538 if (NS_SUCCEEDED(rv
)) {
541 mChannel
= fetchPreload
->Channel();
542 MOZ_ASSERT(mChannel
);
543 mChannel
->SetNotificationCallbacks(this);
545 // Copied from AsyncOnChannelRedirect.
546 for (const auto& redirect
: fetchPreload
->Redirects()) {
547 if (redirect
.Flags() & nsIChannelEventSink::REDIRECT_INTERNAL
) {
548 mRequest
->SetURLForInternalRedirect(redirect
.Flags(), redirect
.Spec(),
549 redirect
.Fragment());
551 mRequest
->AddURL(redirect
.Spec(), redirect
.Fragment());
558 // The preload failed to be consumed. Behave like there were no preload.
559 fetchPreload
= nullptr;
562 // Step 2 deals with letting ServiceWorkers intercept requests. This is
563 // handled by Necko after the channel is opened.
564 // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
565 // set based on the Request's flag.
567 // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
568 // true..." is handled by the CORS proxy.
570 // Step 3.2 "Set request's skip service worker flag." This isn't required
571 // since Necko will fall back to the network if the ServiceWorker does not
572 // respond with a valid Response.
574 // NS_StartCORSPreflight() will automatically kick off the original request
575 // if it succeeds, so we need to have everything setup for the original
578 // Step 3.3 "Let credentials flag be set if one of
579 // - request's credentials mode is "include"
580 // - request's credentials mode is "same-origin" and either the CORS flag
581 // is unset or response tainting is "opaque"
582 // is true, and unset otherwise."
584 // Set skip serviceworker flag.
585 // While the spec also gates on the client being a ServiceWorker, we can't
586 // infer that here. Instead we rely on callers to set the flag correctly.
587 const nsLoadFlags bypassFlag
= mRequest
->SkipServiceWorker()
588 ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER
591 nsSecurityFlags secFlags
= 0;
592 if (mRequest
->Mode() == RequestMode::Cors
) {
593 secFlags
|= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
594 } else if (mRequest
->Mode() == RequestMode::Same_origin
||
595 mRequest
->Mode() == RequestMode::Navigate
) {
596 secFlags
|= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
;
597 } else if (mRequest
->Mode() == RequestMode::No_cors
) {
598 secFlags
|= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
600 MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
601 return NS_ERROR_UNEXPECTED
;
604 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
605 secFlags
|= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS
;
608 // This handles the use credentials flag in "HTTP
609 // network or cache fetch" in the spec and decides whether to transmit
610 // cookies and other identifying information.
611 if (mRequest
->GetCredentialsMode() == RequestCredentials::Include
) {
612 secFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
613 } else if (mRequest
->GetCredentialsMode() == RequestCredentials::Omit
) {
614 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
615 } else if (mRequest
->GetCredentialsMode() ==
616 RequestCredentials::Same_origin
) {
617 secFlags
|= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
619 MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
620 return NS_ERROR_UNEXPECTED
;
623 // From here on we create a channel and set its properties with the
624 // information from the InternalRequest. This is an implementation detail.
625 MOZ_ASSERT(mLoadGroup
);
626 nsCOMPtr
<nsIChannel
> chan
;
628 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
| bypassFlag
;
630 MOZ_ASSERT(mDocument
->NodePrincipal() == mPrincipal
);
631 MOZ_ASSERT(mDocument
->CookieJarSettings() == mCookieJarSettings
);
632 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mDocument
, secFlags
,
633 mRequest
->ContentPolicyType(),
634 nullptr, /* aPerformanceStorage */
635 mLoadGroup
, nullptr, /* aCallbacks */
637 } else if (mClientInfo
.isSome()) {
638 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, mClientInfo
.ref(),
639 mController
, secFlags
, mRequest
->ContentPolicyType(),
640 mCookieJarSettings
, mPerformanceStorage
, mLoadGroup
,
641 nullptr, /* aCallbacks */
645 NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, secFlags
,
646 mRequest
->ContentPolicyType(), mCookieJarSettings
,
647 mPerformanceStorage
, mLoadGroup
, nullptr, /* aCallbacks */
650 NS_ENSURE_SUCCESS(rv
, rv
);
652 if (mCSPEventListener
) {
653 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
654 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
655 NS_ENSURE_SUCCESS(rv
, rv
);
659 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
660 rv
= loadInfo
->SetLoadingEmbedderPolicy(mRequest
->GetEmbedderPolicy());
661 NS_ENSURE_SUCCESS(rv
, rv
);
664 if (mAssociatedBrowsingContextID
) {
665 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
666 rv
= loadInfo
->SetWorkerAssociatedBrowsingContextID(
667 mAssociatedBrowsingContextID
);
670 // If the fetch is created by FetchEvent.request or NavigationPreload request,
671 // corresponding InterceptedHttpChannel information need to propagte to the
672 // channel of the fetch.
673 if (mRequest
->GetInterceptionTriggeringPrincipalInfo()) {
674 auto principalOrErr
= mozilla::ipc::PrincipalInfoToPrincipal(
675 *(mRequest
->GetInterceptionTriggeringPrincipalInfo().get()));
676 if (!principalOrErr
.isErr()) {
677 nsCOMPtr
<nsIPrincipal
> principal
= principalOrErr
.unwrap();
679 nsTArray
<nsCOMPtr
<nsIRedirectHistoryEntry
>> redirectChain
;
680 if (!mRequest
->InterceptionRedirectChain().IsEmpty()) {
681 for (const RedirectHistoryEntryInfo
& entryInfo
:
682 mRequest
->InterceptionRedirectChain()) {
683 nsCOMPtr
<nsIRedirectHistoryEntry
> entry
=
684 mozilla::ipc::RHEntryInfoToRHEntry(entryInfo
);
685 redirectChain
.AppendElement(entry
);
689 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
690 MOZ_ASSERT(loadInfo
);
691 loadInfo
->SetInterceptionInfo(new mozilla::net::InterceptionInfo(
692 principal
, mRequest
->InterceptionContentPolicyType(), redirectChain
,
693 mRequest
->InterceptionFromThirdParty()));
697 if (mDocument
&& mDocument
->GetEmbedderElement() &&
698 mDocument
->GetEmbedderElement()->IsAnyOfHTMLElements(nsGkAtoms::object
,
700 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
701 rv
= loadInfo
->SetIsFromObjectOrEmbed(true);
702 NS_ENSURE_SUCCESS(rv
, rv
);
705 // Insert ourselves into the notification callbacks chain so we can set
706 // headers on redirects.
709 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
710 chan
->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks
));
711 MOZ_ASSERT(!notificationCallbacks
);
714 chan
->SetNotificationCallbacks(this);
716 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(chan
));
717 // Mark channel as urgent-start if the Fetch is triggered by user input
719 if (cos
&& UserActivation::IsHandlingUserInput()) {
720 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
723 // Step 3.5 begins "HTTP network or cache fetch".
724 // HTTP network or cache fetch
725 // ---------------------------
726 // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
727 nsCOMPtr
<nsIHttpChannel
> httpChan
= do_QueryInterface(chan
);
730 nsAutoCString method
;
731 mRequest
->GetMethod(method
);
732 rv
= httpChan
->SetRequestMethod(method
);
733 NS_ENSURE_SUCCESS(rv
, rv
);
735 // Set the same headers.
736 SetRequestHeaders(httpChan
, false, false);
738 // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
739 // If request's referrer policy is the empty string and request's client is
740 // non-null, then set request's referrer policy to request's client's
741 // associated referrer policy.
742 // Basically, "client" is not in our implementation, we use
743 // EnvironmentReferrerPolicy of the worker or document context
744 ReferrerPolicy referrerPolicy
= mRequest
->GetEnvironmentReferrerPolicy();
745 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
746 mRequest
->SetReferrerPolicy(referrerPolicy
);
748 // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
749 // If request’s referrer policy is the empty string,
750 // then set request’s referrer policy to the user-set default policy.
751 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
752 nsCOMPtr
<nsILoadInfo
> loadInfo
= httpChan
->LoadInfo();
753 bool isPrivate
= loadInfo
->GetOriginAttributes().mPrivateBrowsingId
> 0;
755 ReferrerInfo::GetDefaultReferrerPolicy(httpChan
, uri
, isPrivate
);
756 mRequest
->SetReferrerPolicy(referrerPolicy
);
759 rv
= FetchUtil::SetRequestReferrer(mPrincipal
, mDocument
, httpChan
,
761 NS_ENSURE_SUCCESS(rv
, rv
);
763 // Bug 1120722 - Authorization will be handled later.
764 // Auth may require prompting, we don't support it yet.
765 // The next patch in this same bug prevents this from aborting the request.
766 // Credentials checks for CORS are handled by nsCORSListenerProxy,
768 nsCOMPtr
<nsIHttpChannelInternal
> internalChan
= do_QueryInterface(httpChan
);
770 rv
= internalChan
->SetRequestMode(mRequest
->Mode());
771 MOZ_ASSERT(NS_SUCCEEDED(rv
));
772 // Conversion between enumerations is safe due to static asserts in
773 // dom/workers/ServiceWorkerManager.cpp
774 rv
= internalChan
->SetRedirectMode(
775 static_cast<uint32_t>(mRequest
->GetRedirectMode()));
776 MOZ_ASSERT(NS_SUCCEEDED(rv
));
777 mRequest
->MaybeSkipCacheIfPerformingRevalidation();
778 rv
= internalChan
->SetFetchCacheMode(
779 static_cast<uint32_t>(mRequest
->GetCacheMode()));
780 MOZ_ASSERT(NS_SUCCEEDED(rv
));
781 rv
= internalChan
->SetIntegrityMetadata(mRequest
->GetIntegrity());
782 MOZ_ASSERT(NS_SUCCEEDED(rv
));
784 // Set the initiator type
785 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChan
));
787 timedChannel
->SetInitiatorType(u
"fetch"_ns
);
791 // Step 5. Proxy authentication will be handled by Necko.
793 // Continue setting up 'HTTPRequest'. Content-Type and body data.
794 nsCOMPtr
<nsIUploadChannel2
> uploadChan
= do_QueryInterface(chan
);
796 nsAutoCString contentType
;
798 mRequest
->Headers()->GetFirst("content-type"_ns
, contentType
, result
);
799 // We don't actually expect "result" to have failed here: that only happens
800 // for invalid header names. But if for some reason it did, just propagate
802 if (result
.Failed()) {
803 return result
.StealNSResult();
806 // Now contentType is the header that was set in mRequest->Headers(), or a
807 // void string if no header was set.
809 bool hasContentTypeHeader
=
810 mRequest
->Headers()->Has("content-type"_ns
, result
);
811 MOZ_ASSERT(!result
.Failed());
812 MOZ_ASSERT_IF(!hasContentTypeHeader
, contentType
.IsVoid());
816 nsCOMPtr
<nsIInputStream
> bodyStream
;
817 mRequest
->GetBody(getter_AddRefs(bodyStream
), &bodyLength
);
819 nsAutoCString method
;
820 mRequest
->GetMethod(method
);
821 rv
= uploadChan
->ExplicitSetUploadStream(bodyStream
, contentType
,
823 false /* aStreamHasHeaders */);
824 NS_ENSURE_SUCCESS(rv
, rv
);
828 // If preflight is required, start a "CORS preflight fetch"
829 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
830 // implementation is handled by the http channel calling into
831 // nsCORSListenerProxy. We just inform it which unsafe headers are included
833 if (mRequest
->Mode() == RequestMode::Cors
) {
834 AutoTArray
<nsCString
, 5> unsafeHeaders
;
835 mRequest
->Headers()->GetUnsafeHeaders(unsafeHeaders
);
836 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
837 loadInfo
->SetCorsPreflightInfo(unsafeHeaders
, false);
840 if (mIsTrackingFetch
&& StaticPrefs::network_http_tailing_enabled() && cos
) {
841 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
842 nsIClassOfService::Tail
);
845 if (nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(chan
)) {
846 if (mIsTrackingFetch
&&
847 StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
848 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
849 } else if (StaticPrefs::network_fetchpriority_enabled()) {
850 // TODO: Bug 1881040 - we need to take into account of destination for the
851 // fetchpriority mapping.
852 const auto fetchPriority
= ToFetchPriority(mRequest
->GetPriorityMode());
853 // The spec defines the priority to be set in an implementation defined
854 // manner (<https://fetch.spec.whatwg.org/#concept-fetch>, step 15.
855 // See corresponding preferences in StaticPrefList.yaml for more context.
856 const int32_t supportsPriorityDelta
=
857 FETCH_PRIORITY_ADJUSTMENT_FOR(global_fetch_api
, fetchPriority
);
858 p
->AdjustPriority(supportsPriorityDelta
);
862 NotifyNetworkMonitorAlternateStack(chan
, std::move(mOriginStack
));
863 if (mObserver
&& httpChan
) {
864 mObserver
->OnNotifyNetworkMonitorAlternateStack(httpChan
->ChannelId());
867 // Should set a Content-Range header for blob scheme, and also slice the
868 // blob appropriately, so we process the Range header here for later use.
869 if (IsBlobURI(uri
)) {
872 mRequest
->Headers()->Get("Range"_ns
, range
, result
);
873 MOZ_ASSERT(!result
.Failed());
874 if (!range
.IsVoid()) {
875 rv
= NS_SetChannelContentRangeForBlobURI(chan
, uri
, range
);
882 // if the preferred alternative data type in InternalRequest is not empty, set
883 // the data type on the created channel and also create a
884 // AlternativeDataStreamListener to be the stream listener of the channel.
885 if (!aPreferredAlternativeDataType
.IsEmpty()) {
886 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
888 cic
->PreferAlternativeDataType(
889 aPreferredAlternativeDataType
, ""_ns
,
890 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC
);
891 MOZ_ASSERT(!mAltDataListener
);
892 mAltDataListener
= new AlternativeDataStreamListener(
893 this, chan
, aPreferredAlternativeDataType
);
894 rv
= chan
->AsyncOpen(mAltDataListener
);
896 rv
= chan
->AsyncOpen(this);
899 // Integrity check cannot be done on alt-data yet.
900 if (mRequest
->GetIntegrity().IsEmpty()) {
901 MOZ_ASSERT(!FetchUtil::WasmAltDataType
.IsEmpty());
902 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
903 if (cic
&& StaticPrefs::javascript_options_wasm_caching() &&
904 !mRequest
->SkipWasmCaching()) {
905 cic
->PreferAlternativeDataType(
906 FetchUtil::WasmAltDataType
, nsLiteralCString(WASM_CONTENT_TYPE
),
907 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::
912 rv
= chan
->AsyncOpen(this);
919 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
925 SafeRefPtr
<InternalResponse
> FetchDriver::BeginAndGetFilteredResponse(
926 SafeRefPtr
<InternalResponse
> aResponse
, bool aFoundOpaqueRedirect
) {
927 MOZ_ASSERT(aResponse
);
928 AutoTArray
<nsCString
, 4> reqURLList
;
929 mRequest
->GetURLListWithoutFragment(reqURLList
);
930 MOZ_ASSERT(!reqURLList
.IsEmpty());
931 aResponse
->SetURLList(reqURLList
);
932 SafeRefPtr
<InternalResponse
> filteredResponse
;
933 if (aFoundOpaqueRedirect
) {
934 filteredResponse
= aResponse
->OpaqueRedirectResponse();
936 switch (mRequest
->GetResponseTainting()) {
937 case LoadTainting::Basic
:
938 filteredResponse
= aResponse
->BasicResponse();
940 case LoadTainting::CORS
:
941 filteredResponse
= aResponse
->CORSResponse();
943 case LoadTainting::Opaque
: {
944 filteredResponse
= aResponse
->OpaqueResponse();
945 nsresult rv
= filteredResponse
->GeneratePaddingInfo();
946 if (NS_WARN_IF(NS_FAILED(rv
))) {
952 MOZ_CRASH("Unexpected case");
956 MOZ_ASSERT(filteredResponse
);
957 MOZ_ASSERT(mObserver
);
958 MOZ_ASSERT(filteredResponse
);
959 if (!ShouldCheckSRI(*mRequest
, *filteredResponse
)) {
960 // Need to keep mObserver alive.
961 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
962 observer
->OnResponseAvailable(filteredResponse
.clonePtr());
964 mResponseAvailableCalled
= true;
968 return filteredResponse
;
971 void FetchDriver::FailWithNetworkError(nsresult rv
) {
972 AssertIsOnMainThread();
974 // Need to keep mObserver alive.
975 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
976 observer
->OnResponseAvailable(InternalResponse::NetworkError(rv
));
978 mResponseAvailableCalled
= true;
982 // mObserver could be null after OnResponseAvailable().
984 mObserver
->OnReportPerformanceTiming();
985 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
,
986 JS::UndefinedHandleValue
);
995 FetchDriver::OnStartRequest(nsIRequest
* aRequest
) {
996 AssertIsOnMainThread();
998 // Note, this can be called multiple times if we are doing an opaqueredirect.
999 // In that case we will get a simulated OnStartRequest() and then the real
1000 // channel will call in with an errored OnStartRequest().
1002 if (mFromPreload
&& mAborted
) {
1003 aRequest
->CancelWithReason(NS_BINDING_ABORTED
,
1004 "FetchDriver::OnStartRequest aborted"_ns
);
1005 return NS_BINDING_ABORTED
;
1009 // if the request is aborted, we remove the mObserver reference in
1010 // OnStopRequest or ~FetchDriver()
1011 MOZ_ASSERT_IF(!mAborted
, !mObserver
);
1012 return NS_BINDING_ABORTED
;
1016 aRequest
->GetStatus(&rv
);
1017 if (NS_FAILED(rv
)) {
1018 FailWithNetworkError(rv
);
1022 // We should only get to the following code once.
1023 MOZ_ASSERT(!mPipeOutputStream
);
1026 MOZ_ASSERT(false, "We should have mObserver here.");
1027 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
1028 return NS_ERROR_UNEXPECTED
;
1031 mNeedToObserveOnDataAvailable
= mObserver
->NeedOnDataAvailable();
1033 SafeRefPtr
<InternalResponse
> response
;
1034 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1035 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
);
1037 // On a successful redirect we perform the following substeps of HTTP Fetch,
1038 // step 5, "redirect status", step 11.
1040 bool foundOpaqueRedirect
= false;
1042 nsAutoCString
contentType(VoidCString());
1044 int64_t contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
1045 rv
= channel
->GetContentLength(&contentLength
);
1046 MOZ_ASSERT_IF(NS_FAILED(rv
),
1047 contentLength
== InternalResponse::UNKNOWN_BODY_SIZE
);
1050 channel
->GetContentType(contentType
);
1052 uint32_t responseStatus
= 0;
1053 rv
= httpChannel
->GetResponseStatus(&responseStatus
);
1054 if (NS_FAILED(rv
)) {
1055 FailWithNetworkError(rv
);
1059 if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus
)) {
1060 if (mRequest
->GetRedirectMode() == RequestRedirect::Error
) {
1061 FailWithNetworkError(NS_BINDING_ABORTED
);
1062 return NS_BINDING_FAILED
;
1064 if (mRequest
->GetRedirectMode() == RequestRedirect::Manual
) {
1065 foundOpaqueRedirect
= true;
1069 nsAutoCString statusText
;
1070 rv
= httpChannel
->GetResponseStatusText(statusText
);
1071 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1073 response
= MakeSafeRefPtr
<InternalResponse
>(responseStatus
, statusText
,
1074 mRequest
->GetCredentialsMode());
1076 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
1077 new mozilla::ipc::PrincipalInfo());
1078 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
1079 if (NS_WARN_IF(NS_FAILED(rv
))) {
1083 response
->SetPrincipalInfo(std::move(principalInfo
));
1085 response
->Headers()->FillResponseHeaders(httpChannel
);
1087 // If Content-Encoding or Transfer-Encoding headers are set, then the actual
1088 // Content-Length (which refer to the decoded data) is obscured behind the
1091 if (response
->Headers()->Has("content-encoding"_ns
, result
) ||
1092 response
->Headers()->Has("transfer-encoding"_ns
, result
)) {
1093 // We cannot trust the content-length when content-encoding or
1094 // transfer-encoding are set. There are many servers which just
1096 contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
1098 MOZ_ASSERT(!result
.Failed());
1100 // Should set a Content-Range header for blob scheme
1101 // (https://fetch.spec.whatwg.org/#scheme-fetch)
1102 nsAutoCString
contentRange(VoidCString());
1103 nsCOMPtr
<nsIBaseChannel
> baseChan
= do_QueryInterface(mChannel
);
1105 RefPtr
<mozilla::net::ContentRange
> range
= baseChan
->ContentRange();
1107 range
->AsHeader(contentRange
);
1111 response
= MakeSafeRefPtr
<InternalResponse
>(
1112 contentRange
.IsVoid() ? 200 : 206,
1113 contentRange
.IsVoid() ? "OK"_ns
: "Partial Content"_ns
,
1114 mRequest
->GetCredentialsMode());
1116 IgnoredErrorResult result
;
1117 if (!contentRange
.IsVoid()) {
1118 response
->Headers()->Append("Content-Range"_ns
, contentRange
, result
);
1119 MOZ_ASSERT(!result
.Failed());
1123 RefPtr
<CMimeType
> fullMimeType(baseChan
->FullMimeType());
1125 fullMimeType
->Serialize(contentType
);
1128 if (contentType
.IsVoid()) {
1129 channel
->GetContentType(contentType
);
1130 if (!contentType
.IsEmpty()) {
1131 nsAutoCString contentCharset
;
1132 channel
->GetContentCharset(contentCharset
);
1133 if (NS_SUCCEEDED(rv
) && !contentCharset
.IsEmpty()) {
1134 contentType
+= ";charset="_ns
+ contentCharset
;
1139 response
->Headers()->Append("Content-Type"_ns
, contentType
, result
);
1140 MOZ_ASSERT(!result
.Failed());
1142 if (contentLength
>= 0) {
1143 nsAutoCString contentLenStr
;
1144 contentLenStr
.AppendInt(contentLength
);
1146 IgnoredErrorResult result
;
1147 response
->Headers()->Append("Content-Length"_ns
, contentLenStr
, result
);
1148 MOZ_ASSERT(!result
.Failed());
1152 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
1154 if (mAltDataListener
) {
1155 // Skip the case that mAltDataListener->Status() equals to FALLBACK, that
1156 // means the opened channel for alternative data loading is reused for
1157 // loading the main data.
1158 if (mAltDataListener
->Status() !=
1159 AlternativeDataStreamListener::FALLBACK
) {
1160 // Verify the cache ID is the same with from alternative data cache.
1161 // If the cache ID is different, droping the alternative data loading,
1162 // otherwise setup the response's alternative body and cacheInfoChannel.
1163 uint64_t cacheEntryId
= 0;
1164 if (NS_SUCCEEDED(cic
->GetCacheEntryId(&cacheEntryId
)) &&
1166 mAltDataListener
->GetAlternativeDataCacheEntryId()) {
1167 mAltDataListener
->Cancel();
1169 // AlternativeDataStreamListener::OnStartRequest had already been
1170 // called, the alternative data input stream and cacheInfo channel
1172 nsCOMPtr
<nsICacheInfoChannel
> cacheInfo
=
1173 mAltDataListener
->GetCacheInfoChannel();
1174 nsCOMPtr
<nsIInputStream
> altInputStream
=
1175 mAltDataListener
->GetAlternativeInputStream();
1176 MOZ_ASSERT(altInputStream
&& cacheInfo
);
1177 response
->SetAlternativeBody(altInputStream
);
1178 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1179 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1180 "nsICacheInfoChannel", cacheInfo
, false));
1181 response
->SetCacheInfoChannel(handle
);
1183 } else if (!mAltDataListener
->GetAlternativeDataType().IsEmpty()) {
1184 // If the status is FALLBACK and the
1185 // mAltDataListener::mAlternativeDataType is not empty, that means the
1186 // data need to be saved into cache, setup the response's
1187 // nsICacheInfoChannel for caching the data after loading.
1188 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1189 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1190 "nsICacheInfoChannel", cic
, false));
1191 response
->SetCacheInfoChannel(handle
);
1193 } else if (!cic
->PreferredAlternativeDataTypes().IsEmpty()) {
1194 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes().Length() == 1);
1195 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes()[0].type().Equals(
1196 FetchUtil::WasmAltDataType
));
1198 cic
->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral(
1199 WASM_CONTENT_TYPE
));
1201 if (contentType
.EqualsLiteral(WASM_CONTENT_TYPE
)) {
1202 // We want to attach the CacheInfoChannel to the response object such
1203 // that we can track its origin when the Response object is manipulated
1204 // by JavaScript code. This is important for WebAssembly, which uses
1205 // fetch to query its sources in JavaScript and transfer the Response
1206 // object to other function responsible for storing the alternate data
1207 // using the CacheInfoChannel.
1208 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1209 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1210 "nsICacheInfoChannel", cic
, false));
1211 response
->SetCacheInfoChannel(handle
);
1216 // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
1217 nsAutoCString method
;
1218 mRequest
->GetMethod(method
);
1219 if (!(method
.EqualsLiteral("HEAD") || method
.EqualsLiteral("CONNECT"))) {
1220 // We open a pipe so that we can immediately set the pipe's read end as the
1221 // response's body. Setting the segment size to UINT32_MAX means that the
1222 // pipe has infinite space. The nsIChannel will continue to buffer data in
1223 // xpcom events even if we block on a fixed size pipe. It might be possible
1224 // to suspend the channel and then resume when there is space available, but
1225 // for now use an infinite pipe to avoid blocking.
1226 nsCOMPtr
<nsIInputStream
> pipeInputStream
;
1227 NS_NewPipe(getter_AddRefs(pipeInputStream
),
1228 getter_AddRefs(mPipeOutputStream
), 0, /* default segment size */
1229 UINT32_MAX
/* infinite pipe */,
1230 true /* non-blocking input, otherwise you deadlock */,
1231 false /* blocking output, since the pipe is 'in'finite */);
1232 response
->SetBody(pipeInputStream
, contentLength
);
1235 // If the request is a file channel, then remember the local path to
1236 // that file so we can later create File blobs rather than plain ones.
1237 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1239 nsCOMPtr
<nsIFile
> file
;
1240 rv
= fc
->GetFile(getter_AddRefs(file
));
1241 if (!NS_WARN_IF(NS_FAILED(rv
))) {
1243 file
->GetPath(path
);
1244 response
->SetBodyLocalPath(path
);
1247 // If the request is a blob URI, then remember that URI so that we
1248 // can later just use that blob instance instead of cloning it.
1249 nsCString blobURISpec
;
1250 GetBlobURISpecFromChannel(aRequest
, blobURISpec
);
1251 if (!blobURISpec
.IsVoid()) {
1252 response
->SetBodyBlobURISpec(blobURISpec
);
1256 response
->InitChannelInfo(channel
);
1258 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1259 // Propagate any tainting from the channel back to our response here. This
1260 // step is not reflected in the spec because the spec is written such that
1261 // FetchEvent.respondWith() just passes the already-tainted Response back to
1262 // the outer fetch(). In gecko, however, we serialize the Response through
1263 // the channel and must regenerate the tainting from the channel in the
1264 // interception case.
1265 mRequest
->MaybeIncreaseResponseTainting(loadInfo
->GetTainting());
1267 // Resolves fetch() promise which may trigger code running in a worker. Make
1268 // sure the Response is fully initialized before calling this.
1270 BeginAndGetFilteredResponse(std::move(response
), foundOpaqueRedirect
);
1271 if (NS_WARN_IF(!mResponse
)) {
1272 // Fail to generate a paddingInfo for opaque response.
1273 MOZ_DIAGNOSTIC_ASSERT(mRequest
->GetResponseTainting() ==
1274 LoadTainting::Opaque
&&
1275 !foundOpaqueRedirect
);
1276 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
1277 return NS_ERROR_UNEXPECTED
;
1280 // From "Main Fetch" step 19: SRI-part1.
1281 if (ShouldCheckSRI(*mRequest
, *mResponse
) && mSRIMetadata
.IsEmpty()) {
1282 nsIConsoleReportCollector
* reporter
= nullptr;
1284 reporter
= mObserver
->GetReporter();
1287 nsAutoCString sourceUri
;
1288 if (mDocument
&& mDocument
->GetDocumentURI()) {
1289 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1290 } else if (!mWorkerScript
.IsEmpty()) {
1291 sourceUri
.Assign(mWorkerScript
);
1293 SRICheck::IntegrityMetadata(mRequest
->GetIntegrity(), sourceUri
, reporter
,
1296 MakeUnique
<SRICheckDataVerifier
>(mSRIMetadata
, sourceUri
, reporter
);
1298 // Do not retarget off main thread when using SRI API.
1302 nsCOMPtr
<nsIEventTarget
> sts
=
1303 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
, &rv
);
1304 if (NS_WARN_IF(NS_FAILED(rv
))) {
1305 FailWithNetworkError(rv
);
1310 // Try to retarget off main thread.
1311 if (nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(aRequest
)) {
1312 RefPtr
<TaskQueue
> queue
=
1313 TaskQueue::Create(sts
.forget(), "FetchDriver STS Delivery Queue");
1314 Unused
<< NS_WARN_IF(NS_FAILED(rr
->RetargetDeliveryTo(queue
)));
1321 // Runnable to call the observer OnDataAvailable on the main-thread.
1322 class DataAvailableRunnable final
: public Runnable
{
1323 RefPtr
<FetchDriverObserver
> mObserver
;
1326 explicit DataAvailableRunnable(FetchDriverObserver
* aObserver
)
1327 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver
) {
1328 MOZ_ASSERT(aObserver
);
1333 mObserver
->OnDataAvailable();
1334 mObserver
= nullptr;
1339 struct SRIVerifierAndOutputHolder
{
1340 SRIVerifierAndOutputHolder(SRICheckDataVerifier
* aVerifier
,
1341 nsIOutputStream
* aOutputStream
)
1342 : mVerifier(aVerifier
), mOutputStream(aOutputStream
) {}
1344 SRICheckDataVerifier
* mVerifier
;
1345 nsIOutputStream
* mOutputStream
;
1348 SRIVerifierAndOutputHolder() = delete;
1351 // Just like NS_CopySegmentToStream, but also sends the data into an
1352 // SRICheckDataVerifier.
1353 nsresult
CopySegmentToStreamAndSRI(nsIInputStream
* aInStr
, void* aClosure
,
1354 const char* aBuffer
, uint32_t aOffset
,
1355 uint32_t aCount
, uint32_t* aCountWritten
) {
1356 auto holder
= static_cast<SRIVerifierAndOutputHolder
*>(aClosure
);
1357 MOZ_DIAGNOSTIC_ASSERT(holder
&& holder
->mVerifier
&& holder
->mOutputStream
,
1359 nsresult rv
= holder
->mVerifier
->Update(
1360 aCount
, reinterpret_cast<const uint8_t*>(aBuffer
));
1361 NS_ENSURE_SUCCESS(rv
, rv
);
1363 // The rest is just like NS_CopySegmentToStream.
1367 rv
= holder
->mOutputStream
->Write(aBuffer
, aCount
, &n
);
1368 if (NS_FAILED(rv
)) {
1373 *aCountWritten
+= n
;
1378 } // anonymous namespace
1381 FetchDriver::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
1382 uint64_t aOffset
, uint32_t aCount
) {
1383 // NB: This can be called on any thread! But we're guaranteed that it is
1384 // called between OnStartRequest and OnStopRequest, so we don't need to worry
1385 // about races for accesses in OnStartRequest, OnStopRequest and
1386 // member functions accessed before opening the channel.
1387 // However, we have a possibility of a race from FetchDriverAbortActions.
1388 // Hence, we need to ensure that we are not modifying any members accessed by
1389 // FetchDriver::FetchDriverAbortActions
1391 if (!mPipeOutputStream
) {
1392 // We ignore the body for HEAD/CONNECT requests.
1393 // nsIStreamListener mandates reading from the stream before returning.
1395 nsresult rv
= aInputStream
->ReadSegments(NS_DiscardSegment
, nullptr, aCount
,
1397 NS_ENSURE_SUCCESS(rv
, rv
);
1401 if (mNeedToObserveOnDataAvailable
) {
1402 mNeedToObserveOnDataAvailable
= false;
1404 // Need to keep mObserver alive.
1405 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1406 if (NS_IsMainThread()) {
1407 observer
->OnDataAvailable();
1409 RefPtr
<Runnable
> runnable
= new DataAvailableRunnable(observer
);
1410 nsresult rv
= mMainThreadEventTarget
->Dispatch(runnable
.forget(),
1411 NS_DISPATCH_NORMAL
);
1412 if (NS_WARN_IF(NS_FAILED(rv
))) {
1421 return NS_ERROR_UNEXPECTED
;
1424 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1425 // not write to aRead.
1427 MOZ_ASSERT(mPipeOutputStream
);
1429 // From "Main Fetch" step 19: SRI-part2.
1430 // Note: Avoid checking the hidden opaque body.
1432 if (mResponse
->Type() != ResponseType::Opaque
&&
1433 ShouldCheckSRI(*mRequest
, *mResponse
)) {
1434 MOZ_ASSERT(mSRIDataVerifier
);
1436 SRIVerifierAndOutputHolder
holder(mSRIDataVerifier
.get(),
1438 rv
= aInputStream
->ReadSegments(CopySegmentToStreamAndSRI
, &holder
, aCount
,
1441 rv
= aInputStream
->ReadSegments(NS_CopySegmentToStream
, mPipeOutputStream
,
1445 // If no data was read, it's possible the output stream is closed but the
1446 // ReadSegments call followed its contract of returning NS_OK despite write
1447 // errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
1448 // taken together with ReadSegments' contract, because the pipe will just
1449 // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1450 // So we must just assume the pipe is broken.
1451 if (aRead
== 0 && aCount
!= 0) {
1452 return NS_BASE_STREAM_CLOSED
;
1458 FetchDriver::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
1459 AssertIsOnMainThread();
1461 MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled
);
1462 mOnStopRequestCalled
= true;
1464 if (mObserver
&& mAborted
) {
1465 // fetch request was aborted.
1466 // We have already sent the observer
1467 // notification that request has been aborted in FetchDriverAbortActions.
1468 // Remove the observer reference and don't push anymore notifications.
1469 mObserver
= nullptr;
1472 // main data loading is going to finish, breaking the reference cycle.
1473 RefPtr
<AlternativeDataStreamListener
> altDataListener
=
1474 std::move(mAltDataListener
);
1476 // For PFetch and ServiceWorker navigationPreload, resource timing should be
1477 // reported before the body stream closing.
1479 mObserver
->OnReportPerformanceTiming();
1482 // We need to check mObserver, which is nulled by FailWithNetworkError(),
1483 // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1484 // mResponse will definitely be null so we must not take the else branch.
1485 if (NS_FAILED(aStatusCode
) || !mObserver
) {
1486 nsCOMPtr
<nsIAsyncOutputStream
> outputStream
=
1487 do_QueryInterface(mPipeOutputStream
);
1489 outputStream
->CloseWithStatus(NS_FAILED(aStatusCode
) ? aStatusCode
1490 : NS_BINDING_FAILED
);
1492 if (altDataListener
) {
1493 altDataListener
->Cancel();
1496 // We proceed as usual here, since we've already created a successful
1497 // response from OnStartRequest.
1499 MOZ_ASSERT(mResponse
);
1500 MOZ_ASSERT(!mResponse
->IsError());
1502 // From "Main Fetch" step 19: SRI-part3.
1503 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1504 MOZ_ASSERT(mSRIDataVerifier
);
1506 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1508 nsIConsoleReportCollector
* reporter
= nullptr;
1510 reporter
= mObserver
->GetReporter();
1513 nsAutoCString sourceUri
;
1514 if (mDocument
&& mDocument
->GetDocumentURI()) {
1515 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1516 } else if (!mWorkerScript
.IsEmpty()) {
1517 sourceUri
.Assign(mWorkerScript
);
1520 mSRIDataVerifier
->Verify(mSRIMetadata
, channel
, sourceUri
, reporter
);
1521 if (NS_FAILED(rv
)) {
1522 if (altDataListener
) {
1523 altDataListener
->Cancel();
1525 FailWithNetworkError(rv
);
1531 if (mPipeOutputStream
) {
1532 mPipeOutputStream
->Close();
1536 FinishOnStopRequest(altDataListener
);
1540 void FetchDriver::FinishOnStopRequest(
1541 AlternativeDataStreamListener
* aAltDataListener
) {
1542 AssertIsOnMainThread();
1543 // OnStopRequest is not called from channel, that means the main data loading
1544 // does not finish yet. Reaching here since alternative data loading finishes.
1545 if (!mOnStopRequestCalled
) {
1549 MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener
);
1550 // Wait for alternative data loading finish if we needed it.
1551 if (aAltDataListener
&&
1552 aAltDataListener
->Status() == AlternativeDataStreamListener::LOADING
) {
1553 // For LOADING case, channel holds the reference of altDataListener, no need
1554 // to restore it to mAltDataListener.
1559 // From "Main Fetch" step 19.1, 19.2: Process response.
1560 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1561 MOZ_ASSERT(mResponse
);
1562 // Need to keep mObserver alive.
1563 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1564 observer
->OnResponseAvailable(mResponse
.clonePtr());
1566 mResponseAvailableCalled
= true;
1572 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
,
1573 JS::UndefinedHandleValue
);
1574 mObserver
= nullptr;
1582 FetchDriver::ShouldPrepareForIntercept(nsIURI
* aURI
, nsIChannel
* aChannel
,
1583 bool* aShouldIntercept
) {
1584 MOZ_ASSERT(aChannel
);
1586 if (mInterceptController
) {
1587 MOZ_ASSERT(XRE_IsParentProcess());
1588 return mInterceptController
->ShouldPrepareForIntercept(aURI
, aChannel
,
1592 nsCOMPtr
<nsINetworkInterceptController
> controller
;
1593 NS_QueryNotificationCallbacks(nullptr, mLoadGroup
,
1594 NS_GET_IID(nsINetworkInterceptController
),
1595 getter_AddRefs(controller
));
1597 return controller
->ShouldPrepareForIntercept(aURI
, aChannel
,
1601 *aShouldIntercept
= false;
1606 FetchDriver::ChannelIntercepted(nsIInterceptedChannel
* aChannel
) {
1607 if (mInterceptController
) {
1608 MOZ_ASSERT(XRE_IsParentProcess());
1609 return mInterceptController
->ChannelIntercepted(aChannel
);
1612 nsCOMPtr
<nsINetworkInterceptController
> controller
;
1613 NS_QueryNotificationCallbacks(nullptr, mLoadGroup
,
1614 NS_GET_IID(nsINetworkInterceptController
),
1615 getter_AddRefs(controller
));
1617 return controller
->ChannelIntercepted(aChannel
);
1623 void FetchDriver::EnableNetworkInterceptControl() {
1624 MOZ_ASSERT(XRE_IsParentProcess());
1625 MOZ_ASSERT(NS_IsMainThread());
1626 MOZ_ASSERT(!mInterceptController
);
1627 mInterceptController
= new ServiceWorkerInterceptController();
1631 FetchDriver::AsyncOnChannelRedirect(nsIChannel
* aOldChannel
,
1632 nsIChannel
* aNewChannel
, uint32_t aFlags
,
1633 nsIAsyncVerifyRedirectCallback
* aCallback
) {
1634 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= do_QueryInterface(aOldChannel
);
1635 nsCOMPtr
<nsIHttpChannel
> newHttpChannel
= do_QueryInterface(aNewChannel
);
1636 if (oldHttpChannel
&& newHttpChannel
) {
1637 nsAutoCString method
;
1638 mRequest
->GetMethod(method
);
1641 bool rewriteToGET
= false;
1642 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(method
,
1645 // we need to strip Authentication headers for cross-origin requests
1646 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
1647 bool skipAuthHeader
=
1648 (StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
1649 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel
, aNewChannel
, aFlags
));
1651 SetRequestHeaders(newHttpChannel
, rewriteToGET
, skipAuthHeader
);
1654 // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1655 // However, ignore internal redirects here. We don't want to flip
1656 // Response.redirected to true if an internal redirect occurs. These
1657 // should be transparent to script.
1658 nsCOMPtr
<nsIURI
> uri
;
1659 MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(uri
)));
1661 nsCOMPtr
<nsIURI
> uriClone
;
1662 nsresult rv
= NS_GetURIWithoutRef(uri
, getter_AddRefs(uriClone
));
1663 if (NS_WARN_IF(NS_FAILED(rv
))) {
1667 rv
= uriClone
->GetSpec(spec
);
1668 if (NS_WARN_IF(NS_FAILED(rv
))) {
1672 rv
= uri
->GetRef(fragment
);
1673 if (NS_WARN_IF(NS_FAILED(rv
))) {
1677 if (!(aFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
)) {
1678 mRequest
->AddURL(spec
, fragment
);
1680 // Overwrite the URL only when the request is redirected by a service
1682 mRequest
->SetURLForInternalRedirect(aFlags
, spec
, fragment
);
1685 // In redirect, httpChannel already took referrer-policy into account, so
1686 // updates request’s associated referrer policy from channel.
1687 UpdateReferrerInfoFromNewChannel(aNewChannel
);
1689 aCallback
->OnRedirectVerifyCallback(NS_OK
);
1694 FetchDriver::CheckListenerChain() { return NS_OK
; }
1697 FetchDriver::OnDataFinished(nsresult
) { return NS_OK
; }
1700 FetchDriver::GetInterface(const nsIID
& aIID
, void** aResult
) {
1701 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
1702 *aResult
= static_cast<nsIChannelEventSink
*>(this);
1706 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
1707 *aResult
= static_cast<nsIStreamListener
*>(this);
1711 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
1712 *aResult
= static_cast<nsIRequestObserver
*>(this);
1717 return QueryInterface(aIID
, aResult
);
1720 void FetchDriver::SetDocument(Document
* aDocument
) {
1721 // Cannot set document after Fetch() has been called.
1722 MOZ_ASSERT(!mFetchCalled
);
1723 mDocument
= aDocument
;
1726 void FetchDriver::SetCSPEventListener(nsICSPEventListener
* aCSPEventListener
) {
1727 MOZ_ASSERT(!mFetchCalled
);
1728 mCSPEventListener
= aCSPEventListener
;
1731 void FetchDriver::SetClientInfo(const ClientInfo
& aClientInfo
) {
1732 MOZ_ASSERT(!mFetchCalled
);
1733 mClientInfo
.emplace(aClientInfo
);
1736 void FetchDriver::SetController(
1737 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
1738 MOZ_ASSERT(!mFetchCalled
);
1739 mController
= aController
;
1742 PerformanceTimingData
* FetchDriver::GetPerformanceTimingData(
1743 nsAString
& aInitiatorType
, nsAString
& aEntryName
) {
1744 MOZ_ASSERT(XRE_IsParentProcess());
1749 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(mChannel
);
1750 if (!timedChannel
) {
1753 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1757 return dom::PerformanceTimingData::Create(timedChannel
, httpChannel
, 0,
1758 aInitiatorType
, aEntryName
);
1761 void FetchDriver::SetRequestHeaders(nsIHttpChannel
* aChannel
,
1762 bool aStripRequestBodyHeader
,
1763 bool aStripAuthHeader
) const {
1764 MOZ_ASSERT(aChannel
);
1766 // nsIHttpChannel has a set of pre-configured headers (Accept,
1767 // Accept-Languages, ...) and we don't want to merge the Request's headers
1768 // with them. This array is used to know if the current header has been aleady
1769 // set, if yes, we ask necko to merge it with the previous one, otherwise, we
1770 // don't want the merge.
1771 nsTArray
<nsCString
> headersSet
;
1773 AutoTArray
<InternalHeaders::Entry
, 5> headers
;
1774 mRequest
->Headers()->GetEntries(headers
);
1775 for (uint32_t i
= 0; i
< headers
.Length(); ++i
) {
1776 if (aStripRequestBodyHeader
&&
1777 (headers
[i
].mName
.LowerCaseEqualsASCII("content-type") ||
1778 headers
[i
].mName
.LowerCaseEqualsASCII("content-encoding") ||
1779 headers
[i
].mName
.LowerCaseEqualsASCII("content-language") ||
1780 headers
[i
].mName
.LowerCaseEqualsASCII("content-location"))) {
1784 if (aStripAuthHeader
&&
1785 headers
[i
].mName
.LowerCaseEqualsASCII("authorization")) {
1789 bool alreadySet
= headersSet
.Contains(headers
[i
].mName
);
1791 headersSet
.AppendElement(headers
[i
].mName
);
1794 if (headers
[i
].mValue
.IsEmpty()) {
1795 DebugOnly
<nsresult
> rv
=
1796 aChannel
->SetEmptyRequestHeader(headers
[i
].mName
);
1797 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1799 DebugOnly
<nsresult
> rv
= aChannel
->SetRequestHeader(
1800 headers
[i
].mName
, headers
[i
].mValue
, alreadySet
/* merge */);
1801 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1806 void FetchDriver::RunAbortAlgorithm() { FetchDriverAbortActions(Signal()); }
1808 void FetchDriver::FetchDriverAbortActions(AbortSignalImpl
* aSignalImpl
) {
1809 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
1813 mResponseAvailableCalled
= true;
1815 JS::Rooted
<JS::Value
> reason(RootingCx());
1817 reason
.set(aSignalImpl
->RawReason());
1819 mObserver
->OnResponseEnd(FetchDriverObserver::eAborted
, reason
);
1820 // As a part of cleanup, we are not removing the mObserver reference as it
1821 // could race with mObserver access in OnDataAvailable when it runs OMT.
1822 // We will be removing the reference in the OnStopRequest which guaranteed
1823 // to run after cancelling the channel.
1827 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
1828 "FetchDriver::RunAbortAlgorithm"_ns
);
1835 } // namespace mozilla::dom