Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / fetch / FetchDriver.cpp
blob79b54f4c4a01452009da2b818bb2d1e0b4be270c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "js/Value.h"
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"
18 #include "nsIFile.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"
29 #include "nsIPipe.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"
60 #include "Fetch.h"
61 #include "FetchUtil.h"
62 #include "InternalRequest.h"
63 #include "InternalResponse.h"
65 namespace mozilla::dom {
67 namespace {
69 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) {
70 MOZ_ASSERT(aRequest);
72 aBlobURISpec.SetIsVoid(true);
74 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
75 if (!channel) {
76 return;
79 nsCOMPtr<nsIURI> uri;
80 nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
81 if (NS_FAILED(rv)) {
82 return;
85 if (!dom::IsBlobURI(uri)) {
86 return;
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 {
105 public:
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
136 // CANCELED anymore.
137 enum eStatus { LOADING = 0, COMPLETED, CANCELED, FALLBACK };
139 AlternativeDataStreamListener(FetchDriver* aFetchDriver, nsIChannel* aChannel,
140 const nsACString& aAlternativeDataType);
141 eStatus Status();
142 void Cancel();
143 uint64_t GetAlternativeDataCacheEntryId();
144 const nsACString& GetAlternativeDataType() const;
145 already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel();
146 already_AddRefed<nsIInputStream> GetAlternativeInputStream();
148 private:
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),
173 mChannel(aChannel),
174 mStatus(AlternativeDataStreamListener::LOADING) {
175 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver);
176 MOZ_DIAGNOSTIC_ASSERT(mChannel);
179 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() {
180 return mStatus;
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
190 // to FetchDriver
191 mChannel->CancelWithReason(NS_BINDING_ABORTED,
192 "AlternativeDataStreamListener::Cancel"_ns);
193 mChannel = nullptr;
195 mStatus = AlternativeDataStreamListener::CANCELED;
198 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
199 return mAlternativeDataCacheEntryId;
202 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType()
203 const {
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();
219 NS_IMETHODIMP
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);
259 NS_IMETHODIMP
260 AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest,
261 nsIInputStream* aInputStream,
262 uint64_t aOffset,
263 uint32_t aCount) {
264 if (mStatus == AlternativeDataStreamListener::LOADING) {
265 MOZ_ASSERT(mPipeAlternativeOutputStream);
266 uint32_t read = 0;
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,
273 aCount);
275 return NS_OK;
278 NS_IMETHODIMP
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) {
288 // do nothing
289 return NS_OK;
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);
317 return NS_OK;
320 NS_IMETHODIMP
321 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
323 NS_IMETHODIMP
324 AlternativeDataStreamListener::OnDataFinished(nsresult aStatus) {
325 return NS_OK;
328 //-----------------------------------------------------------------------------
329 // FetchDriver
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)
351 #ifdef DEBUG
353 mResponseAvailableCalled(false),
354 mFetchCalled(false)
355 #endif
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);
370 if (mObserver) {
371 mObserver = nullptr;
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.
380 if (!mDocument) {
381 // Preloads are mapped on the document, no document, no preload.
382 return nullptr;
384 CORSMode cors;
385 switch (mRequest->Mode()) {
386 case RequestMode::No_cors:
387 cors = CORSMode::CORS_NONE;
388 break;
389 case RequestMode::Cors:
390 cors = mRequest->GetCredentialsMode() == RequestCredentials::Include
391 ? CORSMode::CORS_USE_CREDENTIALS
392 : CORSMode::CORS_ANONYMOUS;
393 break;
394 default:
395 // Can't be satisfied by a preload because preload cannot define any of
396 // remaining modes.
397 return nullptr;
399 if (!mRequest->Headers()->HasOnlySimpleHeaders()) {
400 // Preload can't set any headers.
401 return nullptr;
403 if (!mRequest->GetIntegrity().IsEmpty()) {
404 // There is currently no support for SRI checking in the fetch preloader.
405 return nullptr;
407 if (mRequest->GetCacheMode() != RequestCache::Default) {
408 // Preload can only go with the default caching mode.
409 return nullptr;
411 if (mRequest->SkipServiceWorker()) {
412 // Preload can't be forbidden interception.
413 return nullptr;
415 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
416 // Preload always follows redirects.
417 return nullptr;
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.
424 return nullptr;
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);
435 if (!httpChannel) {
436 return;
439 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
440 if (!referrerInfo) {
441 return;
444 nsAutoCString 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();
453 #ifdef DEBUG
454 MOZ_ASSERT(!mFetchCalled);
455 mFetchCalled = true;
456 #endif
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))) {
469 return rv;
472 mRequest->SetPrincipalInfo(std::move(principalInfo));
474 // If the signal is aborted, it's time to inform the observer and terminate
475 // the operation.
476 if (aSignalImpl) {
477 if (aSignalImpl->Aborted()) {
478 FetchDriverAbortActions(aSignalImpl);
479 return NS_OK;
482 Follow(aSignalImpl);
485 rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
486 if (NS_FAILED(rv)) {
487 FailWithNetworkError(rv);
490 // Any failure is handled by FailWithNetworkError notifying the aObserver.
491 return NS_OK;
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."
502 mResponse = nullptr;
503 mOnStopRequestCalled = false;
504 nsresult rv;
506 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
507 NS_ENSURE_SUCCESS(rv, rv);
509 nsAutoCString url;
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);
533 if (fetchPreload) {
534 fetchPreload->RemoveSelf(mDocument);
535 fetchPreload->NotifyUsage(mDocument, PreloaderBase::LoadBackground::Keep);
537 rv = fetchPreload->AsyncConsume(this);
538 if (NS_SUCCEEDED(rv)) {
539 mFromPreload = true;
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());
550 } else {
551 mRequest->AddURL(redirect.Spec(), redirect.Fragment());
555 return NS_OK;
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
576 // request too.
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
589 : 0;
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;
599 } else {
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;
618 } else {
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;
629 if (mDocument) {
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 */
636 loadFlags, ios);
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 */
642 loadFlags, ios);
643 } else {
644 rv =
645 NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags,
646 mRequest->ContentPolicyType(), mCookieJarSettings,
647 mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */
648 loadFlags, ios);
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,
699 nsGkAtoms::embed)) {
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.
707 #ifdef DEBUG
709 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
710 chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
711 MOZ_ASSERT(!notificationCallbacks);
713 #endif
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
718 // events.
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);
728 if (httpChan) {
729 // Copy the method.
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;
754 referrerPolicy =
755 ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate);
756 mRequest->SetReferrerPolicy(referrerPolicy);
759 rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
760 *mRequest);
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));
786 if (timedChannel) {
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);
795 if (uploadChan) {
796 nsAutoCString contentType;
797 ErrorResult result;
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
801 // it out.
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.
808 #ifdef DEBUG
809 bool hasContentTypeHeader =
810 mRequest->Headers()->Has("content-type"_ns, result);
811 MOZ_ASSERT(!result.Failed());
812 MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
813 #endif // DEBUG
815 int64_t bodyLength;
816 nsCOMPtr<nsIInputStream> bodyStream;
817 mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
818 if (bodyStream) {
819 nsAutoCString method;
820 mRequest->GetMethod(method);
821 rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
822 bodyLength, method,
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
832 // in the request.
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)) {
870 ErrorResult result;
871 nsAutoCString range;
872 mRequest->Headers()->Get("Range"_ns, range, result);
873 MOZ_ASSERT(!result.Failed());
874 if (!range.IsVoid()) {
875 rv = NS_SetChannelContentRangeForBlobURI(chan, uri, range);
876 if (NS_FAILED(rv)) {
877 return rv;
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);
887 if (cic) {
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);
895 } else {
896 rv = chan->AsyncOpen(this);
898 } else {
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::
908 SERIALIZE);
912 rv = chan->AsyncOpen(this);
915 if (NS_FAILED(rv)) {
916 return rv;
919 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
921 mChannel = chan;
922 return NS_OK;
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();
935 } else {
936 switch (mRequest->GetResponseTainting()) {
937 case LoadTainting::Basic:
938 filteredResponse = aResponse->BasicResponse();
939 break;
940 case LoadTainting::CORS:
941 filteredResponse = aResponse->CORSResponse();
942 break;
943 case LoadTainting::Opaque: {
944 filteredResponse = aResponse->OpaqueResponse();
945 nsresult rv = filteredResponse->GeneratePaddingInfo();
946 if (NS_WARN_IF(NS_FAILED(rv))) {
947 return nullptr;
949 break;
951 default:
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());
963 #ifdef DEBUG
964 mResponseAvailableCalled = true;
965 #endif
968 return filteredResponse;
971 void FetchDriver::FailWithNetworkError(nsresult rv) {
972 AssertIsOnMainThread();
973 if (mObserver) {
974 // Need to keep mObserver alive.
975 RefPtr<FetchDriverObserver> observer = mObserver;
976 observer->OnResponseAvailable(InternalResponse::NetworkError(rv));
977 #ifdef DEBUG
978 mResponseAvailableCalled = true;
979 #endif
982 // mObserver could be null after OnResponseAvailable().
983 if (mObserver) {
984 mObserver->OnReportPerformanceTiming();
985 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking,
986 JS::UndefinedHandleValue);
987 mObserver = nullptr;
990 mChannel = nullptr;
991 Unfollow();
994 NS_IMETHODIMP
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;
1008 if (!mChannel) {
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;
1015 nsresult rv;
1016 aRequest->GetStatus(&rv);
1017 if (NS_FAILED(rv)) {
1018 FailWithNetworkError(rv);
1019 return rv;
1022 // We should only get to the following code once.
1023 MOZ_ASSERT(!mPipeOutputStream);
1025 if (!mObserver) {
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);
1049 if (httpChannel) {
1050 channel->GetContentType(contentType);
1052 uint32_t responseStatus = 0;
1053 rv = httpChannel->GetResponseStatus(&responseStatus);
1054 if (NS_FAILED(rv)) {
1055 FailWithNetworkError(rv);
1056 return 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))) {
1080 return 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
1089 // encodings.
1090 ErrorResult result;
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
1095 // get this wrong.
1096 contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
1098 MOZ_ASSERT(!result.Failed());
1099 } else {
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);
1104 if (baseChan) {
1105 RefPtr<mozilla::net::ContentRange> range = baseChan->ContentRange();
1106 if (range) {
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());
1122 if (baseChan) {
1123 RefPtr<CMimeType> fullMimeType(baseChan->FullMimeType());
1124 if (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);
1153 if (cic) {
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)) &&
1165 cacheEntryId !=
1166 mAltDataListener->GetAlternativeDataCacheEntryId()) {
1167 mAltDataListener->Cancel();
1168 } else {
1169 // AlternativeDataStreamListener::OnStartRequest had already been
1170 // called, the alternative data input stream and cacheInfo channel
1171 // must be created.
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));
1197 MOZ_ASSERT(
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);
1238 if (fc) {
1239 nsCOMPtr<nsIFile> file;
1240 rv = fc->GetFile(getter_AddRefs(file));
1241 if (!NS_WARN_IF(NS_FAILED(rv))) {
1242 nsAutoString path;
1243 file->GetPath(path);
1244 response->SetBodyLocalPath(path);
1246 } else {
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.
1269 mResponse =
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;
1283 if (mObserver) {
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,
1294 &mSRIMetadata);
1295 mSRIDataVerifier =
1296 MakeUnique<SRICheckDataVerifier>(mSRIMetadata, sourceUri, reporter);
1298 // Do not retarget off main thread when using SRI API.
1299 return NS_OK;
1302 nsCOMPtr<nsIEventTarget> sts =
1303 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1304 if (NS_WARN_IF(NS_FAILED(rv))) {
1305 FailWithNetworkError(rv);
1306 // Cancel request.
1307 return 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)));
1316 return NS_OK;
1319 namespace {
1321 // Runnable to call the observer OnDataAvailable on the main-thread.
1322 class DataAvailableRunnable final : public Runnable {
1323 RefPtr<FetchDriverObserver> mObserver;
1325 public:
1326 explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1327 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) {
1328 MOZ_ASSERT(aObserver);
1331 NS_IMETHOD
1332 Run() override {
1333 mObserver->OnDataAvailable();
1334 mObserver = nullptr;
1335 return NS_OK;
1339 struct SRIVerifierAndOutputHolder {
1340 SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1341 nsIOutputStream* aOutputStream)
1342 : mVerifier(aVerifier), mOutputStream(aOutputStream) {}
1344 SRICheckDataVerifier* mVerifier;
1345 nsIOutputStream* mOutputStream;
1347 private:
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,
1358 "Bogus holder");
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.
1364 *aCountWritten = 0;
1365 while (aCount) {
1366 uint32_t n = 0;
1367 rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1368 if (NS_FAILED(rv)) {
1369 return rv;
1371 aBuffer += n;
1372 aCount -= n;
1373 *aCountWritten += n;
1375 return NS_OK;
1378 } // anonymous namespace
1380 NS_IMETHODIMP
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.
1394 uint32_t totalRead;
1395 nsresult rv = aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount,
1396 &totalRead);
1397 NS_ENSURE_SUCCESS(rv, rv);
1398 return NS_OK;
1401 if (mNeedToObserveOnDataAvailable) {
1402 mNeedToObserveOnDataAvailable = false;
1403 if (mObserver) {
1404 // Need to keep mObserver alive.
1405 RefPtr<FetchDriverObserver> observer = mObserver;
1406 if (NS_IsMainThread()) {
1407 observer->OnDataAvailable();
1408 } else {
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))) {
1413 return rv;
1419 if (!mResponse) {
1420 MOZ_ASSERT(false);
1421 return NS_ERROR_UNEXPECTED;
1424 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1425 // not write to aRead.
1426 uint32_t aRead = 0;
1427 MOZ_ASSERT(mPipeOutputStream);
1429 // From "Main Fetch" step 19: SRI-part2.
1430 // Note: Avoid checking the hidden opaque body.
1431 nsresult rv;
1432 if (mResponse->Type() != ResponseType::Opaque &&
1433 ShouldCheckSRI(*mRequest, *mResponse)) {
1434 MOZ_ASSERT(mSRIDataVerifier);
1436 SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(),
1437 mPipeOutputStream);
1438 rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount,
1439 &aRead);
1440 } else {
1441 rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream,
1442 aCount, &aRead);
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;
1454 return rv;
1457 NS_IMETHODIMP
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.
1478 if (mObserver) {
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);
1488 if (outputStream) {
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.
1498 } else {
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;
1509 if (mObserver) {
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);
1519 nsresult rv =
1520 mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri, reporter);
1521 if (NS_FAILED(rv)) {
1522 if (altDataListener) {
1523 altDataListener->Cancel();
1525 FailWithNetworkError(rv);
1526 // Cancel request.
1527 return rv;
1531 if (mPipeOutputStream) {
1532 mPipeOutputStream->Close();
1536 FinishOnStopRequest(altDataListener);
1537 return NS_OK;
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) {
1546 return;
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.
1555 return;
1558 if (mObserver) {
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());
1565 #ifdef DEBUG
1566 mResponseAvailableCalled = true;
1567 #endif
1571 if (mObserver) {
1572 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking,
1573 JS::UndefinedHandleValue);
1574 mObserver = nullptr;
1577 mChannel = nullptr;
1578 Unfollow();
1581 NS_IMETHODIMP
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,
1589 aShouldIntercept);
1592 nsCOMPtr<nsINetworkInterceptController> controller;
1593 NS_QueryNotificationCallbacks(nullptr, mLoadGroup,
1594 NS_GET_IID(nsINetworkInterceptController),
1595 getter_AddRefs(controller));
1596 if (controller) {
1597 return controller->ShouldPrepareForIntercept(aURI, aChannel,
1598 aShouldIntercept);
1601 *aShouldIntercept = false;
1602 return NS_OK;
1605 NS_IMETHODIMP
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));
1616 if (controller) {
1617 return controller->ChannelIntercepted(aChannel);
1620 return NS_OK;
1623 void FetchDriver::EnableNetworkInterceptControl() {
1624 MOZ_ASSERT(XRE_IsParentProcess());
1625 MOZ_ASSERT(NS_IsMainThread());
1626 MOZ_ASSERT(!mInterceptController);
1627 mInterceptController = new ServiceWorkerInterceptController();
1630 NS_IMETHODIMP
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);
1640 // Fetch 4.4.11
1641 bool rewriteToGET = false;
1642 Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
1643 &rewriteToGET);
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))) {
1664 return rv;
1666 nsCString spec;
1667 rv = uriClone->GetSpec(spec);
1668 if (NS_WARN_IF(NS_FAILED(rv))) {
1669 return rv;
1671 nsCString fragment;
1672 rv = uri->GetRef(fragment);
1673 if (NS_WARN_IF(NS_FAILED(rv))) {
1674 return rv;
1677 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1678 mRequest->AddURL(spec, fragment);
1679 } else {
1680 // Overwrite the URL only when the request is redirected by a service
1681 // worker.
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);
1690 return NS_OK;
1693 NS_IMETHODIMP
1694 FetchDriver::CheckListenerChain() { return NS_OK; }
1696 NS_IMETHODIMP
1697 FetchDriver::OnDataFinished(nsresult) { return NS_OK; }
1699 NS_IMETHODIMP
1700 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) {
1701 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1702 *aResult = static_cast<nsIChannelEventSink*>(this);
1703 NS_ADDREF_THIS();
1704 return NS_OK;
1706 if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1707 *aResult = static_cast<nsIStreamListener*>(this);
1708 NS_ADDREF_THIS();
1709 return NS_OK;
1711 if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1712 *aResult = static_cast<nsIRequestObserver*>(this);
1713 NS_ADDREF_THIS();
1714 return NS_OK;
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());
1745 if (!mChannel) {
1746 return nullptr;
1749 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
1750 if (!timedChannel) {
1751 return nullptr;
1753 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
1754 if (!httpChannel) {
1755 return nullptr;
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"))) {
1781 continue;
1784 if (aStripAuthHeader &&
1785 headers[i].mName.LowerCaseEqualsASCII("authorization")) {
1786 continue;
1789 bool alreadySet = headersSet.Contains(headers[i].mName);
1790 if (!alreadySet) {
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));
1798 } else {
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());
1811 if (mObserver) {
1812 #ifdef DEBUG
1813 mResponseAvailableCalled = true;
1814 #endif
1815 JS::Rooted<JS::Value> reason(RootingCx());
1816 if (aSignalImpl) {
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.
1826 if (mChannel) {
1827 mChannel->CancelWithReason(NS_BINDING_ABORTED,
1828 "FetchDriver::RunAbortAlgorithm"_ns);
1829 mChannel = nullptr;
1832 mAborted = true;
1835 } // namespace mozilla::dom