1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/DebugOnly.h"
8 #include "mozilla/dom/FetchDriver.h"
9 
10 #include "nsIAsyncVerifyRedirectCallback.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsICookieJarSettings.h"
13 #include "nsIFile.h"
14 #include "nsIInputStream.h"
15 #include "nsIOutputStream.h"
16 #include "nsIFileChannel.h"
17 #include "nsIHttpChannel.h"
18 #include "nsIHttpChannelInternal.h"
19 #include "nsISupportsPriority.h"
20 #include "nsIThreadRetargetableRequest.h"
21 #include "nsIUploadChannel2.h"
22 #include "nsIInterfaceRequestorUtils.h"
23 #include "nsIPipe.h"
24 
25 #include "nsContentPolicyUtils.h"
26 #include "nsDataHandler.h"
27 #include "nsNetUtil.h"
28 #include "nsPrintfCString.h"
29 #include "nsProxyRelease.h"
30 #include "nsStreamUtils.h"
31 #include "nsStringStream.h"
32 #include "nsHttpChannel.h"
33 
34 #include "mozilla/dom/BlobURLProtocolHandler.h"
35 #include "mozilla/dom/File.h"
36 #include "mozilla/dom/PerformanceStorage.h"
37 #include "mozilla/dom/UserActivation.h"
38 #include "mozilla/dom/WorkerCommon.h"
39 #include "mozilla/PreloaderBase.h"
40 #include "mozilla/net/NeckoChannelParams.h"
41 #include "mozilla/ipc/PBackgroundSharedTypes.h"
42 #include "mozilla/StaticPrefs_browser.h"
43 #include "mozilla/StaticPrefs_network.h"
44 #include "mozilla/StaticPrefs_privacy.h"
45 #include "mozilla/Unused.h"
46 
47 #include "Fetch.h"
48 #include "FetchUtil.h"
49 #include "InternalRequest.h"
50 #include "InternalResponse.h"
51 
52 namespace mozilla::dom {
53 
54 namespace {
55 
GetBlobURISpecFromChannel(nsIRequest * aRequest,nsCString & aBlobURISpec)56 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) {
57   MOZ_ASSERT(aRequest);
58 
59   aBlobURISpec.SetIsVoid(true);
60 
61   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
62   if (!channel) {
63     return;
64   }
65 
66   nsCOMPtr<nsIURI> uri;
67   nsresult rv = channel->GetURI(getter_AddRefs(uri));
68   if (NS_FAILED(rv)) {
69     return;
70   }
71 
72   if (!dom::IsBlobURI(uri)) {
73     return;
74   }
75 
76   uri->GetSpec(aBlobURISpec);
77 }
78 
ShouldCheckSRI(const InternalRequest & aRequest,const InternalResponse & aResponse)79 bool ShouldCheckSRI(const InternalRequest& aRequest,
80                     const InternalResponse& aResponse) {
81   return !aRequest.GetIntegrity().IsEmpty() &&
82          aResponse.Type() != ResponseType::Error;
83 }
84 
85 }  // anonymous namespace
86 
87 //-----------------------------------------------------------------------------
88 // AlternativeDataStreamListener
89 //-----------------------------------------------------------------------------
90 class AlternativeDataStreamListener final
91     : public nsIStreamListener,
92       public nsIThreadRetargetableStreamListener {
93  public:
94   NS_DECL_THREADSAFE_ISUPPORTS
95   NS_DECL_NSIREQUESTOBSERVER
96   NS_DECL_NSISTREAMLISTENER
97   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
98 
99   // The status of AlternativeDataStreamListener
100   // LOADING: is the initial status, loading the alternative data
101   // COMPLETED: Alternative data loading is completed
102   // CANCELED: Alternative data loading is canceled, this would make
103   //           AlternativeDataStreamListener ignore all channel callbacks
104   // FALLBACK: fallback the channel callbacks to FetchDriver
105   // Depends on different situaions, the status transition could be followings
106   // 1. LOADING->COMPLETED
107   //    This is the normal status transition for alternative data loading
108   //
109   // 2. LOADING->CANCELED
110   //    LOADING->COMPLETED->CANCELED
111   //    Alternative data loading could be canceled when cacheId from alternative
112   //    data channel does not match with from main data channel(The cacheID
113   //    checking is in FetchDriver::OnStartRequest).
114   //    Notice the alternative data loading could finish before the cacheID
115   //    checking, so the statust transition could be
116   //    LOADING->COMPLETED->CANCELED
117   //
118   // 3. LOADING->FALLBACK
119   //    For the case that alternative data loading could not be initialized,
120   //    i.e. alternative data does not exist or no preferred alternative data
121   //    type is requested. Once the status becomes FALLBACK,
122   //    AlternativeDataStreamListener transits the channel callback request to
123   //    FetchDriver, and the status should not go back to LOADING, COMPLETED, or
124   //    CANCELED anymore.
125   enum eStatus { LOADING = 0, COMPLETED, CANCELED, FALLBACK };
126 
127   AlternativeDataStreamListener(FetchDriver* aFetchDriver, nsIChannel* aChannel,
128                                 const nsACString& aAlternativeDataType);
129   eStatus Status();
130   void Cancel();
131   uint64_t GetAlternativeDataCacheEntryId();
132   const nsACString& GetAlternativeDataType() const;
133   already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel();
134   already_AddRefed<nsIInputStream> GetAlternativeInputStream();
135 
136  private:
137   ~AlternativeDataStreamListener() = default;
138 
139   // This creates a strong reference cycle with FetchDriver and its
140   // mAltDataListener. We need to clear at least one reference of them once the
141   // data loading finishes.
142   RefPtr<FetchDriver> mFetchDriver;
143   nsCString mAlternativeDataType;
144   nsCOMPtr<nsIInputStream> mPipeAlternativeInputStream;
145   nsCOMPtr<nsIOutputStream> mPipeAlternativeOutputStream;
146   uint64_t mAlternativeDataCacheEntryId;
147   nsCOMPtr<nsICacheInfoChannel> mCacheInfoChannel;
148   nsCOMPtr<nsIChannel> mChannel;
149   Atomic<eStatus> mStatus;
150 };
151 
NS_IMPL_ISUPPORTS(AlternativeDataStreamListener,nsIStreamListener,nsIThreadRetargetableStreamListener)152 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener, nsIStreamListener,
153                   nsIThreadRetargetableStreamListener)
154 
155 AlternativeDataStreamListener::AlternativeDataStreamListener(
156     FetchDriver* aFetchDriver, nsIChannel* aChannel,
157     const nsACString& aAlternativeDataType)
158     : mFetchDriver(aFetchDriver),
159       mAlternativeDataType(aAlternativeDataType),
160       mAlternativeDataCacheEntryId(0),
161       mChannel(aChannel),
162       mStatus(AlternativeDataStreamListener::LOADING) {
163   MOZ_DIAGNOSTIC_ASSERT(mFetchDriver);
164   MOZ_DIAGNOSTIC_ASSERT(mChannel);
165 }
166 
Status()167 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() {
168   return mStatus;
169 }
170 
Cancel()171 void AlternativeDataStreamListener::Cancel() {
172   mAlternativeDataCacheEntryId = 0;
173   mCacheInfoChannel = nullptr;
174   mPipeAlternativeOutputStream = nullptr;
175   mPipeAlternativeInputStream = nullptr;
176   if (mChannel && mStatus != AlternativeDataStreamListener::FALLBACK) {
177     // if mStatus is fallback, we need to keep channel to forward request back
178     // to FetchDriver
179     mChannel->Cancel(NS_BINDING_ABORTED);
180     mChannel = nullptr;
181   }
182   mStatus = AlternativeDataStreamListener::CANCELED;
183 }
184 
GetAlternativeDataCacheEntryId()185 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
186   return mAlternativeDataCacheEntryId;
187 }
188 
GetAlternativeDataType() const189 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType()
190     const {
191   return mAlternativeDataType;
192 }
193 
194 already_AddRefed<nsIInputStream>
GetAlternativeInputStream()195 AlternativeDataStreamListener::GetAlternativeInputStream() {
196   nsCOMPtr<nsIInputStream> inputStream = mPipeAlternativeInputStream;
197   return inputStream.forget();
198 }
199 
200 already_AddRefed<nsICacheInfoChannel>
GetCacheInfoChannel()201 AlternativeDataStreamListener::GetCacheInfoChannel() {
202   nsCOMPtr<nsICacheInfoChannel> channel = mCacheInfoChannel;
203   return channel.forget();
204 }
205 
206 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)207 AlternativeDataStreamListener::OnStartRequest(nsIRequest* aRequest) {
208   AssertIsOnMainThread();
209   MOZ_ASSERT(!mAlternativeDataType.IsEmpty());
210   // Checking the alternative data type is the same between we asked and the
211   // saved in the channel.
212   nsAutoCString alternativeDataType;
213   nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
214   mStatus = AlternativeDataStreamListener::LOADING;
215   if (cic && NS_SUCCEEDED(cic->GetAlternativeDataType(alternativeDataType)) &&
216       mAlternativeDataType.Equals(alternativeDataType) &&
217       NS_SUCCEEDED(cic->GetCacheEntryId(&mAlternativeDataCacheEntryId))) {
218     MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream);
219     MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream);
220     nsresult rv =
221         NS_NewPipe(getter_AddRefs(mPipeAlternativeInputStream),
222                    getter_AddRefs(mPipeAlternativeOutputStream),
223                    0 /* default segment size */, UINT32_MAX /* infinite pipe */,
224                    true /* non-blocking input, otherwise you deadlock */,
225                    false /* blocking output, since the pipe is 'in'finite */);
226 
227     if (NS_FAILED(rv)) {
228       mFetchDriver->FailWithNetworkError(rv);
229       return rv;
230     }
231 
232     MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel);
233     mCacheInfoChannel = cic;
234 
235     // call FetchDriver::HttpFetch to load main body
236     MOZ_ASSERT(mFetchDriver);
237     return mFetchDriver->HttpFetch();
238 
239   } else {
240     // Needn't load alternative data, since alternative data does not exist.
241     // Set status to FALLBACK to reuse the opened channel to load main body,
242     // then call FetchDriver::OnStartRequest to continue the work. Unfortunately
243     // can't change the stream listener to mFetchDriver, need to keep
244     // AlternativeDataStreamListener alive to redirect OnDataAvailable and
245     // OnStopRequest to mFetchDriver.
246     MOZ_ASSERT(alternativeDataType.IsEmpty());
247     mStatus = AlternativeDataStreamListener::FALLBACK;
248     mAlternativeDataCacheEntryId = 0;
249     MOZ_ASSERT(mFetchDriver);
250     return mFetchDriver->OnStartRequest(aRequest);
251   }
252   return NS_OK;
253 }
254 
255 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)256 AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest,
257                                                nsIInputStream* aInputStream,
258                                                uint64_t aOffset,
259                                                uint32_t aCount) {
260   if (mStatus == AlternativeDataStreamListener::LOADING) {
261     MOZ_ASSERT(mPipeAlternativeOutputStream);
262     uint32_t read = 0;
263     return aInputStream->ReadSegments(
264         NS_CopySegmentToStream, mPipeAlternativeOutputStream, aCount, &read);
265   }
266   if (mStatus == AlternativeDataStreamListener::FALLBACK) {
267     MOZ_ASSERT(mFetchDriver);
268     return mFetchDriver->OnDataAvailable(aRequest, aInputStream, aOffset,
269                                          aCount);
270   }
271   return NS_OK;
272 }
273 
274 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)275 AlternativeDataStreamListener::OnStopRequest(nsIRequest* aRequest,
276                                              nsresult aStatusCode) {
277   AssertIsOnMainThread();
278 
279   // Alternative data loading is going to finish, breaking the reference cycle
280   // here by taking the ownership to a loacl variable.
281   RefPtr<FetchDriver> fetchDriver = std::move(mFetchDriver);
282 
283   if (mStatus == AlternativeDataStreamListener::CANCELED) {
284     // do nothing
285     return NS_OK;
286   }
287 
288   if (mStatus == AlternativeDataStreamListener::FALLBACK) {
289     MOZ_ASSERT(fetchDriver);
290     return fetchDriver->OnStopRequest(aRequest, aStatusCode);
291   }
292 
293   MOZ_DIAGNOSTIC_ASSERT(mStatus == AlternativeDataStreamListener::LOADING);
294 
295   MOZ_ASSERT(!mAlternativeDataType.IsEmpty() && mPipeAlternativeOutputStream &&
296              mPipeAlternativeInputStream);
297 
298   mPipeAlternativeOutputStream->Close();
299   mPipeAlternativeOutputStream = nullptr;
300 
301   // Cleanup the states for alternative data if needed.
302   if (NS_FAILED(aStatusCode)) {
303     mAlternativeDataCacheEntryId = 0;
304     mCacheInfoChannel = nullptr;
305     mPipeAlternativeInputStream = nullptr;
306   }
307   mStatus = AlternativeDataStreamListener::COMPLETED;
308   // alternative data loading finish, call FetchDriver::FinishOnStopRequest to
309   // continue the final step for the case FetchDriver::OnStopRequest is called
310   // earlier than AlternativeDataStreamListener::OnStopRequest
311   MOZ_ASSERT(fetchDriver);
312   fetchDriver->FinishOnStopRequest(this);
313   return NS_OK;
314 }
315 
316 NS_IMETHODIMP
CheckListenerChain()317 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
318 //-----------------------------------------------------------------------------
319 // FetchDriver
320 //-----------------------------------------------------------------------------
321 
NS_IMPL_ISUPPORTS(FetchDriver,nsIStreamListener,nsIChannelEventSink,nsIInterfaceRequestor,nsIThreadRetargetableStreamListener)322 NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener, nsIChannelEventSink,
323                   nsIInterfaceRequestor, nsIThreadRetargetableStreamListener)
324 
325 FetchDriver::FetchDriver(SafeRefPtr<InternalRequest> aRequest,
326                          nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup,
327                          nsIEventTarget* aMainThreadEventTarget,
328                          nsICookieJarSettings* aCookieJarSettings,
329                          PerformanceStorage* aPerformanceStorage,
330                          bool aIsTrackingFetch)
331     : mPrincipal(aPrincipal),
332       mLoadGroup(aLoadGroup),
333       mRequest(std::move(aRequest)),
334       mMainThreadEventTarget(aMainThreadEventTarget),
335       mCookieJarSettings(aCookieJarSettings),
336       mPerformanceStorage(aPerformanceStorage),
337       mNeedToObserveOnDataAvailable(false),
338       mIsTrackingFetch(aIsTrackingFetch),
339       mOnStopRequestCalled(false)
340 #ifdef DEBUG
341       ,
342       mResponseAvailableCalled(false),
343       mFetchCalled(false)
344 #endif
345 {
346   AssertIsOnMainThread();
347 
348   MOZ_ASSERT(mRequest);
349   MOZ_ASSERT(aPrincipal);
350   MOZ_ASSERT(aMainThreadEventTarget);
351 }
352 
~FetchDriver()353 FetchDriver::~FetchDriver() {
354   AssertIsOnMainThread();
355 
356   // We assert this since even on failures, we should call
357   // FailWithNetworkError().
358   MOZ_ASSERT(mResponseAvailableCalled);
359 }
360 
FindPreload(nsIURI * aURI)361 already_AddRefed<PreloaderBase> FetchDriver::FindPreload(nsIURI* aURI) {
362   // Decide if we allow reuse of an existing <link rel=preload as=fetch>
363   // response for this request.  First examine this fetch requets itself if it
364   // is 'pure' enough to use the response and then try to find a preload.
365 
366   if (!mDocument) {
367     // Preloads are mapped on the document, no document, no preload.
368     return nullptr;
369   }
370   CORSMode cors;
371   switch (mRequest->Mode()) {
372     case RequestMode::No_cors:
373       cors = CORSMode::CORS_NONE;
374       break;
375     case RequestMode::Cors:
376       cors = mRequest->GetCredentialsMode() == RequestCredentials::Include
377                  ? CORSMode::CORS_USE_CREDENTIALS
378                  : CORSMode::CORS_ANONYMOUS;
379       break;
380     default:
381       // Can't be satisfied by a preload because preload cannot define any of
382       // remaining modes.
383       return nullptr;
384   }
385   if (!mRequest->Headers()->HasOnlySimpleHeaders()) {
386     // Preload can't set any headers.
387     return nullptr;
388   }
389   if (!mRequest->GetIntegrity().IsEmpty()) {
390     // There is currently no support for SRI checking in the fetch preloader.
391     return nullptr;
392   }
393   if (mRequest->GetCacheMode() != RequestCache::Default) {
394     // Preload can only go with the default caching mode.
395     return nullptr;
396   }
397   if (mRequest->SkipServiceWorker()) {
398     // Preload can't be forbidden interception.
399     return nullptr;
400   }
401   if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
402     // Preload always follows redirects.
403     return nullptr;
404   }
405   nsAutoCString method;
406   mRequest->GetMethod(method);
407   if (!method.EqualsLiteral("GET")) {
408     // Preload can only do GET, this also eliminates the case we do upload, so
409     // no need to check if the request has any body to send out.
410     return nullptr;
411   }
412 
413   // OK, this request can be satisfied by a preloaded response, try to find one.
414 
415   auto preloadKey = PreloadHashKey::CreateAsFetch(aURI, cors);
416   return mDocument->Preloads().LookupPreload(preloadKey);
417 }
418 
UpdateReferrerInfoFromNewChannel(nsIChannel * aChannel)419 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel* aChannel) {
420   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
421   if (!httpChannel) {
422     return;
423   }
424 
425   nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
426   if (!referrerInfo) {
427     return;
428   }
429 
430   nsAutoString computedReferrerSpec;
431   mRequest->SetReferrerPolicy(referrerInfo->ReferrerPolicy());
432   Unused << referrerInfo->GetComputedReferrerSpec(computedReferrerSpec);
433   mRequest->SetReferrer(computedReferrerSpec);
434 }
435 
Fetch(AbortSignalImpl * aSignalImpl,FetchDriverObserver * aObserver)436 nsresult FetchDriver::Fetch(AbortSignalImpl* aSignalImpl,
437                             FetchDriverObserver* aObserver) {
438   AssertIsOnMainThread();
439 #ifdef DEBUG
440   MOZ_ASSERT(!mFetchCalled);
441   mFetchCalled = true;
442 #endif
443 
444   mObserver = aObserver;
445 
446   // FIXME(nsm): Deal with HSTS.
447 
448   MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
449                      "Synchronous fetch not supported");
450 
451   UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(
452       new mozilla::ipc::PrincipalInfo());
453   nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
454   if (NS_WARN_IF(NS_FAILED(rv))) {
455     return rv;
456   }
457 
458   mRequest->SetPrincipalInfo(std::move(principalInfo));
459 
460   // If the signal is aborted, it's time to inform the observer and terminate
461   // the operation.
462   if (aSignalImpl) {
463     if (aSignalImpl->Aborted()) {
464       RunAbortAlgorithm();
465       return NS_OK;
466     }
467 
468     Follow(aSignalImpl);
469   }
470 
471   rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
472   if (NS_FAILED(rv)) {
473     FailWithNetworkError(rv);
474   }
475 
476   // Any failure is handled by FailWithNetworkError notifying the aObserver.
477   return NS_OK;
478 }
479 
480 // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
481 // Functionality is often split between here, the CORS listener proxy and the
482 // Necko HTTP implementation.
HttpFetch(const nsACString & aPreferredAlternativeDataType)483 nsresult FetchDriver::HttpFetch(
484     const nsACString& aPreferredAlternativeDataType) {
485   MOZ_ASSERT(NS_IsMainThread());
486 
487   // Step 1. "Let response be null."
488   mResponse = nullptr;
489   mOnStopRequestCalled = false;
490   nsresult rv;
491 
492   nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
493   NS_ENSURE_SUCCESS(rv, rv);
494 
495   nsAutoCString url;
496   mRequest->GetURL(url);
497   nsCOMPtr<nsIURI> uri;
498   rv = NS_NewURI(getter_AddRefs(uri), url);
499   NS_ENSURE_SUCCESS(rv, rv);
500 
501   // Unsafe requests aren't allowed with when using no-core mode.
502   if (mRequest->Mode() == RequestMode::No_cors && mRequest->UnsafeRequest() &&
503       (!mRequest->HasSimpleMethod() ||
504        !mRequest->Headers()->HasOnlySimpleHeaders())) {
505     MOZ_ASSERT(false, "The API should have caught this");
506     return NS_ERROR_DOM_BAD_URI;
507   }
508 
509   // non-GET requests aren't allowed for blob.
510   if (IsBlobURI(uri)) {
511     nsAutoCString method;
512     mRequest->GetMethod(method);
513     if (!method.EqualsLiteral("GET")) {
514       return NS_ERROR_DOM_NETWORK_ERR;
515     }
516   }
517 
518   RefPtr<PreloaderBase> fetchPreload = FindPreload(uri);
519   if (fetchPreload) {
520     fetchPreload->RemoveSelf(mDocument);
521     fetchPreload->NotifyUsage(PreloaderBase::LoadBackground::Keep);
522 
523     rv = fetchPreload->AsyncConsume(this);
524     if (NS_SUCCEEDED(rv)) {
525       mFromPreload = true;
526 
527       mChannel = fetchPreload->Channel();
528       MOZ_ASSERT(mChannel);
529       mChannel->SetNotificationCallbacks(this);
530 
531       // Copied from AsyncOnChannelRedirect.
532       for (const auto& redirect : fetchPreload->Redirects()) {
533         if (redirect.Flags() & nsIChannelEventSink::REDIRECT_INTERNAL) {
534           mRequest->SetURLForInternalRedirect(redirect.Flags(), redirect.Spec(),
535                                               redirect.Fragment());
536         } else {
537           mRequest->AddURL(redirect.Spec(), redirect.Fragment());
538         }
539       }
540 
541       return NS_OK;
542     }
543 
544     // The preload failed to be consumed.  Behave like there were no preload.
545     fetchPreload = nullptr;
546   }
547 
548   // Step 2 deals with letting ServiceWorkers intercept requests. This is
549   // handled by Necko after the channel is opened.
550   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
551   // set based on the Request's flag.
552 
553   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
554   // true..." is handled by the CORS proxy.
555   //
556   // Step 3.2 "Set request's skip service worker flag." This isn't required
557   // since Necko will fall back to the network if the ServiceWorker does not
558   // respond with a valid Response.
559   //
560   // NS_StartCORSPreflight() will automatically kick off the original request
561   // if it succeeds, so we need to have everything setup for the original
562   // request too.
563 
564   // Step 3.3 "Let credentials flag be set if one of
565   //  - request's credentials mode is "include"
566   //  - request's credentials mode is "same-origin" and either the CORS flag
567   //    is unset or response tainting is "opaque"
568   // is true, and unset otherwise."
569 
570   // Set skip serviceworker flag.
571   // While the spec also gates on the client being a ServiceWorker, we can't
572   // infer that here. Instead we rely on callers to set the flag correctly.
573   const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker()
574                                      ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER
575                                      : 0;
576 
577   nsSecurityFlags secFlags = 0;
578   if (mRequest->Mode() == RequestMode::Cors) {
579     secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
580   } else if (mRequest->Mode() == RequestMode::Same_origin ||
581              mRequest->Mode() == RequestMode::Navigate) {
582     secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT;
583   } else if (mRequest->Mode() == RequestMode::No_cors) {
584     secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
585   } else {
586     MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
587     return NS_ERROR_UNEXPECTED;
588   }
589 
590   if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
591     secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
592   }
593 
594   // This is handles the use credentials flag in "HTTP
595   // network or cache fetch" in the spec and decides whether to transmit
596   // cookies and other identifying information.
597   if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
598     secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
599   } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
600     secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
601   } else if (mRequest->GetCredentialsMode() ==
602              RequestCredentials::Same_origin) {
603     secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
604   } else {
605     MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
606     return NS_ERROR_UNEXPECTED;
607   }
608 
609   // From here on we create a channel and set its properties with the
610   // information from the InternalRequest. This is an implementation detail.
611   MOZ_ASSERT(mLoadGroup);
612   nsCOMPtr<nsIChannel> chan;
613 
614   nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND | bypassFlag;
615   if (mDocument) {
616     MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
617     MOZ_ASSERT(mDocument->CookieJarSettings() == mCookieJarSettings);
618     rv = NS_NewChannel(getter_AddRefs(chan), uri, mDocument, secFlags,
619                        mRequest->ContentPolicyType(),
620                        nullptr,             /* aPerformanceStorage */
621                        mLoadGroup, nullptr, /* aCallbacks */
622                        loadFlags, ios);
623   } else if (mClientInfo.isSome()) {
624     rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, mClientInfo.ref(),
625                        mController, secFlags, mRequest->ContentPolicyType(),
626                        mCookieJarSettings, mPerformanceStorage, mLoadGroup,
627                        nullptr, /* aCallbacks */
628                        loadFlags, ios);
629   } else {
630     rv =
631         NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags,
632                       mRequest->ContentPolicyType(), mCookieJarSettings,
633                       mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */
634                       loadFlags, ios);
635   }
636   NS_ENSURE_SUCCESS(rv, rv);
637 
638   if (mCSPEventListener) {
639     nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
640     rv = loadInfo->SetCspEventListener(mCSPEventListener);
641     NS_ENSURE_SUCCESS(rv, rv);
642   }
643 
644   {
645     nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
646     rv = loadInfo->SetLoadingEmbedderPolicy(mRequest->GetEmbedderPolicy());
647     NS_ENSURE_SUCCESS(rv, rv);
648   }
649 
650   if (mDocument && mDocument->GetEmbedderElement() &&
651       mDocument->GetEmbedderElement()->IsAnyOfHTMLElements(nsGkAtoms::object,
652                                                            nsGkAtoms::embed)) {
653     nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
654     rv = loadInfo->SetIsFromObjectOrEmbed(true);
655     NS_ENSURE_SUCCESS(rv, rv);
656   }
657 
658   // Insert ourselves into the notification callbacks chain so we can set
659   // headers on redirects.
660 #ifdef DEBUG
661   {
662     nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
663     chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
664     MOZ_ASSERT(!notificationCallbacks);
665   }
666 #endif
667   chan->SetNotificationCallbacks(this);
668 
669   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan));
670   // Mark channel as urgent-start if the Fetch is triggered by user input
671   // events.
672   if (cos && UserActivation::IsHandlingUserInput()) {
673     cos->AddClassFlags(nsIClassOfService::UrgentStart);
674   }
675 
676   // Step 3.5 begins "HTTP network or cache fetch".
677   // HTTP network or cache fetch
678   // ---------------------------
679   // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
680   nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
681   if (httpChan) {
682     // Copy the method.
683     nsAutoCString method;
684     mRequest->GetMethod(method);
685     rv = httpChan->SetRequestMethod(method);
686     NS_ENSURE_SUCCESS(rv, rv);
687 
688     // Set the same headers.
689     SetRequestHeaders(httpChan, false);
690 
691     // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
692     // If request's referrer policy is the empty string and request's client is
693     // non-null, then set request's referrer policy to request's client's
694     // associated referrer policy.
695     // Basically, "client" is not in our implementation, we use
696     // EnvironmentReferrerPolicy of the worker or document context
697     ReferrerPolicy referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
698     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
699       mRequest->SetReferrerPolicy(referrerPolicy);
700     }
701     // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
702     // If request’s referrer policy is the empty string,
703     // then set request’s referrer policy to the user-set default policy.
704     if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
705       nsCOMPtr<nsILoadInfo> loadInfo = httpChan->LoadInfo();
706       bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
707       referrerPolicy =
708           ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate);
709       mRequest->SetReferrerPolicy(referrerPolicy);
710     }
711 
712     rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
713                                        *mRequest);
714     NS_ENSURE_SUCCESS(rv, rv);
715 
716     // Bug 1120722 - Authorization will be handled later.
717     // Auth may require prompting, we don't support it yet.
718     // The next patch in this same bug prevents this from aborting the request.
719     // Credentials checks for CORS are handled by nsCORSListenerProxy,
720 
721     nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
722 
723     // Conversion between enumerations is safe due to static asserts in
724     // dom/workers/ServiceWorkerManager.cpp
725     rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
726     MOZ_ASSERT(NS_SUCCEEDED(rv));
727     rv = internalChan->SetRedirectMode(
728         static_cast<uint32_t>(mRequest->GetRedirectMode()));
729     MOZ_ASSERT(NS_SUCCEEDED(rv));
730     mRequest->MaybeSkipCacheIfPerformingRevalidation();
731     rv = internalChan->SetFetchCacheMode(
732         static_cast<uint32_t>(mRequest->GetCacheMode()));
733     MOZ_ASSERT(NS_SUCCEEDED(rv));
734     rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
735     MOZ_ASSERT(NS_SUCCEEDED(rv));
736 
737     // Set the initiator type
738     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
739     if (timedChannel) {
740       timedChannel->SetInitiatorType(u"fetch"_ns);
741     }
742   }
743 
744   // Step 5. Proxy authentication will be handled by Necko.
745 
746   // Continue setting up 'HTTPRequest'. Content-Type and body data.
747   nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
748   if (uploadChan) {
749     nsAutoCString contentType;
750     ErrorResult result;
751     mRequest->Headers()->GetFirst("content-type"_ns, contentType, result);
752     // We don't actually expect "result" to have failed here: that only happens
753     // for invalid header names.  But if for some reason it did, just propagate
754     // it out.
755     if (result.Failed()) {
756       return result.StealNSResult();
757     }
758 
759     // Now contentType is the header that was set in mRequest->Headers(), or a
760     // void string if no header was set.
761 #ifdef DEBUG
762     bool hasContentTypeHeader =
763         mRequest->Headers()->Has("content-type"_ns, result);
764     MOZ_ASSERT(!result.Failed());
765     MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
766 #endif  // DEBUG
767 
768     int64_t bodyLength;
769     nsCOMPtr<nsIInputStream> bodyStream;
770     mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
771     if (bodyStream) {
772       nsAutoCString method;
773       mRequest->GetMethod(method);
774       rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
775                                                bodyLength, method,
776                                                false /* aStreamHasHeaders */);
777       NS_ENSURE_SUCCESS(rv, rv);
778     }
779   }
780 
781   // If preflight is required, start a "CORS preflight fetch"
782   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
783   // implementation is handled by the http channel calling into
784   // nsCORSListenerProxy. We just inform it which unsafe headers are included
785   // in the request.
786   if (mRequest->Mode() == RequestMode::Cors) {
787     AutoTArray<nsCString, 5> unsafeHeaders;
788     mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
789     nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
790     loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
791   }
792 
793   if (mIsTrackingFetch && StaticPrefs::network_http_tailing_enabled() && cos) {
794     cos->AddClassFlags(nsIClassOfService::Throttleable |
795                        nsIClassOfService::Tail);
796   }
797 
798   if (mIsTrackingFetch &&
799       StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
800     nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
801     if (p) {
802       p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
803     }
804   }
805 
806   NotifyNetworkMonitorAlternateStack(chan, std::move(mOriginStack));
807 
808   // if the preferred alternative data type in InternalRequest is not empty, set
809   // the data type on the created channel and also create a
810   // AlternativeDataStreamListener to be the stream listener of the channel.
811   if (!aPreferredAlternativeDataType.IsEmpty()) {
812     nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
813     if (cic) {
814       cic->PreferAlternativeDataType(aPreferredAlternativeDataType, ""_ns,
815                                      true);
816       MOZ_ASSERT(!mAltDataListener);
817       mAltDataListener = new AlternativeDataStreamListener(
818           this, chan, aPreferredAlternativeDataType);
819       rv = chan->AsyncOpen(mAltDataListener);
820     } else {
821       rv = chan->AsyncOpen(this);
822     }
823   } else {
824     // Integrity check cannot be done on alt-data yet.
825     if (mRequest->GetIntegrity().IsEmpty()) {
826       nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
827       if (cic) {
828         cic->PreferAlternativeDataType(nsLiteralCString(WASM_ALT_DATA_TYPE_V1),
829                                        nsLiteralCString(WASM_CONTENT_TYPE),
830                                        false);
831       }
832     }
833 
834     rv = chan->AsyncOpen(this);
835   }
836 
837   if (NS_FAILED(rv)) {
838     return rv;
839   }
840 
841   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
842 
843   mChannel = chan;
844   return NS_OK;
845 }
BeginAndGetFilteredResponse(InternalResponse * aResponse,bool aFoundOpaqueRedirect)846 already_AddRefed<InternalResponse> FetchDriver::BeginAndGetFilteredResponse(
847     InternalResponse* aResponse, bool aFoundOpaqueRedirect) {
848   MOZ_ASSERT(aResponse);
849   AutoTArray<nsCString, 4> reqURLList;
850   mRequest->GetURLListWithoutFragment(reqURLList);
851   MOZ_ASSERT(!reqURLList.IsEmpty());
852   aResponse->SetURLList(reqURLList);
853   RefPtr<InternalResponse> filteredResponse;
854   if (aFoundOpaqueRedirect) {
855     filteredResponse = aResponse->OpaqueRedirectResponse();
856   } else {
857     switch (mRequest->GetResponseTainting()) {
858       case LoadTainting::Basic:
859         filteredResponse = aResponse->BasicResponse();
860         break;
861       case LoadTainting::CORS:
862         filteredResponse = aResponse->CORSResponse();
863         break;
864       case LoadTainting::Opaque: {
865         filteredResponse = aResponse->OpaqueResponse();
866         nsresult rv = filteredResponse->GeneratePaddingInfo();
867         if (NS_WARN_IF(NS_FAILED(rv))) {
868           return nullptr;
869         }
870         break;
871       }
872       default:
873         MOZ_CRASH("Unexpected case");
874     }
875   }
876 
877   MOZ_ASSERT(filteredResponse);
878   MOZ_ASSERT(mObserver);
879   if (!ShouldCheckSRI(*mRequest, *filteredResponse)) {
880     // Need to keep mObserver alive.
881     RefPtr<FetchDriverObserver> observer = mObserver;
882     observer->OnResponseAvailable(filteredResponse);
883 #ifdef DEBUG
884     mResponseAvailableCalled = true;
885 #endif
886   }
887 
888   return filteredResponse.forget();
889 }
890 
FailWithNetworkError(nsresult rv)891 void FetchDriver::FailWithNetworkError(nsresult rv) {
892   AssertIsOnMainThread();
893   RefPtr<InternalResponse> error = InternalResponse::NetworkError(rv);
894   if (mObserver) {
895     // Need to keep mObserver alive.
896     RefPtr<FetchDriverObserver> observer = mObserver;
897     observer->OnResponseAvailable(error);
898 #ifdef DEBUG
899     mResponseAvailableCalled = true;
900 #endif
901   }
902 
903   // mObserver could be null after OnResponseAvailable().
904   if (mObserver) {
905     mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
906     mObserver = nullptr;
907   }
908 
909   mChannel = nullptr;
910 }
911 
912 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)913 FetchDriver::OnStartRequest(nsIRequest* aRequest) {
914   AssertIsOnMainThread();
915 
916   // Note, this can be called multiple times if we are doing an opaqueredirect.
917   // In that case we will get a simulated OnStartRequest() and then the real
918   // channel will call in with an errored OnStartRequest().
919 
920   if (mFromPreload && mAborted) {
921     aRequest->Cancel(NS_BINDING_ABORTED);
922     return NS_BINDING_ABORTED;
923   }
924 
925   if (!mChannel) {
926     MOZ_ASSERT(!mObserver);
927     return NS_BINDING_ABORTED;
928   }
929 
930   nsresult rv;
931   aRequest->GetStatus(&rv);
932   if (NS_FAILED(rv)) {
933     FailWithNetworkError(rv);
934     return rv;
935   }
936 
937   // We should only get to the following code once.
938   MOZ_ASSERT(!mPipeOutputStream);
939 
940   if (!mObserver) {
941     MOZ_ASSERT(false, "We should have mObserver here.");
942     FailWithNetworkError(NS_ERROR_UNEXPECTED);
943     return NS_ERROR_UNEXPECTED;
944   }
945 
946   mNeedToObserveOnDataAvailable = mObserver->NeedOnDataAvailable();
947 
948   RefPtr<InternalResponse> response;
949   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
950   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
951 
952   // On a successful redirect we perform the following substeps of HTTP Fetch,
953   // step 5, "redirect status", step 11.
954 
955   bool foundOpaqueRedirect = false;
956 
957   nsAutoCString contentType;
958   channel->GetContentType(contentType);
959 
960   int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
961   rv = channel->GetContentLength(&contentLength);
962   MOZ_ASSERT_IF(NS_FAILED(rv),
963                 contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
964 
965   if (httpChannel) {
966     uint32_t responseStatus = 0;
967     rv = httpChannel->GetResponseStatus(&responseStatus);
968     if (NS_FAILED(rv)) {
969       FailWithNetworkError(rv);
970       return rv;
971     }
972 
973     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
974       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
975         FailWithNetworkError(NS_BINDING_ABORTED);
976         return NS_BINDING_FAILED;
977       }
978       if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
979         foundOpaqueRedirect = true;
980       }
981     }
982 
983     nsAutoCString statusText;
984     rv = httpChannel->GetResponseStatusText(statusText);
985     MOZ_ASSERT(NS_SUCCEEDED(rv));
986 
987     response = new InternalResponse(responseStatus, statusText,
988                                     mRequest->GetCredentialsMode());
989 
990     UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(
991         new mozilla::ipc::PrincipalInfo());
992     nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
993     if (NS_WARN_IF(NS_FAILED(rv))) {
994       return rv;
995     }
996 
997     response->SetPrincipalInfo(std::move(principalInfo));
998 
999     response->Headers()->FillResponseHeaders(httpChannel);
1000 
1001     // If Content-Encoding or Transfer-Encoding headers are set, then the actual
1002     // Content-Length (which refer to the decoded data) is obscured behind the
1003     // encodings.
1004     ErrorResult result;
1005     if (response->Headers()->Has("content-encoding"_ns, result) ||
1006         response->Headers()->Has("transfer-encoding"_ns, result)) {
1007       // We cannot trust the content-length when content-encoding or
1008       // transfer-encoding are set.  There are many servers which just
1009       // get this wrong.
1010       contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
1011     }
1012     MOZ_ASSERT(!result.Failed());
1013   } else {
1014     response =
1015         new InternalResponse(200, "OK"_ns, mRequest->GetCredentialsMode());
1016 
1017     if (!contentType.IsEmpty()) {
1018       nsAutoCString contentCharset;
1019       channel->GetContentCharset(contentCharset);
1020       if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
1021         contentType += ";charset="_ns + contentCharset;
1022       }
1023 
1024       IgnoredErrorResult result;
1025       response->Headers()->Append("Content-Type"_ns, contentType, result);
1026       MOZ_ASSERT(!result.Failed());
1027     }
1028 
1029     if (contentLength > 0) {
1030       nsAutoCString contentLenStr;
1031       contentLenStr.AppendInt(contentLength);
1032 
1033       IgnoredErrorResult result;
1034       response->Headers()->Append("Content-Length"_ns, contentLenStr, result);
1035       MOZ_ASSERT(!result.Failed());
1036     }
1037   }
1038 
1039   nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
1040   if (cic) {
1041     if (mAltDataListener) {
1042       // Skip the case that mAltDataListener->Status() equals to FALLBACK, that
1043       // means the opened channel for alternative data loading is reused for
1044       // loading the main data.
1045       if (mAltDataListener->Status() !=
1046           AlternativeDataStreamListener::FALLBACK) {
1047         // Verify the cache ID is the same with from alternative data cache.
1048         // If the cache ID is different, droping the alternative data loading,
1049         // otherwise setup the response's alternative body and cacheInfoChannel.
1050         uint64_t cacheEntryId = 0;
1051         if (NS_SUCCEEDED(cic->GetCacheEntryId(&cacheEntryId)) &&
1052             cacheEntryId !=
1053                 mAltDataListener->GetAlternativeDataCacheEntryId()) {
1054           mAltDataListener->Cancel();
1055         } else {
1056           // AlternativeDataStreamListener::OnStartRequest had already been
1057           // called, the alternative data input stream and cacheInfo channel
1058           // must be created.
1059           nsCOMPtr<nsICacheInfoChannel> cacheInfo =
1060               mAltDataListener->GetCacheInfoChannel();
1061           nsCOMPtr<nsIInputStream> altInputStream =
1062               mAltDataListener->GetAlternativeInputStream();
1063           MOZ_ASSERT(altInputStream && cacheInfo);
1064           response->SetAlternativeBody(altInputStream);
1065           nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1066               new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1067                   "nsICacheInfoChannel", cacheInfo, false));
1068           response->SetCacheInfoChannel(handle);
1069         }
1070       } else if (!mAltDataListener->GetAlternativeDataType().IsEmpty()) {
1071         // If the status is FALLBACK and the
1072         // mAltDataListener::mAlternativeDataType is not empty, that means the
1073         // data need to be saved into cache, setup the response's
1074         // nsICacheInfoChannel for caching the data after loading.
1075         nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1076             new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1077                 "nsICacheInfoChannel", cic, false));
1078         response->SetCacheInfoChannel(handle);
1079       }
1080     } else if (!cic->PreferredAlternativeDataTypes().IsEmpty()) {
1081       MOZ_ASSERT(cic->PreferredAlternativeDataTypes().Length() == 1);
1082       MOZ_ASSERT(cic->PreferredAlternativeDataTypes()[0].type().EqualsLiteral(
1083           WASM_ALT_DATA_TYPE_V1));
1084       MOZ_ASSERT(
1085           cic->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral(
1086               WASM_CONTENT_TYPE));
1087 
1088       if (contentType.EqualsLiteral(WASM_CONTENT_TYPE)) {
1089         // We want to attach the CacheInfoChannel to the response object such
1090         // that we can track its origin when the Response object is manipulated
1091         // by JavaScript code. This is important for WebAssembly, which uses
1092         // fetch to query its sources in JavaScript and transfer the Response
1093         // object to other function responsible for storing the alternate data
1094         // using the CacheInfoChannel.
1095         nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1096             new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1097                 "nsICacheInfoChannel", cic, false));
1098         response->SetCacheInfoChannel(handle);
1099       }
1100     }
1101   }
1102 
1103   // We open a pipe so that we can immediately set the pipe's read end as the
1104   // response's body. Setting the segment size to UINT32_MAX means that the
1105   // pipe has infinite space. The nsIChannel will continue to buffer data in
1106   // xpcom events even if we block on a fixed size pipe.  It might be possible
1107   // to suspend the channel and then resume when there is space available, but
1108   // for now use an infinite pipe to avoid blocking.
1109   nsCOMPtr<nsIInputStream> pipeInputStream;
1110   rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
1111                   getter_AddRefs(mPipeOutputStream),
1112                   0, /* default segment size */
1113                   UINT32_MAX /* infinite pipe */,
1114                   true /* non-blocking input, otherwise you deadlock */,
1115                   false /* blocking output, since the pipe is 'in'finite */);
1116   if (NS_WARN_IF(NS_FAILED(rv))) {
1117     FailWithNetworkError(rv);
1118     // Cancel request.
1119     return rv;
1120   }
1121   response->SetBody(pipeInputStream, contentLength);
1122 
1123   // If the request is a file channel, then remember the local path to
1124   // that file so we can later create File blobs rather than plain ones.
1125   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
1126   if (fc) {
1127     nsCOMPtr<nsIFile> file;
1128     rv = fc->GetFile(getter_AddRefs(file));
1129     if (!NS_WARN_IF(NS_FAILED(rv))) {
1130       nsAutoString path;
1131       file->GetPath(path);
1132       response->SetBodyLocalPath(path);
1133     }
1134   } else {
1135     // If the request is a blob URI, then remember that URI so that we
1136     // can later just use that blob instance instead of cloning it.
1137     nsCString blobURISpec;
1138     GetBlobURISpecFromChannel(aRequest, blobURISpec);
1139     if (!blobURISpec.IsVoid()) {
1140       response->SetBodyBlobURISpec(blobURISpec);
1141     }
1142   }
1143 
1144   response->InitChannelInfo(channel);
1145 
1146   nsCOMPtr<nsIURI> channelURI;
1147   rv = channel->GetURI(getter_AddRefs(channelURI));
1148   if (NS_WARN_IF(NS_FAILED(rv))) {
1149     FailWithNetworkError(rv);
1150     // Cancel request.
1151     return rv;
1152   }
1153 
1154   nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1155   // Propagate any tainting from the channel back to our response here.  This
1156   // step is not reflected in the spec because the spec is written such that
1157   // FetchEvent.respondWith() just passes the already-tainted Response back to
1158   // the outer fetch().  In gecko, however, we serialize the Response through
1159   // the channel and must regenerate the tainting from the channel in the
1160   // interception case.
1161   mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
1162 
1163   // Resolves fetch() promise which may trigger code running in a worker.  Make
1164   // sure the Response is fully initialized before calling this.
1165   mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
1166   if (NS_WARN_IF(!mResponse)) {
1167     // Fail to generate a paddingInfo for opaque response.
1168     MOZ_DIAGNOSTIC_ASSERT(mRequest->GetResponseTainting() ==
1169                               LoadTainting::Opaque &&
1170                           !foundOpaqueRedirect);
1171     FailWithNetworkError(NS_ERROR_UNEXPECTED);
1172     return NS_ERROR_UNEXPECTED;
1173   }
1174 
1175   // From "Main Fetch" step 19: SRI-part1.
1176   if (ShouldCheckSRI(*mRequest, *mResponse) && mSRIMetadata.IsEmpty()) {
1177     nsIConsoleReportCollector* reporter = nullptr;
1178     if (mObserver) {
1179       reporter = mObserver->GetReporter();
1180     }
1181 
1182     nsAutoCString sourceUri;
1183     if (mDocument && mDocument->GetDocumentURI()) {
1184       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1185     } else if (!mWorkerScript.IsEmpty()) {
1186       sourceUri.Assign(mWorkerScript);
1187     }
1188     SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri, reporter,
1189                                 &mSRIMetadata);
1190     mSRIDataVerifier =
1191         MakeUnique<SRICheckDataVerifier>(mSRIMetadata, sourceUri, reporter);
1192 
1193     // Do not retarget off main thread when using SRI API.
1194     return NS_OK;
1195   }
1196 
1197   nsCOMPtr<nsIEventTarget> sts =
1198       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1199   if (NS_WARN_IF(NS_FAILED(rv))) {
1200     FailWithNetworkError(rv);
1201     // Cancel request.
1202     return rv;
1203   }
1204 
1205   // Try to retarget off main thread.
1206   if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
1207     Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
1208   }
1209   return NS_OK;
1210 }
1211 
1212 namespace {
1213 
1214 // Runnable to call the observer OnDataAvailable on the main-thread.
1215 class DataAvailableRunnable final : public Runnable {
1216   RefPtr<FetchDriverObserver> mObserver;
1217 
1218  public:
DataAvailableRunnable(FetchDriverObserver * aObserver)1219   explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1220       : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) {
1221     MOZ_ASSERT(aObserver);
1222   }
1223 
1224   NS_IMETHOD
Run()1225   Run() override {
1226     mObserver->OnDataAvailable();
1227     mObserver = nullptr;
1228     return NS_OK;
1229   }
1230 };
1231 
1232 struct SRIVerifierAndOutputHolder {
SRIVerifierAndOutputHoldermozilla::dom::__anoncefc3aba0211::SRIVerifierAndOutputHolder1233   SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1234                              nsIOutputStream* aOutputStream)
1235       : mVerifier(aVerifier), mOutputStream(aOutputStream) {}
1236 
1237   SRICheckDataVerifier* mVerifier;
1238   nsIOutputStream* mOutputStream;
1239 
1240  private:
1241   SRIVerifierAndOutputHolder() = delete;
1242 };
1243 
1244 // Just like NS_CopySegmentToStream, but also sends the data into an
1245 // SRICheckDataVerifier.
CopySegmentToStreamAndSRI(nsIInputStream * aInStr,void * aClosure,const char * aBuffer,uint32_t aOffset,uint32_t aCount,uint32_t * aCountWritten)1246 nsresult CopySegmentToStreamAndSRI(nsIInputStream* aInStr, void* aClosure,
1247                                    const char* aBuffer, uint32_t aOffset,
1248                                    uint32_t aCount, uint32_t* aCountWritten) {
1249   auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure);
1250   MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream,
1251                         "Bogus holder");
1252   nsresult rv = holder->mVerifier->Update(
1253       aCount, reinterpret_cast<const uint8_t*>(aBuffer));
1254   NS_ENSURE_SUCCESS(rv, rv);
1255 
1256   // The rest is just like NS_CopySegmentToStream.
1257   *aCountWritten = 0;
1258   while (aCount) {
1259     uint32_t n = 0;
1260     rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1261     if (NS_FAILED(rv)) {
1262       return rv;
1263     }
1264     aBuffer += n;
1265     aCount -= n;
1266     *aCountWritten += n;
1267   }
1268   return NS_OK;
1269 }
1270 
1271 }  // anonymous namespace
1272 
1273 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)1274 FetchDriver::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1275                              uint64_t aOffset, uint32_t aCount) {
1276   // NB: This can be called on any thread!  But we're guaranteed that it is
1277   // called between OnStartRequest and OnStopRequest, so we don't need to worry
1278   // about races.
1279 
1280   if (mNeedToObserveOnDataAvailable) {
1281     mNeedToObserveOnDataAvailable = false;
1282     if (mObserver) {
1283       // Need to keep mObserver alive.
1284       RefPtr<FetchDriverObserver> observer = mObserver;
1285       if (NS_IsMainThread()) {
1286         observer->OnDataAvailable();
1287       } else {
1288         RefPtr<Runnable> runnable = new DataAvailableRunnable(observer);
1289         nsresult rv = mMainThreadEventTarget->Dispatch(runnable.forget(),
1290                                                        NS_DISPATCH_NORMAL);
1291         if (NS_WARN_IF(NS_FAILED(rv))) {
1292           return rv;
1293         }
1294       }
1295     }
1296   }
1297 
1298   if (!mResponse) {
1299     MOZ_ASSERT(false);
1300     return NS_ERROR_UNEXPECTED;
1301   }
1302 
1303   // Needs to be initialized to 0 because in some cases nsStringInputStream may
1304   // not write to aRead.
1305   uint32_t aRead = 0;
1306   MOZ_ASSERT(mPipeOutputStream);
1307 
1308   // From "Main Fetch" step 19: SRI-part2.
1309   // Note: Avoid checking the hidden opaque body.
1310   nsresult rv;
1311   if (mResponse->Type() != ResponseType::Opaque &&
1312       ShouldCheckSRI(*mRequest, *mResponse)) {
1313     MOZ_ASSERT(mSRIDataVerifier);
1314 
1315     SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(),
1316                                       mPipeOutputStream);
1317     rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount,
1318                                     &aRead);
1319   } else {
1320     rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream,
1321                                     aCount, &aRead);
1322   }
1323 
1324   // If no data was read, it's possible the output stream is closed but the
1325   // ReadSegments call followed its contract of returning NS_OK despite write
1326   // errors.  Unfortunately, nsIOutputStream has an ill-conceived contract when
1327   // taken together with ReadSegments' contract, because the pipe will just
1328   // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1329   // So we must just assume the pipe is broken.
1330   if (aRead == 0 && aCount != 0) {
1331     return NS_BASE_STREAM_CLOSED;
1332   }
1333   return rv;
1334 }
1335 
1336 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)1337 FetchDriver::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1338   AssertIsOnMainThread();
1339 
1340   MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled);
1341   mOnStopRequestCalled = true;
1342 
1343   // main data loading is going to finish, breaking the reference cycle.
1344   RefPtr<AlternativeDataStreamListener> altDataListener =
1345       std::move(mAltDataListener);
1346 
1347   // We need to check mObserver, which is nulled by FailWithNetworkError(),
1348   // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1349   // mResponse will definitely be null so we must not take the else branch.
1350   if (NS_FAILED(aStatusCode) || !mObserver) {
1351     nsCOMPtr<nsIAsyncOutputStream> outputStream =
1352         do_QueryInterface(mPipeOutputStream);
1353     if (outputStream) {
1354       outputStream->CloseWithStatus(NS_FAILED(aStatusCode) ? aStatusCode
1355                                                            : NS_BINDING_FAILED);
1356     }
1357     if (altDataListener) {
1358       altDataListener->Cancel();
1359     }
1360 
1361     // We proceed as usual here, since we've already created a successful
1362     // response from OnStartRequest.
1363   } else {
1364     MOZ_ASSERT(mResponse);
1365     MOZ_ASSERT(!mResponse->IsError());
1366 
1367     // From "Main Fetch" step 19: SRI-part3.
1368     if (ShouldCheckSRI(*mRequest, *mResponse)) {
1369       MOZ_ASSERT(mSRIDataVerifier);
1370 
1371       nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
1372 
1373       nsIConsoleReportCollector* reporter = nullptr;
1374       if (mObserver) {
1375         reporter = mObserver->GetReporter();
1376       }
1377 
1378       nsAutoCString sourceUri;
1379       if (mDocument && mDocument->GetDocumentURI()) {
1380         mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1381       } else if (!mWorkerScript.IsEmpty()) {
1382         sourceUri.Assign(mWorkerScript);
1383       }
1384       nsresult rv =
1385           mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri, reporter);
1386       if (NS_FAILED(rv)) {
1387         if (altDataListener) {
1388           altDataListener->Cancel();
1389         }
1390         FailWithNetworkError(rv);
1391         // Cancel request.
1392         return rv;
1393       }
1394     }
1395 
1396     if (mPipeOutputStream) {
1397       mPipeOutputStream->Close();
1398     }
1399   }
1400 
1401   FinishOnStopRequest(altDataListener);
1402   return NS_OK;
1403 }
1404 
FinishOnStopRequest(AlternativeDataStreamListener * aAltDataListener)1405 void FetchDriver::FinishOnStopRequest(
1406     AlternativeDataStreamListener* aAltDataListener) {
1407   AssertIsOnMainThread();
1408   // OnStopRequest is not called from channel, that means the main data loading
1409   // does not finish yet. Reaching here since alternative data loading finishes.
1410   if (!mOnStopRequestCalled) {
1411     return;
1412   }
1413 
1414   MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener);
1415   // Wait for alternative data loading finish if we needed it.
1416   if (aAltDataListener &&
1417       aAltDataListener->Status() == AlternativeDataStreamListener::LOADING) {
1418     // For LOADING case, channel holds the reference of altDataListener, no need
1419     // to restore it to mAltDataListener.
1420     return;
1421   }
1422 
1423   if (mObserver) {
1424     // From "Main Fetch" step 19.1, 19.2: Process response.
1425     if (ShouldCheckSRI(*mRequest, *mResponse)) {
1426       MOZ_ASSERT(mResponse);
1427       // Need to keep mObserver alive.
1428       RefPtr<FetchDriverObserver> observer = mObserver;
1429       observer->OnResponseAvailable(mResponse);
1430 #ifdef DEBUG
1431       mResponseAvailableCalled = true;
1432 #endif
1433     }
1434   }
1435 
1436   if (mObserver) {
1437     mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
1438     mObserver = nullptr;
1439   }
1440 
1441   mChannel = nullptr;
1442 }
1443 
1444 NS_IMETHODIMP
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aFlags,nsIAsyncVerifyRedirectCallback * aCallback)1445 FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
1446                                     nsIChannel* aNewChannel, uint32_t aFlags,
1447                                     nsIAsyncVerifyRedirectCallback* aCallback) {
1448   nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
1449   nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(aNewChannel);
1450   if (oldHttpChannel && newHttpChannel) {
1451     nsAutoCString method;
1452     mRequest->GetMethod(method);
1453 
1454     // Fetch 4.4.11
1455     bool rewriteToGET = false;
1456     Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
1457                                                            &rewriteToGET);
1458 
1459     SetRequestHeaders(newHttpChannel, rewriteToGET);
1460   }
1461 
1462   // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1463   // However, ignore internal redirects here.  We don't want to flip
1464   // Response.redirected to true if an internal redirect occurs.  These
1465   // should be transparent to script.
1466   nsCOMPtr<nsIURI> uri;
1467   MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
1468 
1469   nsCOMPtr<nsIURI> uriClone;
1470   nsresult rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
1471   if (NS_WARN_IF(NS_FAILED(rv))) {
1472     return rv;
1473   }
1474   nsCString spec;
1475   rv = uriClone->GetSpec(spec);
1476   if (NS_WARN_IF(NS_FAILED(rv))) {
1477     return rv;
1478   }
1479   nsCString fragment;
1480   rv = uri->GetRef(fragment);
1481   if (NS_WARN_IF(NS_FAILED(rv))) {
1482     return rv;
1483   }
1484 
1485   if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1486     mRequest->AddURL(spec, fragment);
1487   } else {
1488     // Overwrite the URL only when the request is redirected by a service
1489     // worker.
1490     mRequest->SetURLForInternalRedirect(aFlags, spec, fragment);
1491   }
1492 
1493   // In redirect, httpChannel already took referrer-policy into account, so
1494   // updates request’s associated referrer policy from channel.
1495   UpdateReferrerInfoFromNewChannel(aNewChannel);
1496 
1497   aCallback->OnRedirectVerifyCallback(NS_OK);
1498   return NS_OK;
1499 }
1500 
1501 NS_IMETHODIMP
CheckListenerChain()1502 FetchDriver::CheckListenerChain() { return NS_OK; }
1503 
1504 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** aResult)1505 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) {
1506   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1507     *aResult = static_cast<nsIChannelEventSink*>(this);
1508     NS_ADDREF_THIS();
1509     return NS_OK;
1510   }
1511   if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1512     *aResult = static_cast<nsIStreamListener*>(this);
1513     NS_ADDREF_THIS();
1514     return NS_OK;
1515   }
1516   if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1517     *aResult = static_cast<nsIRequestObserver*>(this);
1518     NS_ADDREF_THIS();
1519     return NS_OK;
1520   }
1521 
1522   return QueryInterface(aIID, aResult);
1523 }
1524 
SetDocument(Document * aDocument)1525 void FetchDriver::SetDocument(Document* aDocument) {
1526   // Cannot set document after Fetch() has been called.
1527   MOZ_ASSERT(!mFetchCalled);
1528   mDocument = aDocument;
1529 }
1530 
SetCSPEventListener(nsICSPEventListener * aCSPEventListener)1531 void FetchDriver::SetCSPEventListener(nsICSPEventListener* aCSPEventListener) {
1532   MOZ_ASSERT(!mFetchCalled);
1533   mCSPEventListener = aCSPEventListener;
1534 }
1535 
SetClientInfo(const ClientInfo & aClientInfo)1536 void FetchDriver::SetClientInfo(const ClientInfo& aClientInfo) {
1537   MOZ_ASSERT(!mFetchCalled);
1538   mClientInfo.emplace(aClientInfo);
1539 }
1540 
SetController(const Maybe<ServiceWorkerDescriptor> & aController)1541 void FetchDriver::SetController(
1542     const Maybe<ServiceWorkerDescriptor>& aController) {
1543   MOZ_ASSERT(!mFetchCalled);
1544   mController = aController;
1545 }
1546 
SetRequestHeaders(nsIHttpChannel * aChannel,bool aStripRequestBodyHeader) const1547 void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel,
1548                                     bool aStripRequestBodyHeader) const {
1549   MOZ_ASSERT(aChannel);
1550 
1551   // nsIHttpChannel has a set of pre-configured headers (Accept,
1552   // Accept-Languages, ...) and we don't want to merge the Request's headers
1553   // with them. This array is used to know if the current header has been aleady
1554   // set, if yes, we ask necko to merge it with the previous one, otherwise, we
1555   // don't want the merge.
1556   nsTArray<nsCString> headersSet;
1557 
1558   AutoTArray<InternalHeaders::Entry, 5> headers;
1559   mRequest->Headers()->GetEntries(headers);
1560   for (uint32_t i = 0; i < headers.Length(); ++i) {
1561     if (aStripRequestBodyHeader &&
1562         (headers[i].mName.LowerCaseEqualsASCII("content-type") ||
1563          headers[i].mName.LowerCaseEqualsASCII("content-encoding") ||
1564          headers[i].mName.LowerCaseEqualsASCII("content-language") ||
1565          headers[i].mName.LowerCaseEqualsASCII("content-location"))) {
1566       continue;
1567     }
1568 
1569     bool alreadySet = headersSet.Contains(headers[i].mName);
1570     if (!alreadySet) {
1571       headersSet.AppendElement(headers[i].mName);
1572     }
1573 
1574     if (headers[i].mValue.IsEmpty()) {
1575       DebugOnly<nsresult> rv =
1576           aChannel->SetEmptyRequestHeader(headers[i].mName);
1577       MOZ_ASSERT(NS_SUCCEEDED(rv));
1578     } else {
1579       DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
1580           headers[i].mName, headers[i].mValue, alreadySet /* merge */);
1581       MOZ_ASSERT(NS_SUCCEEDED(rv));
1582     }
1583   }
1584 
1585   nsAutoCString method;
1586   mRequest->GetMethod(method);
1587   if (!method.EqualsLiteral("GET") && !method.EqualsLiteral("HEAD")) {
1588     nsAutoString origin;
1589     if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
1590       DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
1591           nsDependentCString(net::nsHttp::Origin),
1592           NS_ConvertUTF16toUTF8(origin), false /* merge */);
1593       MOZ_ASSERT(NS_SUCCEEDED(rv));
1594     }
1595   }
1596 }
1597 
RunAbortAlgorithm()1598 void FetchDriver::RunAbortAlgorithm() {
1599   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
1600 
1601   if (mObserver) {
1602 #ifdef DEBUG
1603     mResponseAvailableCalled = true;
1604 #endif
1605     mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
1606     mObserver = nullptr;
1607   }
1608 
1609   if (mChannel) {
1610     mChannel->Cancel(NS_BINDING_ABORTED);
1611     mChannel = nullptr;
1612   }
1613 
1614   mAborted = true;
1615 }
1616 
1617 }  // namespace mozilla::dom
1618