1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 // HttpLog.h should generally be included first
9 #include "HttpLog.h"
10 
11 #include "nsHttp.h"
12 #include "nsICacheEntry.h"
13 #include "mozilla/BasePrincipal.h"
14 #include "mozilla/Unused.h"
15 #include "mozilla/dom/ContentChild.h"
16 #include "mozilla/dom/DocGroup.h"
17 #include "mozilla/dom/ServiceWorkerUtils.h"
18 #include "mozilla/dom/BrowserChild.h"
19 #include "mozilla/extensions/StreamFilterParent.h"
20 #include "mozilla/ipc/FileDescriptorSetChild.h"
21 #include "mozilla/ipc/IPCStreamUtils.h"
22 #include "mozilla/net/NeckoChild.h"
23 #include "mozilla/net/HttpChannelChild.h"
24 #include "mozilla/net/UrlClassifierCommon.h"
25 #include "mozilla/net/UrlClassifierFeatureFactory.h"
26 
27 #include "AltDataOutputStreamChild.h"
28 #include "CookieServiceChild.h"
29 #include "HttpBackgroundChannelChild.h"
30 #include "nsCOMPtr.h"
31 #include "nsContentPolicyUtils.h"
32 #include "nsDOMNavigationTiming.h"
33 #include "nsGlobalWindow.h"
34 #include "nsStringStream.h"
35 #include "nsHttpChannel.h"
36 #include "nsHttpHandler.h"
37 #include "nsNetUtil.h"
38 #include "nsSerializationHelper.h"
39 #include "mozilla/Attributes.h"
40 #include "mozilla/dom/PerformanceStorage.h"
41 #include "mozilla/ipc/InputStreamUtils.h"
42 #include "mozilla/ipc/URIUtils.h"
43 #include "mozilla/ipc/BackgroundUtils.h"
44 #include "mozilla/net/ChannelDiverterChild.h"
45 #include "mozilla/net/DNS.h"
46 #include "mozilla/net/SocketProcessBridgeChild.h"
47 #include "mozilla/StaticPrefs_network.h"
48 #include "mozilla/StoragePrincipalHelper.h"
49 #include "SerializedLoadContext.h"
50 #include "nsInputStreamPump.h"
51 #include "InterceptedChannel.h"
52 #include "nsContentSecurityManager.h"
53 #include "nsICompressConvStats.h"
54 #include "nsIDeprecationWarner.h"
55 #include "mozilla/dom/Document.h"
56 #include "nsIEventTarget.h"
57 #include "nsIScriptError.h"
58 #include "nsRedirectHistoryEntry.h"
59 #include "nsSocketTransportService2.h"
60 #include "nsStreamUtils.h"
61 #include "nsThreadUtils.h"
62 #include "nsCORSListenerProxy.h"
63 #include "nsApplicationCache.h"
64 #include "ClassifierDummyChannel.h"
65 #include "nsIOService.h"
66 
67 #ifdef MOZ_TASK_TRACER
68 #  include "GeckoTaskTracer.h"
69 #endif
70 
71 #ifdef MOZ_GECKO_PROFILER
72 #  include "ProfilerMarkerPayload.h"
73 #endif
74 
75 #include <functional>
76 
77 using namespace mozilla::dom;
78 using namespace mozilla::ipc;
79 
80 namespace mozilla {
81 namespace net {
82 
NS_IMPL_ISUPPORTS(InterceptStreamListener,nsIStreamListener,nsIRequestObserver,nsIProgressEventSink)83 NS_IMPL_ISUPPORTS(InterceptStreamListener, nsIStreamListener,
84                   nsIRequestObserver, nsIProgressEventSink)
85 
86 NS_IMETHODIMP
87 InterceptStreamListener::OnStartRequest(nsIRequest* aRequest) {
88   if (mOwner) {
89     mOwner->DoOnStartRequest(mOwner, nullptr);
90   }
91   return NS_OK;
92 }
93 
94 NS_IMETHODIMP
OnStatus(nsIRequest * aRequest,nsresult status,const char16_t * aStatusArg)95 InterceptStreamListener::OnStatus(nsIRequest* aRequest, nsresult status,
96                                   const char16_t* aStatusArg) {
97   if (mOwner) {
98     mOwner->DoOnStatus(mOwner, status);
99   }
100   return NS_OK;
101 }
102 
103 NS_IMETHODIMP
OnProgress(nsIRequest * aRequest,int64_t aProgress,int64_t aProgressMax)104 InterceptStreamListener::OnProgress(nsIRequest* aRequest, int64_t aProgress,
105                                     int64_t aProgressMax) {
106   if (mOwner) {
107     mOwner->DoOnProgress(mOwner, aProgress, aProgressMax);
108   }
109   return NS_OK;
110 }
111 
112 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)113 InterceptStreamListener::OnDataAvailable(nsIRequest* aRequest,
114                                          nsIInputStream* aInputStream,
115                                          uint64_t aOffset, uint32_t aCount) {
116   if (!mOwner) {
117     return NS_OK;
118   }
119 
120   uint32_t loadFlags;
121   mOwner->GetLoadFlags(&loadFlags);
122 
123   if (!(loadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
124     nsCOMPtr<nsIURI> uri;
125     mOwner->GetURI(getter_AddRefs(uri));
126 
127     nsAutoCString host;
128     uri->GetHost(host);
129 
130     OnStatus(mOwner, NS_NET_STATUS_READING, NS_ConvertUTF8toUTF16(host).get());
131 
132     int64_t progress = aOffset + aCount;
133     OnProgress(mOwner, progress, mOwner->mSynthesizedStreamLength);
134   }
135 
136   mOwner->DoOnDataAvailable(mOwner, nullptr, aInputStream, aOffset, aCount);
137   return NS_OK;
138 }
139 
140 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)141 InterceptStreamListener::OnStopRequest(nsIRequest* aRequest,
142                                        nsresult aStatusCode) {
143   if (mOwner) {
144     mOwner->DoPreOnStopRequest(aStatusCode);
145     mOwner->DoOnStopRequest(mOwner, aStatusCode, mContext);
146   }
147   Cleanup();
148   return NS_OK;
149 }
150 
Cleanup()151 void InterceptStreamListener::Cleanup() {
152   mOwner = nullptr;
153   mContext = nullptr;
154 }
155 
156 //-----------------------------------------------------------------------------
157 // HttpChannelChild
158 //-----------------------------------------------------------------------------
159 
HttpChannelChild()160 HttpChannelChild::HttpChannelChild()
161     : HttpAsyncAborter<HttpChannelChild>(this),
162       NeckoTargetHolder(nullptr),
163       mBgChildMutex("HttpChannelChild::BgChildMutex"),
164       mEventTargetMutex("HttpChannelChild::EventTargetMutex"),
165       mSynthesizedStreamLength(0),
166       mCacheEntryId(0),
167       mCacheKey(0),
168       mCacheFetchCount(0),
169       mCacheExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME),
170       mDeletingChannelSent(false),
171       mUnknownDecoderInvolved(false),
172       mDivertingToParent(false),
173       mFlushedForDiversion(false),
174       mIsFromCache(false),
175       mIsRacing(false),
176       mCacheNeedToReportBytesReadInitialized(false),
177       mNeedToReportBytesRead(true),
178       mCacheEntryAvailable(false),
179       mAltDataCacheEntryAvailable(false),
180       mSendResumeAt(false),
181       mKeptAlive(false),
182       mIPCActorDeleted(false),
183       mSuspendSent(false),
184       mSynthesizedResponse(false),
185       mShouldInterceptSubsequentRedirect(false),
186       mRedirectingForSubsequentSynthesizedResponse(false),
187       mPostRedirectChannelShouldIntercept(false),
188       mPostRedirectChannelShouldUpgrade(false),
189       mShouldParentIntercept(false),
190       mSuspendParentAfterSynthesizeResponse(false) {
191   LOG(("Creating HttpChannelChild @%p\n", this));
192 
193   mChannelCreationTime = PR_Now();
194   mChannelCreationTimestamp = TimeStamp::Now();
195   mLastStatusReported =
196       mChannelCreationTimestamp;  // in case we enable the profiler after Init()
197   mAsyncOpenTime = TimeStamp::Now();
198   mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
199 
200   // Ensure that the cookie service is initialized before the first
201   // IPC HTTP channel is created.
202   // We require that the parent cookie service actor exists while
203   // processing HTTP responses.
204   RefPtr<CookieServiceChild> cookieService = CookieServiceChild::GetSingleton();
205 }
206 
~HttpChannelChild()207 HttpChannelChild::~HttpChannelChild() {
208   LOG(("Destroying HttpChannelChild @%p\n", this));
209 
210 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
211   if (mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy && mAsyncOpenSucceeded &&
212       !mSuccesfullyRedirected && !mOnStopRequestCalled) {
213     uint32_t flags =
214         (mSynthesizedResponse ? 1 << 0 : 0) |
215         (mShouldInterceptSubsequentRedirect ? 1 << 1 : 0) |
216         (mRedirectingForSubsequentSynthesizedResponse ? 1 << 2 : 0) |
217         (mPostRedirectChannelShouldIntercept ? 1 << 3 : 0) |
218         (mPostRedirectChannelShouldUpgrade ? 1 << 4 : 0) |
219         (mShouldParentIntercept ? 1 << 5 : 0) |
220         (mInterceptListener ? 1 << 6 : 0) |
221         (mInterceptedRedirectListener ? 1 << 7 : 0) | (mCanceled ? 1 << 8 : 0) |
222         (mRedirectChannelChild ? 1 << 9 : 0);
223     MOZ_CRASH_UNSAFE_PRINTF(
224         "~HttpChannelChild, mOnStopRequestCalled=false, mStatus=0x%08x, "
225         "mActorDestroyReason=%d, redirect count=%zu, flags=%u",
226         static_cast<uint32_t>(nsresult(mStatus)),
227         static_cast<int32_t>(mActorDestroyReason ? *mActorDestroyReason : -1),
228         mLoadInfo->RedirectChainIncludingInternalRedirects().Length(), flags);
229   }
230 #endif
231 
232   ReleaseMainThreadOnlyReferences();
233 }
234 
ReleaseMainThreadOnlyReferences()235 void HttpChannelChild::ReleaseMainThreadOnlyReferences() {
236   if (NS_IsMainThread()) {
237     // Already on main thread, let dtor to
238     // take care of releasing references
239     return;
240   }
241 
242   nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
243   arrayToRelease.AppendElement(mRedirectChannelChild.forget());
244 
245   // To solve multiple inheritence of nsISupports in InterceptStreamListener
246   nsCOMPtr<nsIStreamListener> listener = std::move(mInterceptListener);
247   arrayToRelease.AppendElement(listener.forget());
248 
249   arrayToRelease.AppendElement(mInterceptedRedirectListener.forget());
250 
251   NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
252 }
253 //-----------------------------------------------------------------------------
254 // HttpChannelChild::nsISupports
255 //-----------------------------------------------------------------------------
256 
257 NS_IMPL_ADDREF(HttpChannelChild)
258 
NS_IMETHODIMP_(MozExternalRefCountType)259 NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
260   if (!NS_IsMainThread()) {
261     nsrefcnt count = mRefCnt;
262     nsresult rv = NS_DispatchToMainThread(NewNonOwningRunnableMethod(
263         "HttpChannelChild::Release", this, &HttpChannelChild::Release));
264 
265     // Continue Release procedure if failed to dispatch to main thread.
266     if (!NS_WARN_IF(NS_FAILED(rv))) {
267       return count - 1;
268     }
269   }
270 
271   nsrefcnt count = --mRefCnt;
272   MOZ_ASSERT(int32_t(count) >= 0, "dup release");
273   NS_LOG_RELEASE(this, count, "HttpChannelChild");
274 
275   // Normally we Send_delete in OnStopRequest, but when we need to retain the
276   // remote channel for security info IPDL itself holds 1 reference, so we
277   // Send_delete when refCnt==1.  But if !CanSend(), then there's nobody to send
278   // to, so we fall through.
279   if (mKeptAlive && count == 1 && CanSend()) {
280     mKeptAlive = false;
281     // We send a message to the parent, which calls SendDelete, and then the
282     // child calling Send__delete__() to finally drop the refcount to 0.
283     TrySendDeletingChannel();
284     return 1;
285   }
286 
287   if (count == 0) {
288     mRefCnt = 1; /* stabilize */
289     delete this;
290     return 0;
291   }
292   return count;
293 }
294 
295 NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
NS_INTERFACE_MAP_ENTRY(nsIRequest)296   NS_INTERFACE_MAP_ENTRY(nsIRequest)
297   NS_INTERFACE_MAP_ENTRY(nsIChannel)
298   NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
299   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
300   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICacheInfoChannel,
301                                      !mMultiPartID.isSome())
302   NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
303   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
304   NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
305   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
306   NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
307   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIApplicationCacheContainer,
308                                      !mMultiPartID.isSome())
309   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
310   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
311   NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
312   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
313   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDivertableChannel,
314                                      !mMultiPartID.isSome())
315   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiPartChannel, mMultiPartID.isSome())
316   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIThreadRetargetableRequest,
317                                      !mMultiPartID.isSome())
318   NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelChild)
319 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
320 
321 //-----------------------------------------------------------------------------
322 // HttpChannelChild::PHttpChannelChild
323 //-----------------------------------------------------------------------------
324 
325 void HttpChannelChild::OnBackgroundChildReady(
326     HttpBackgroundChannelChild* aBgChild) {
327   LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n", this,
328        aBgChild));
329   MOZ_ASSERT(OnSocketThread());
330 
331   {
332     MutexAutoLock lock(mBgChildMutex);
333 
334     // mBgChild might be removed or replaced while the original background
335     // channel is inited on STS thread.
336     if (mBgChild != aBgChild) {
337       return;
338     }
339 
340     MOZ_ASSERT(mBgInitFailCallback);
341     mBgInitFailCallback = nullptr;
342   }
343 }
344 
OnBackgroundChildDestroyed(HttpBackgroundChannelChild * aBgChild)345 void HttpChannelChild::OnBackgroundChildDestroyed(
346     HttpBackgroundChannelChild* aBgChild) {
347   LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
348   // This function might be called during shutdown phase, so OnSocketThread()
349   // might return false even on STS thread. Use IsOnCurrentThreadInfallible()
350   // to get correct information.
351   MOZ_ASSERT(gSocketTransportService);
352   MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
353 
354   nsCOMPtr<nsIRunnable> callback;
355   {
356     MutexAutoLock lock(mBgChildMutex);
357 
358     // mBgChild might be removed or replaced while the original background
359     // channel is destroyed on STS thread.
360     if (aBgChild != mBgChild) {
361       return;
362     }
363 
364     mBgChild = nullptr;
365     callback = std::move(mBgInitFailCallback);
366   }
367 
368   if (callback) {
369     nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
370     neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
371   }
372 }
373 
RecvAssociateApplicationCache(const nsCString & aGroupID,const nsCString & aClientID)374 mozilla::ipc::IPCResult HttpChannelChild::RecvAssociateApplicationCache(
375     const nsCString& aGroupID, const nsCString& aClientID) {
376   LOG(("HttpChannelChild::RecvAssociateApplicationCache [this=%p]\n", this));
377   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
378       this, [self = UnsafePtr<HttpChannelChild>(this), aGroupID, aClientID]() {
379         self->AssociateApplicationCache(aGroupID, aClientID);
380       }));
381   return IPC_OK();
382 }
383 
AssociateApplicationCache(const nsCString & aGroupID,const nsCString & aClientID)384 void HttpChannelChild::AssociateApplicationCache(const nsCString& aGroupID,
385                                                  const nsCString& aClientID) {
386   LOG(("HttpChannelChild::AssociateApplicationCache [this=%p]\n", this));
387   mApplicationCache = new nsApplicationCache();
388 
389   mLoadedFromApplicationCache = true;
390   mApplicationCache->InitAsHandle(aGroupID, aClientID);
391 }
392 
RecvOnStartRequest(const nsHttpResponseHead & aResponseHead,const bool & aUseResponseHead,const nsHttpHeaderArray & aRequestHeaders,const HttpChannelOnStartRequestArgs & aArgs)393 mozilla::ipc::IPCResult HttpChannelChild::RecvOnStartRequest(
394     const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
395     const nsHttpHeaderArray& aRequestHeaders,
396     const HttpChannelOnStartRequestArgs& aArgs) {
397   AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnStartRequest", NETWORK);
398   LOG(("HttpChannelChild::RecvOnStartRequest [this=%p]\n", this));
399   // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
400   // stage, as they are set in the listener's OnStartRequest.
401   MOZ_RELEASE_ASSERT(
402       !mFlushedForDiversion,
403       "mFlushedForDiversion should be unset before OnStartRequest!");
404   MOZ_RELEASE_ASSERT(
405       !mDivertingToParent,
406       "mDivertingToParent should be unset before OnStartRequest!");
407 
408   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
409       this, [self = UnsafePtr<HttpChannelChild>(this), aResponseHead,
410              aUseResponseHead, aRequestHeaders, aArgs]() {
411         self->OnStartRequest(aResponseHead, aUseResponseHead, aRequestHeaders,
412                              aArgs);
413       }));
414 
415   {
416     // Child's mEventQ is to control the execution order of the IPC messages
417     // from both main thread IPDL and PBackground IPDL.
418     // To guarantee the ordering, PBackground IPC messages that are sent after
419     // OnStartRequest will be throttled until OnStartRequest hits the Child's
420     // mEventQ.
421     MutexAutoLock lock(mBgChildMutex);
422 
423     // We don't need to notify the background channel if this is a multipart
424     // stream, since all messages will be sent over the main-thread IPDL in
425     // that case.
426     if (mBgChild && !aArgs.multiPartID()) {
427       MOZ_RELEASE_ASSERT(gSocketTransportService);
428       DebugOnly<nsresult> rv = gSocketTransportService->Dispatch(
429           NewRunnableMethod(
430               "HttpBackgroundChannelChild::OnStartRequestReceived", mBgChild,
431               &HttpBackgroundChannelChild::OnStartRequestReceived),
432           NS_DISPATCH_NORMAL);
433     }
434   }
435 
436   return IPC_OK();
437 }
438 
ResourceTimingStructArgsToTimingsStruct(const ResourceTimingStructArgs & aArgs,TimingStruct & aTimings)439 static void ResourceTimingStructArgsToTimingsStruct(
440     const ResourceTimingStructArgs& aArgs, TimingStruct& aTimings) {
441   aTimings.domainLookupStart = aArgs.domainLookupStart();
442   aTimings.domainLookupEnd = aArgs.domainLookupEnd();
443   aTimings.connectStart = aArgs.connectStart();
444   aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
445   aTimings.secureConnectionStart = aArgs.secureConnectionStart();
446   aTimings.connectEnd = aArgs.connectEnd();
447   aTimings.requestStart = aArgs.requestStart();
448   aTimings.responseStart = aArgs.responseStart();
449   aTimings.responseEnd = aArgs.responseEnd();
450 }
451 
OnStartRequest(const nsHttpResponseHead & aResponseHead,const bool & aUseResponseHead,const nsHttpHeaderArray & aRequestHeaders,const HttpChannelOnStartRequestArgs & aArgs)452 void HttpChannelChild::OnStartRequest(
453     const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
454     const nsHttpHeaderArray& aRequestHeaders,
455     const HttpChannelOnStartRequestArgs& aArgs) {
456   LOG(("HttpChannelChild::OnStartRequest [this=%p]\n", this));
457 
458   // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
459   // stage, as they are set in the listener's OnStartRequest.
460   MOZ_RELEASE_ASSERT(
461       !mFlushedForDiversion,
462       "mFlushedForDiversion should be unset before OnStartRequest!");
463   MOZ_RELEASE_ASSERT(
464       !mDivertingToParent,
465       "mDivertingToParent should be unset before OnStartRequest!");
466 
467   // If this channel was aborted by ActorDestroy, then there may be other
468   // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
469   // be handled. In that case we just ignore them to avoid calling the listener
470   // twice.
471   if (mOnStartRequestCalled && mIPCActorDeleted) {
472     return;
473   }
474 
475   mComputedCrossOriginOpenerPolicy = aArgs.openerPolicy();
476 
477   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
478     mStatus = aArgs.channelStatus();
479   }
480 
481   // Cookies headers should not be visible to the child process
482   MOZ_ASSERT(!aRequestHeaders.HasHeader(nsHttp::Cookie));
483   MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
484 
485   if (aUseResponseHead && !mCanceled)
486     mResponseHead = MakeUnique<nsHttpResponseHead>(aResponseHead);
487 
488   if (!aArgs.securityInfoSerialization().IsEmpty()) {
489     [[maybe_unused]] nsresult rv = NS_DeserializeObject(
490         aArgs.securityInfoSerialization(), getter_AddRefs(mSecurityInfo));
491     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
492                           "Deserializing security info should not fail");
493   }
494 
495   ipc::MergeParentLoadInfoForwarder(aArgs.loadInfoForwarder(), mLoadInfo);
496 
497   mIsFromCache = aArgs.isFromCache();
498   mIsRacing = aArgs.isRacing();
499   mCacheEntryAvailable = aArgs.cacheEntryAvailable();
500   mCacheEntryId = aArgs.cacheEntryId();
501   mCacheFetchCount = aArgs.cacheFetchCount();
502   mCacheExpirationTime = aArgs.cacheExpirationTime();
503   mCachedCharset = aArgs.cachedCharset();
504   mSelfAddr = aArgs.selfAddr();
505   mPeerAddr = aArgs.peerAddr();
506 
507   mRedirectCount = aArgs.redirectCount();
508   mAvailableCachedAltDataType = aArgs.altDataType();
509   mDeliveringAltData = aArgs.deliveringAltData();
510   mAltDataLength = aArgs.altDataLength();
511   mResolvedByTRR = aArgs.isResolvedByTRR();
512 
513   SetApplyConversion(aArgs.applyConversion());
514 
515   mAfterOnStartRequestBegun = true;
516 
517   AutoEventEnqueuer ensureSerialDispatch(mEventQ);
518 
519   mCacheKey = aArgs.cacheKey();
520 
521   // replace our request headers with what actually got sent in the parent
522   mRequestHead.SetHeaders(aRequestHeaders);
523 
524   // Note: this is where we would notify "http-on-examine-response" observers.
525   // We have deliberately disabled this for child processes (see bug 806753)
526   //
527   // gHttpHandler->OnExamineResponse(this);
528 
529   mTracingEnabled = false;
530 
531   ResourceTimingStructArgsToTimingsStruct(aArgs.timing(), mTransactionTimings);
532 
533   mAllRedirectsSameOrigin = aArgs.allRedirectsSameOrigin();
534 
535   mMultiPartID = aArgs.multiPartID();
536   mIsLastPartOfMultiPart = aArgs.isLastPartOfMultiPart();
537 
538   DoOnStartRequest(this, nullptr);
539 }
540 
RecvOnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)541 mozilla::ipc::IPCResult HttpChannelChild::RecvOnTransportAndData(
542     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
543     const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
544   AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnTransportAndData", NETWORK);
545   LOG(("HttpChannelChild::RecvOnTransportAndData [this=%p]\n", this));
546 
547   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
548       this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
549              aTransportStatus, aOffset, aCount, aData]() {
550         self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
551                                  aCount, aData);
552       }));
553   return IPC_OK();
554 }
555 
RecvOnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const TimeStamp & aLastActiveTabOptHit,const nsHttpHeaderArray & aResponseTrailers,nsTArray<ConsoleReportCollected> && aConsoleReports)556 mozilla::ipc::IPCResult HttpChannelChild::RecvOnStopRequest(
557     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
558     const TimeStamp& aLastActiveTabOptHit,
559     const nsHttpHeaderArray& aResponseTrailers,
560     nsTArray<ConsoleReportCollected>&& aConsoleReports) {
561   AUTO_PROFILER_LABEL("HttpChannelChild::RecvOnStopRequest", NETWORK);
562   LOG(("HttpChannelChild::RecvOnStopRequest [this=%p]\n", this));
563 
564   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
565       this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
566              aResponseTrailers,
567              consoleReports = CopyableTArray{std::move(aConsoleReports)}]() {
568         self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers,
569                             consoleReports);
570       }));
571   return IPC_OK();
572 }
573 
RecvOnAfterLastPart(const nsresult & aStatus)574 mozilla::ipc::IPCResult HttpChannelChild::RecvOnAfterLastPart(
575     const nsresult& aStatus) {
576   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
577       this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
578         self->OnAfterLastPart(aStatus);
579       }));
580   return IPC_OK();
581 }
582 
OnAfterLastPart(const nsresult & aStatus)583 void HttpChannelChild::OnAfterLastPart(const nsresult& aStatus) {
584   if (mOnStopRequestCalled) {
585     return;
586   }
587   mOnStopRequestCalled = true;
588 
589   // notify "http-on-stop-connect" observers
590   gHttpHandler->OnStopRequest(this);
591 
592   ReleaseListeners();
593 
594   // If a preferred alt-data type was set, the parent would hold a reference to
595   // the cache entry in case the child calls openAlternativeOutputStream().
596   // (see nsHttpChannel::OnStopRequest)
597   if (!mPreferredCachedAltDataTypes.IsEmpty()) {
598     mAltDataCacheEntryAvailable = mCacheEntryAvailable;
599   }
600   mCacheEntryAvailable = false;
601 
602   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
603   CleanupBackgroundChannel();
604 
605   if (mLoadFlags & LOAD_DOCUMENT_URI) {
606     // Keep IPDL channel open, but only for updating security info.
607     // If IPDL is already closed, then do nothing.
608     if (CanSend()) {
609       mKeptAlive = true;
610       SendDocumentChannelCleanup(true);
611     }
612   } else {
613     // The parent process will respond by sending a DeleteSelf message and
614     // making sure not to send any more messages after that.
615     TrySendDeletingChannel();
616   }
617 }
618 
619 class SyntheticDiversionListener final : public nsIStreamListener {
620   RefPtr<HttpChannelChild> mChannel;
621 
622   ~SyntheticDiversionListener() = default;
623 
624  public:
SyntheticDiversionListener(HttpChannelChild * aChannel)625   explicit SyntheticDiversionListener(HttpChannelChild* aChannel)
626       : mChannel(aChannel) {
627     MOZ_ASSERT(mChannel);
628   }
629 
630   NS_IMETHOD
OnStartRequest(nsIRequest * aRequest)631   OnStartRequest(nsIRequest* aRequest) override {
632     MOZ_ASSERT_UNREACHABLE(
633         "SyntheticDiversionListener should never see OnStartRequest");
634     return NS_OK;
635   }
636 
637   NS_IMETHOD
OnStopRequest(nsIRequest * aRequest,nsresult aStatus)638   OnStopRequest(nsIRequest* aRequest, nsresult aStatus) override {
639     if (mChannel->CanSend()) {
640       mChannel->SendDivertOnStopRequest(aStatus);
641       mChannel->SendDivertComplete();
642     }
643     return NS_OK;
644   }
645 
646   NS_IMETHOD
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)647   OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
648                   uint64_t aOffset, uint32_t aCount) override {
649     if (!mChannel->CanSend()) {
650       aRequest->Cancel(NS_ERROR_ABORT);
651       return NS_ERROR_ABORT;
652     }
653 
654     nsAutoCString data;
655     nsresult rv = NS_ConsumeStream(aInputStream, aCount, data);
656     if (NS_WARN_IF(NS_FAILED(rv))) {
657       aRequest->Cancel(rv);
658       return rv;
659     }
660 
661     mChannel->SendDivertOnDataAvailable(data, aOffset, aCount);
662     return NS_OK;
663   }
664 
665   NS_DECL_ISUPPORTS
666 };
667 
668 NS_IMPL_ISUPPORTS(SyntheticDiversionListener, nsIStreamListener);
669 
DoOnStartRequest(nsIRequest * aRequest,nsISupports * aContext)670 void HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest,
671                                         nsISupports* aContext) {
672   nsresult rv;
673 
674   LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
675 
676   // mListener could be null if the redirect setup is not completed.
677   MOZ_ASSERT(mListener || mOnStartRequestCalled);
678   if (!mListener) {
679     Cancel(NS_ERROR_FAILURE);
680     return;
681   }
682 
683   if (mSynthesizedResponsePump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
684     mSynthesizedResponsePump->PeekStream(CallTypeSniffers,
685                                          static_cast<nsIChannel*>(this));
686   }
687 
688   if (mListener) {
689     nsCOMPtr<nsIStreamListener> listener(mListener);
690     mOnStartRequestCalled = true;
691     rv = listener->OnStartRequest(aRequest);
692   } else {
693     rv = NS_ERROR_UNEXPECTED;
694   }
695   mOnStartRequestCalled = true;
696 
697   if (NS_FAILED(rv)) {
698     Cancel(rv);
699     return;
700   }
701 
702   if (mDivertingToParent) {
703     mListener = nullptr;
704     mCompressListener = nullptr;
705     if (mLoadGroup) {
706       mLoadGroup->RemoveRequest(this, nullptr, mStatus);
707     }
708 
709     // If the response has been synthesized in the child, then we are going
710     // be getting OnDataAvailable and OnStopRequest from the synthetic
711     // stream pump.  We need to forward these back to the parent diversion
712     // listener.
713     if (mSynthesizedResponse) {
714       mListener = new SyntheticDiversionListener(this);
715     }
716 
717     return;
718   }
719 
720   nsCOMPtr<nsIStreamListener> listener;
721   rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
722   if (NS_FAILED(rv)) {
723     Cancel(rv);
724   } else if (listener) {
725     mListener = listener;
726     mCompressListener = listener;
727   }
728 }
729 
ProcessOnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)730 void HttpChannelChild::ProcessOnTransportAndData(
731     const nsresult& aChannelStatus, const nsresult& aTransportStatus,
732     const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
733   LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
734   MOZ_ASSERT(OnSocketThread());
735   MOZ_ASSERT(
736       !mMultiPartID,
737       "Should only send ODA on the main-thread channel when using multi-part!");
738   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
739                      "Should not be receiving any more callbacks from parent!");
740   mEventQ->RunOrEnqueue(
741       new ChannelFunctionEvent(
742           [self = UnsafePtr<HttpChannelChild>(this)]() {
743             return self->GetODATarget();
744           },
745           [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
746            aTransportStatus, aOffset, aCount, aData]() {
747             self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
748                                      aCount, aData);
749           }),
750       mDivertingToParent);
751 }
752 
MaybeDivertOnData(const nsCString & aData,const uint64_t & aOffset,const uint32_t & aCount)753 void HttpChannelChild::MaybeDivertOnData(const nsCString& aData,
754                                          const uint64_t& aOffset,
755                                          const uint32_t& aCount) {
756   LOG(("HttpChannelChild::MaybeDivertOnData [this=%p]", this));
757 
758   if (mDivertingToParent) {
759     SendDivertOnDataAvailable(aData, aOffset, aCount);
760   }
761 }
762 
OnTransportAndData(const nsresult & aChannelStatus,const nsresult & aTransportStatus,const uint64_t & aOffset,const uint32_t & aCount,const nsCString & aData)763 void HttpChannelChild::OnTransportAndData(const nsresult& aChannelStatus,
764                                           const nsresult& aTransportStatus,
765                                           const uint64_t& aOffset,
766                                           const uint32_t& aCount,
767                                           const nsCString& aData) {
768   LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
769 
770   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
771     mStatus = aChannelStatus;
772   }
773 
774   // For diversion to parent, just SendDivertOnDataAvailable.
775   if (mDivertingToParent) {
776     MOZ_ASSERT(NS_IsMainThread());
777     MOZ_RELEASE_ASSERT(
778         !mFlushedForDiversion,
779         "Should not be processing any more callbacks from parent!");
780 
781     SendDivertOnDataAvailable(aData, aOffset, aCount);
782     return;
783   }
784 
785   if (mCanceled) {
786     return;
787   }
788 
789   if (mUnknownDecoderInvolved) {
790     LOG(("UnknownDecoder is involved queue OnDataAvailable call. [this=%p]",
791          this));
792     MOZ_ASSERT(NS_IsMainThread());
793     mUnknownDecoderEventQ.AppendElement(
794         MakeUnique<NeckoTargetChannelFunctionEvent>(
795             this,
796             [self = UnsafePtr<HttpChannelChild>(this), aData, aOffset,
797              aCount]() { self->MaybeDivertOnData(aData, aOffset, aCount); }));
798   }
799 
800   // Hold queue lock throughout all three calls, else we might process a later
801   // necko msg in between them.
802   AutoEventEnqueuer ensureSerialDispatch(mEventQ);
803 
804   int64_t progressMax;
805   if (NS_FAILED(GetContentLength(&progressMax))) {
806     progressMax = -1;
807   }
808 
809   const int64_t progress = aOffset + aCount;
810 
811   // OnTransportAndData will be run on retargeted thread if applicable, however
812   // OnStatus/OnProgress event can only be fired on main thread. We need to
813   // dispatch the status/progress event handling back to main thread with the
814   // appropriate event target for networking.
815   if (NS_IsMainThread()) {
816     DoOnStatus(this, aTransportStatus);
817     DoOnProgress(this, progress, progressMax);
818   } else {
819     RefPtr<HttpChannelChild> self = this;
820     nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
821     MOZ_ASSERT(neckoTarget);
822 
823     DebugOnly<nsresult> rv = neckoTarget->Dispatch(
824         NS_NewRunnableFunction(
825             "net::HttpChannelChild::OnTransportAndData",
826             [self, aTransportStatus, progress, progressMax]() {
827               self->DoOnStatus(self, aTransportStatus);
828               self->DoOnProgress(self, progress, progressMax);
829             }),
830         NS_DISPATCH_NORMAL);
831     MOZ_ASSERT(NS_SUCCEEDED(rv));
832   }
833 
834   // OnDataAvailable
835   //
836   // NOTE: the OnDataAvailable contract requires the client to read all the data
837   // in the inputstream.  This code relies on that ('data' will go away after
838   // this function).  Apparently the previous, non-e10s behavior was to actually
839   // support only reading part of the data, allowing later calls to read the
840   // rest.
841   nsCOMPtr<nsIInputStream> stringStream;
842   nsresult rv =
843       NS_NewByteInputStream(getter_AddRefs(stringStream),
844                             MakeSpan(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
845   if (NS_FAILED(rv)) {
846     Cancel(rv);
847     return;
848   }
849 
850   DoOnDataAvailable(this, nullptr, stringStream, aOffset, aCount);
851   stringStream->Close();
852 
853   // TODO: Bug 1523916 backpressure needs to take into account if the data is
854   // coming from the main process or from the socket process via PBackground.
855   if (NeedToReportBytesRead()) {
856     mUnreportBytesRead += aCount;
857     if (mUnreportBytesRead >= gHttpHandler->SendWindowSize() >> 2) {
858       if (NS_IsMainThread()) {
859         Unused << SendBytesRead(mUnreportBytesRead);
860       } else {
861         // PHttpChannel connects to the main thread
862         RefPtr<HttpChannelChild> self = this;
863         int32_t bytesRead = mUnreportBytesRead;
864         nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
865         MOZ_ASSERT(neckoTarget);
866 
867         DebugOnly<nsresult> rv = neckoTarget->Dispatch(
868             NS_NewRunnableFunction("net::HttpChannelChild::SendBytesRead",
869                                    [self, bytesRead]() {
870                                      Unused << self->SendBytesRead(bytesRead);
871                                    }),
872             NS_DISPATCH_NORMAL);
873         MOZ_ASSERT(NS_SUCCEEDED(rv));
874       }
875       mUnreportBytesRead = 0;
876     }
877   }
878 }
879 
NeedToReportBytesRead()880 bool HttpChannelChild::NeedToReportBytesRead() {
881   if (mCacheNeedToReportBytesReadInitialized) {
882     // No need to send SendRecvBytes when diversion starts since the parent
883     // process will suspend for diversion triggered in during OnStrartRequest at
884     // child side, which is earlier. Parent will take over the flow control
885     // after the diverting starts. Sending |SendBytesRead| is redundant.
886     return mNeedToReportBytesRead && !mDivertingToParent;
887   }
888 
889   // Might notify parent for partial cache, and the IPC message is ignored by
890   // parent.
891   int64_t contentLength = -1;
892   if (gHttpHandler->SendWindowSize() == 0 || mIsFromCache ||
893       NS_FAILED(GetContentLength(&contentLength)) ||
894       contentLength < gHttpHandler->SendWindowSize()) {
895     mNeedToReportBytesRead = false;
896   }
897 
898   mCacheNeedToReportBytesReadInitialized = true;
899   return mNeedToReportBytesRead;
900 }
901 
DoOnStatus(nsIRequest * aRequest,nsresult status)902 void HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status) {
903   LOG(("HttpChannelChild::DoOnStatus [this=%p]\n", this));
904   MOZ_ASSERT(NS_IsMainThread());
905 
906   if (mCanceled) return;
907 
908   // cache the progress sink so we don't have to query for it each time.
909   if (!mProgressSink) GetCallback(mProgressSink);
910 
911   // block status/progress after Cancel or OnStopRequest has been called,
912   // or if channel has LOAD_BACKGROUND set.
913   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
914       !(mLoadFlags & LOAD_BACKGROUND)) {
915     nsAutoCString host;
916     mURI->GetHost(host);
917     mProgressSink->OnStatus(aRequest, status,
918                             NS_ConvertUTF8toUTF16(host).get());
919   }
920 }
921 
DoOnProgress(nsIRequest * aRequest,int64_t progress,int64_t progressMax)922 void HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress,
923                                     int64_t progressMax) {
924   LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
925   MOZ_ASSERT(NS_IsMainThread());
926 
927   if (mCanceled) return;
928 
929   // cache the progress sink so we don't have to query for it each time.
930   if (!mProgressSink) GetCallback(mProgressSink);
931 
932   // block status/progress after Cancel or OnStopRequest has been called,
933   // or if channel has LOAD_BACKGROUND set.
934   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
935     // OnProgress
936     //
937     if (progress > 0) {
938       mProgressSink->OnProgress(aRequest, progress, progressMax);
939     }
940   }
941 }
942 
DoOnDataAvailable(nsIRequest * aRequest,nsISupports * aContext,nsIInputStream * aStream,uint64_t aOffset,uint32_t aCount)943 void HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest,
944                                          nsISupports* aContext,
945                                          nsIInputStream* aStream,
946                                          uint64_t aOffset, uint32_t aCount) {
947   AUTO_PROFILER_LABEL("HttpChannelChild::DoOnDataAvailable", NETWORK);
948   LOG(("HttpChannelChild::DoOnDataAvailable [this=%p]\n", this));
949   if (mCanceled) return;
950 
951   if (mListener) {
952     nsCOMPtr<nsIStreamListener> listener(mListener);
953     nsresult rv = listener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
954     if (NS_FAILED(rv)) {
955       CancelOnMainThread(rv);
956     }
957   }
958 }
959 
ProcessOnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const nsHttpHeaderArray & aResponseTrailers,const nsTArray<ConsoleReportCollected> & aConsoleReports)960 void HttpChannelChild::ProcessOnStopRequest(
961     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
962     const nsHttpHeaderArray& aResponseTrailers,
963     const nsTArray<ConsoleReportCollected>& aConsoleReports) {
964   LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
965   MOZ_ASSERT(OnSocketThread());
966   MOZ_ASSERT(
967       !mMultiPartID,
968       "Should only send ODA on the main-thread channel when using multi-part!");
969   MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
970                      "Should not be receiving any more callbacks from parent!");
971 
972   mEventQ->RunOrEnqueue(
973       new NeckoTargetChannelFunctionEvent(
974           this,
975           [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
976            aResponseTrailers,
977            consoleReports = CopyableTArray{aConsoleReports.Clone()}]() {
978             self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers,
979                                 consoleReports);
980           }),
981       mDivertingToParent);
982 }
983 
MaybeDivertOnStop(const nsresult & aChannelStatus)984 void HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus) {
985   LOG(
986       ("HttpChannelChild::MaybeDivertOnStop [this=%p, "
987        "mDivertingToParent=%d status=%" PRIx32 "]",
988        this, static_cast<bool>(mDivertingToParent),
989        static_cast<uint32_t>(aChannelStatus)));
990   if (mDivertingToParent) {
991     SendDivertOnStopRequest(aChannelStatus);
992   }
993 }
994 
OnStopRequest(const nsresult & aChannelStatus,const ResourceTimingStructArgs & aTiming,const nsHttpHeaderArray & aResponseTrailers,const nsTArray<ConsoleReportCollected> & aConsoleReports)995 void HttpChannelChild::OnStopRequest(
996     const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
997     const nsHttpHeaderArray& aResponseTrailers,
998     const nsTArray<ConsoleReportCollected>& aConsoleReports) {
999   LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
1000        static_cast<uint32_t>(aChannelStatus)));
1001   MOZ_ASSERT(NS_IsMainThread());
1002 
1003   // If this channel was aborted by ActorDestroy, then there may be other
1004   // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
1005   // be handled. In that case we just ignore them to avoid calling the listener
1006   // twice.
1007   if (mOnStopRequestCalled && mIPCActorDeleted) {
1008     return;
1009   }
1010 
1011   if (mDivertingToParent) {
1012     MOZ_RELEASE_ASSERT(
1013         !mFlushedForDiversion,
1014         "Should not be processing any more callbacks from parent!");
1015 
1016     SendDivertOnStopRequest(aChannelStatus);
1017     return;
1018   }
1019 
1020   if (mUnknownDecoderInvolved) {
1021     LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
1022          this));
1023     MOZ_ASSERT(NS_IsMainThread());
1024     mUnknownDecoderEventQ.AppendElement(
1025         MakeUnique<NeckoTargetChannelFunctionEvent>(
1026             this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus]() {
1027               self->MaybeDivertOnStop(aChannelStatus);
1028             }));
1029   }
1030 
1031   if (!aConsoleReports.IsEmpty()) {
1032     for (const ConsoleReportCollected& report : aConsoleReports) {
1033       if (report.propertiesFile() <
1034           nsContentUtils::PropertiesFile::PropertiesFile_COUNT) {
1035         AddConsoleReport(
1036             report.errorFlags(), report.category(),
1037             nsContentUtils::PropertiesFile(report.propertiesFile()),
1038             report.sourceFileURI(), report.lineNumber(), report.columnNumber(),
1039             report.messageName(), report.stringParams());
1040       }
1041     }
1042 
1043     MaybeFlushConsoleReports();
1044   }
1045 
1046   nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
1047   if (conv) {
1048     conv->GetDecodedDataLength(&mDecodedBodySize);
1049   }
1050 
1051   ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
1052 
1053   // Do not overwrite or adjust the original mAsyncOpenTime by timing.fetchStart
1054   // We must use the original child process time in order to account for child
1055   // side work and IPC transit overhead.
1056   // XXX: This depends on TimeStamp being equivalent across processes.
1057   // This is true for modern hardware but for older platforms it is not always
1058   // true.
1059 
1060   mRedirectStartTimeStamp = aTiming.redirectStart();
1061   mRedirectEndTimeStamp = aTiming.redirectEnd();
1062   mTransferSize = aTiming.transferSize();
1063   mEncodedBodySize = aTiming.encodedBodySize();
1064   mProtocolVersion = aTiming.protocolVersion();
1065 
1066   mCacheReadStart = aTiming.cacheReadStart();
1067   mCacheReadEnd = aTiming.cacheReadEnd();
1068 
1069 #ifdef MOZ_GECKO_PROFILER
1070   if (profiler_can_accept_markers()) {
1071     nsAutoCString contentType;
1072     if (mResponseHead) {
1073       mResponseHead->ContentType(contentType);
1074     }
1075     int32_t priority = PRIORITY_NORMAL;
1076     GetPriority(&priority);
1077     profiler_add_network_marker(
1078         mURI, priority, mChannelId, NetworkLoadType::LOAD_STOP,
1079         mLastStatusReported, TimeStamp::Now(), mTransferSize, kCacheUnknown,
1080         mLoadInfo->GetInnerWindowID(), &mTransactionTimings, nullptr,
1081         std::move(mSource), Some(nsDependentCString(contentType.get())));
1082   }
1083 #endif
1084 
1085   mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers);
1086 
1087   DoPreOnStopRequest(aChannelStatus);
1088 
1089   {  // We must flush the queue before we Send__delete__
1090     // (although we really shouldn't receive any msgs after OnStop),
1091     // so make sure this goes out of scope before then.
1092     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
1093 
1094     DoOnStopRequest(this, aChannelStatus, nullptr);
1095     // DoOnStopRequest() calls ReleaseListeners()
1096   }
1097 
1098   // If unknownDecoder is involved and the received content is short we will
1099   // know whether we need to divert to parent only after OnStopRequest of the
1100   // listeners chain is called in DoOnStopRequest. At that moment
1101   // unknownDecoder will call OnStartRequest of the real listeners of the
1102   // channel including the OnStopRequest of UrlLoader which decides whether we
1103   // need to divert to parent.
1104   // If we are diverting to parent we should not do a cleanup.
1105   if (mDivertingToParent) {
1106     LOG(
1107         ("HttpChannelChild::OnStopRequest  - We are diverting to parent, "
1108          "postpone cleaning up."));
1109     return;
1110   }
1111 
1112   // If we're a multi-part stream, then don't cleanup yet, and we'll do so
1113   // in OnAfterLastPart.
1114   if (mMultiPartID) {
1115     LOG(
1116         ("HttpChannelChild::OnStopRequest  - Expecting future parts on a "
1117          "multipart channel postpone cleaning up."));
1118     return;
1119   }
1120 
1121   CleanupBackgroundChannel();
1122 
1123   // If there is a possibility we might want to write alt data to the cache
1124   // entry, we keep the channel alive. We still send the DocumentChannelCleanup
1125   // message but request the cache entry to be kept by the parent.
1126   // If the channel has failed, the cache entry is in a non-writtable state and
1127   // we want to release it to not block following consumers.
1128   if (NS_SUCCEEDED(aChannelStatus) && !mPreferredCachedAltDataTypes.IsEmpty()) {
1129     mKeptAlive = true;
1130     SendDocumentChannelCleanup(false);  // don't clear cache entry
1131     return;
1132   }
1133 
1134   if (mLoadFlags & LOAD_DOCUMENT_URI) {
1135     // Keep IPDL channel open, but only for updating security info.
1136     // If IPDL is already closed, then do nothing.
1137     if (CanSend()) {
1138       mKeptAlive = true;
1139       SendDocumentChannelCleanup(true);
1140     }
1141   } else {
1142     // The parent process will respond by sending a DeleteSelf message and
1143     // making sure not to send any more messages after that.
1144     TrySendDeletingChannel();
1145   }
1146 }
1147 
DoPreOnStopRequest(nsresult aStatus)1148 void HttpChannelChild::DoPreOnStopRequest(nsresult aStatus) {
1149   AUTO_PROFILER_LABEL("HttpChannelChild::DoPreOnStopRequest", NETWORK);
1150   LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
1151        this, static_cast<uint32_t>(aStatus)));
1152   mIsPending = false;
1153 
1154   MaybeCallSynthesizedCallback();
1155 
1156   MaybeReportTimingData();
1157 
1158   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
1159     mStatus = aStatus;
1160   }
1161 
1162   CollectOMTTelemetry();
1163 }
1164 
CollectOMTTelemetry()1165 void HttpChannelChild::CollectOMTTelemetry() {
1166   MOZ_ASSERT(NS_IsMainThread());
1167 
1168   // Only collect telemetry for HTTP channel that is loaded successfully and
1169   // completely.
1170   if (mCanceled || NS_FAILED(mStatus)) {
1171     return;
1172   }
1173 
1174   // Use content policy type to accumulate data by usage.
1175   nsAutoCString key(
1176       NS_CP_ContentTypeName(mLoadInfo->InternalContentPolicyType()));
1177 
1178   Telemetry::AccumulateCategoricalKeyed(key, mOMTResult);
1179 }
1180 
DoOnStopRequest(nsIRequest * aRequest,nsresult aChannelStatus,nsISupports * aContext)1181 void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
1182                                        nsresult aChannelStatus,
1183                                        nsISupports* aContext) {
1184   AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
1185   LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
1186   MOZ_ASSERT(NS_IsMainThread());
1187   MOZ_ASSERT(!mIsPending);
1188 
1189   auto checkForBlockedContent = [&]() {
1190     // NB: We use aChannelStatus here instead of mStatus because if there was an
1191     // nsCORSListenerProxy on this request, it will override the tracking
1192     // protection's return value.
1193     if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
1194             aChannelStatus) ||
1195         aChannelStatus == NS_ERROR_MALWARE_URI ||
1196         aChannelStatus == NS_ERROR_UNWANTED_URI ||
1197         aChannelStatus == NS_ERROR_BLOCKED_URI ||
1198         aChannelStatus == NS_ERROR_HARMFUL_URI ||
1199         aChannelStatus == NS_ERROR_PHISHING_URI) {
1200       nsCString list, provider, fullhash;
1201 
1202       nsresult rv = GetMatchedList(list);
1203       NS_ENSURE_SUCCESS_VOID(rv);
1204 
1205       rv = GetMatchedProvider(provider);
1206       NS_ENSURE_SUCCESS_VOID(rv);
1207 
1208       rv = GetMatchedFullHash(fullhash);
1209       NS_ENSURE_SUCCESS_VOID(rv);
1210 
1211       UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
1212                                              provider, fullhash);
1213     }
1214   };
1215   checkForBlockedContent();
1216 
1217   // See bug 1587686. If the redirect setup is not completed, the post-redirect
1218   // channel will be not opened and mListener will be null.
1219   MOZ_ASSERT(mListener || !mWasOpened);
1220   if (!mListener) {
1221     return;
1222   }
1223 
1224   MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
1225 
1226   if (mListener) {
1227     nsCOMPtr<nsIStreamListener> listener(mListener);
1228     mOnStopRequestCalled = true;
1229     listener->OnStopRequest(aRequest, mStatus);
1230   }
1231   mOnStopRequestCalled = true;
1232 
1233   // If we're a multi-part stream, then don't cleanup yet, and we'll do so
1234   // in OnAfterLastPart.
1235   if (mMultiPartID) {
1236     LOG(
1237         ("HttpChannelChild::DoOnStopRequest  - Expecting future parts on a "
1238          "multipart channel not releasing listeners."));
1239     mOnStopRequestCalled = false;
1240     mOnStartRequestCalled = false;
1241     return;
1242   }
1243 
1244   // notify "http-on-stop-connect" observers
1245   gHttpHandler->OnStopRequest(this);
1246 
1247   ReleaseListeners();
1248 
1249   // If a preferred alt-data type was set, the parent would hold a reference to
1250   // the cache entry in case the child calls openAlternativeOutputStream().
1251   // (see nsHttpChannel::OnStopRequest)
1252   if (!mPreferredCachedAltDataTypes.IsEmpty()) {
1253     mAltDataCacheEntryAvailable = mCacheEntryAvailable;
1254   }
1255   mCacheEntryAvailable = false;
1256 
1257   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
1258 }
1259 
RecvOnProgress(const int64_t & aProgress,const int64_t & aProgressMax)1260 mozilla::ipc::IPCResult HttpChannelChild::RecvOnProgress(
1261     const int64_t& aProgress, const int64_t& aProgressMax) {
1262   LOG(("HttpChannelChild::RecvOnProgress [this=%p]\n", this));
1263   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1264       this,
1265       [self = UnsafePtr<HttpChannelChild>(this), aProgress, aProgressMax]() {
1266         AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1267         self->DoOnProgress(self, aProgress, aProgressMax);
1268       }));
1269   return IPC_OK();
1270 }
1271 
RecvOnStatus(const nsresult & aStatus)1272 mozilla::ipc::IPCResult HttpChannelChild::RecvOnStatus(
1273     const nsresult& aStatus) {
1274   LOG(("HttpChannelChild::RecvOnStatus [this=%p]\n", this));
1275   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1276       this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1277         AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1278         self->DoOnStatus(self, aStatus);
1279       }));
1280   return IPC_OK();
1281 }
1282 
RecvFailedAsyncOpen(const nsresult & aStatus)1283 mozilla::ipc::IPCResult HttpChannelChild::RecvFailedAsyncOpen(
1284     const nsresult& aStatus) {
1285   LOG(("HttpChannelChild::RecvFailedAsyncOpen [this=%p]\n", this));
1286   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1287       this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1288         self->FailedAsyncOpen(aStatus);
1289       }));
1290   return IPC_OK();
1291 }
1292 
1293 // We need to have an implementation of this function just so that we can keep
1294 // all references to mCallOnResume of type HttpChannelChild:  it's not OK in C++
1295 // to set a member function ptr to a base class function.
HandleAsyncAbort()1296 void HttpChannelChild::HandleAsyncAbort() {
1297   HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
1298 
1299   // Ignore all the messages from background channel after channel aborted.
1300   CleanupBackgroundChannel();
1301 }
1302 
FailedAsyncOpen(const nsresult & status)1303 void HttpChannelChild::FailedAsyncOpen(const nsresult& status) {
1304   LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n", this,
1305        static_cast<uint32_t>(status)));
1306   MOZ_ASSERT(NS_IsMainThread());
1307 
1308   // Might be called twice in race condition in theory.
1309   // (one by RecvFailedAsyncOpen, another by
1310   // HttpBackgroundChannelChild::ActorFailed)
1311   if (mOnStartRequestCalled) {
1312     return;
1313   }
1314 
1315   if (NS_SUCCEEDED(mStatus)) {
1316     mStatus = status;
1317   }
1318 
1319   // We're already being called from IPDL, therefore already "async"
1320   HandleAsyncAbort();
1321 
1322   if (CanSend()) {
1323     TrySendDeletingChannel();
1324   }
1325 }
1326 
CleanupBackgroundChannel()1327 void HttpChannelChild::CleanupBackgroundChannel() {
1328   MutexAutoLock lock(mBgChildMutex);
1329 
1330   AUTO_PROFILER_LABEL("HttpChannelChild::CleanupBackgroundChannel", NETWORK);
1331   LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
1332        this, mBgChild.get()));
1333 
1334   mBgInitFailCallback = nullptr;
1335 
1336   if (!mBgChild) {
1337     return;
1338   }
1339 
1340   RefPtr<HttpBackgroundChannelChild> bgChild = std::move(mBgChild);
1341 
1342   MOZ_RELEASE_ASSERT(gSocketTransportService);
1343   if (!OnSocketThread()) {
1344     gSocketTransportService->Dispatch(
1345         NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
1346                           bgChild,
1347                           &HttpBackgroundChannelChild::OnChannelClosed),
1348         NS_DISPATCH_NORMAL);
1349   } else {
1350     bgChild->OnChannelClosed();
1351   }
1352 }
1353 
DoNotifyListenerCleanup()1354 void HttpChannelChild::DoNotifyListenerCleanup() {
1355   LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
1356 
1357   if (mInterceptListener) {
1358     mInterceptListener->Cleanup();
1359     mInterceptListener = nullptr;
1360   }
1361 
1362   MaybeCallSynthesizedCallback();
1363 }
1364 
DoAsyncAbort(nsresult aStatus)1365 void HttpChannelChild::DoAsyncAbort(nsresult aStatus) {
1366   Unused << AsyncAbort(aStatus);
1367 }
1368 
RecvDeleteSelf()1369 mozilla::ipc::IPCResult HttpChannelChild::RecvDeleteSelf() {
1370   LOG(("HttpChannelChild::RecvDeleteSelf [this=%p]\n", this));
1371   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1372       this,
1373       [self = UnsafePtr<HttpChannelChild>(this)]() { self->DeleteSelf(); }));
1374   return IPC_OK();
1375 }
1376 
OverrideRunnable(HttpChannelChild * aChannel,HttpChannelChild * aNewChannel,InterceptStreamListener * aListener,nsIInputStream * aInput,nsIInterceptedBodyCallback * aCallback,UniquePtr<nsHttpResponseHead> && aHead,nsICacheInfoChannel * aCacheInfo)1377 HttpChannelChild::OverrideRunnable::OverrideRunnable(
1378     HttpChannelChild* aChannel, HttpChannelChild* aNewChannel,
1379     InterceptStreamListener* aListener, nsIInputStream* aInput,
1380     nsIInterceptedBodyCallback* aCallback,
1381     UniquePtr<nsHttpResponseHead>&& aHead, nsICacheInfoChannel* aCacheInfo)
1382     : Runnable("net::HttpChannelChild::OverrideRunnable") {
1383   mChannel = aChannel;
1384   mNewChannel = aNewChannel;
1385   mListener = aListener;
1386   mInput = aInput;
1387   mCallback = aCallback;
1388   mHead = std::move(aHead);
1389   mSynthesizedCacheInfo = aCacheInfo;
1390 }
1391 
OverrideWithSynthesizedResponse()1392 void HttpChannelChild::OverrideRunnable::OverrideWithSynthesizedResponse() {
1393   if (mNewChannel) {
1394     mNewChannel->OverrideWithSynthesizedResponse(
1395         mHead, mInput, mCallback, mListener, mSynthesizedCacheInfo);
1396   }
1397 }
1398 
1399 NS_IMETHODIMP
Run()1400 HttpChannelChild::OverrideRunnable::Run() {
1401   // Check to see if the channel was canceled in the middle of the redirect.
1402   nsresult rv = NS_OK;
1403   Unused << mChannel->GetStatus(&rv);
1404   if (NS_FAILED(rv)) {
1405     if (mCallback) {
1406       mCallback->BodyComplete(rv);
1407       mCallback = nullptr;
1408     }
1409     mChannel->CleanupRedirectingChannel(rv);
1410     if (mNewChannel) {
1411       mNewChannel->Cancel(rv);
1412     }
1413     return NS_OK;
1414   }
1415 
1416   bool ret = mChannel->Redirect3Complete(this);
1417 
1418   // If the method returns false, it means the IPDL connection is being
1419   // asyncly torn down and reopened, and OverrideWithSynthesizedResponse
1420   // will be called later from FinishInterceptedRedirect. This object will
1421   // be assigned to HttpChannelChild::mOverrideRunnable in order to do so.
1422   // If it is true, we can call the method right now.
1423   if (ret) {
1424     OverrideWithSynthesizedResponse();
1425   }
1426 
1427   return NS_OK;
1428 }
1429 
RecvFinishInterceptedRedirect()1430 mozilla::ipc::IPCResult HttpChannelChild::RecvFinishInterceptedRedirect() {
1431   // Hold a ref to this to keep it from being deleted by Send__delete__()
1432   RefPtr<HttpChannelChild> self(this);
1433   Send__delete__(this);
1434 
1435   {
1436     // Reset the event target since the IPC actor is about to be destroyed.
1437     // Following channel event should be handled on main thread.
1438     MutexAutoLock lock(mEventTargetMutex);
1439     mNeckoTarget = nullptr;
1440   }
1441 
1442   // The IPDL connection was torn down by a interception logic in
1443   // CompleteRedirectSetup, and we need to call FinishInterceptedRedirect.
1444   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
1445   MOZ_ASSERT(neckoTarget);
1446 
1447   Unused << neckoTarget->Dispatch(
1448       NewRunnableMethod("net::HttpChannelChild::FinishInterceptedRedirect",
1449                         this, &HttpChannelChild::FinishInterceptedRedirect),
1450       NS_DISPATCH_NORMAL);
1451 
1452   return IPC_OK();
1453 }
1454 
DeleteSelf()1455 void HttpChannelChild::DeleteSelf() { Send__delete__(this); }
1456 
DoNotifyListener()1457 void HttpChannelChild::DoNotifyListener() {
1458   LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
1459   MOZ_ASSERT(NS_IsMainThread());
1460 
1461   // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
1462   // LOAD_ONLY_IF_MODIFIED) we want to set mAfterOnStartRequestBegun to true
1463   // before notifying listener.
1464   if (!mAfterOnStartRequestBegun) {
1465     mAfterOnStartRequestBegun = true;
1466   }
1467 
1468   if (mListener && !mOnStartRequestCalled) {
1469     nsCOMPtr<nsIStreamListener> listener = mListener;
1470     mOnStartRequestCalled = true;  // avoid reentrancy bugs by setting this now
1471     listener->OnStartRequest(this);
1472   }
1473   mOnStartRequestCalled = true;
1474 
1475   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1476       this, [self = UnsafePtr<HttpChannelChild>(this)] {
1477         self->ContinueDoNotifyListener();
1478       }));
1479 }
1480 
ContinueDoNotifyListener()1481 void HttpChannelChild::ContinueDoNotifyListener() {
1482   LOG(("HttpChannelChild::ContinueDoNotifyListener this=%p", this));
1483   MOZ_ASSERT(NS_IsMainThread());
1484 
1485   // Make sure mIsPending is set to false. At this moment we are done from
1486   // the point of view of our consumer and we have to report our self
1487   // as not-pending.
1488   mIsPending = false;
1489 
1490   if (mListener && !mOnStopRequestCalled) {
1491     nsCOMPtr<nsIStreamListener> listener = mListener;
1492     mOnStopRequestCalled = true;
1493     listener->OnStopRequest(this, mStatus);
1494   }
1495   mOnStopRequestCalled = true;
1496 
1497   // notify "http-on-stop-request" observers
1498   gHttpHandler->OnStopRequest(this);
1499 
1500   // This channel has finished its job, potentially release any tail-blocked
1501   // requests with this.
1502   RemoveAsNonTailRequest();
1503 
1504   // We have to make sure to drop the references to listeners and callbacks
1505   // no longer needed.
1506   ReleaseListeners();
1507 
1508   DoNotifyListenerCleanup();
1509 
1510   // If this is a navigation, then we must let the docshell flush the reports
1511   // to the console later.  The LoadDocument() is pointing at the detached
1512   // document that started the navigation.  We want to show the reports on the
1513   // new document.  Otherwise the console is wiped and the user never sees
1514   // the information.
1515   if (!IsNavigation()) {
1516     if (mLoadGroup) {
1517       FlushConsoleReports(mLoadGroup);
1518     } else {
1519       RefPtr<dom::Document> doc;
1520       mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1521       FlushConsoleReports(doc);
1522     }
1523   }
1524 }
1525 
FinishInterceptedRedirect()1526 void HttpChannelChild::FinishInterceptedRedirect() {
1527   nsresult rv;
1528   rv = AsyncOpen(mInterceptedRedirectListener);
1529 
1530   mInterceptedRedirectListener = nullptr;
1531 
1532   if (mInterceptingChannel) {
1533     mInterceptingChannel->CleanupRedirectingChannel(rv);
1534     mInterceptingChannel = nullptr;
1535   }
1536 
1537   if (mOverrideRunnable) {
1538     mOverrideRunnable->OverrideWithSynthesizedResponse();
1539     mOverrideRunnable = nullptr;
1540   }
1541 }
1542 
RecvReportSecurityMessage(const nsString & messageTag,const nsString & messageCategory)1543 mozilla::ipc::IPCResult HttpChannelChild::RecvReportSecurityMessage(
1544     const nsString& messageTag, const nsString& messageCategory) {
1545   DebugOnly<nsresult> rv = AddSecurityMessage(messageTag, messageCategory);
1546   MOZ_ASSERT(NS_SUCCEEDED(rv));
1547   return IPC_OK();
1548 }
1549 
RecvRedirect1Begin(const uint32_t & aRegistrarId,const URIParams & aNewUri,const uint32_t & aNewLoadFlags,const uint32_t & aRedirectFlags,const ParentLoadInfoForwarderArgs & aLoadInfoForwarder,const nsHttpResponseHead & aResponseHead,const nsCString & aSecurityInfoSerialization,const uint64_t & aChannelId,const NetAddr & aOldPeerAddr,const ResourceTimingStructArgs & aTiming)1550 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect1Begin(
1551     const uint32_t& aRegistrarId, const URIParams& aNewUri,
1552     const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags,
1553     const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
1554     const nsHttpResponseHead& aResponseHead,
1555     const nsCString& aSecurityInfoSerialization, const uint64_t& aChannelId,
1556     const NetAddr& aOldPeerAddr, const ResourceTimingStructArgs& aTiming) {
1557   // TODO: handle security info
1558   LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
1559   // We set peer address of child to the old peer,
1560   // Then it will be updated to new peer in OnStartRequest
1561   mPeerAddr = aOldPeerAddr;
1562 
1563   // Cookies headers should not be visible to the child process
1564   MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
1565 
1566   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1567       this, [self = UnsafePtr<HttpChannelChild>(this), aRegistrarId, aNewUri,
1568              aNewLoadFlags, aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1569              aSecurityInfoSerialization, aChannelId, aTiming]() {
1570         self->Redirect1Begin(aRegistrarId, aNewUri, aNewLoadFlags,
1571                              aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1572                              aSecurityInfoSerialization, aChannelId, aTiming);
1573       }));
1574   return IPC_OK();
1575 }
1576 
SetupRedirect(nsIURI * uri,const nsHttpResponseHead * responseHead,const uint32_t & redirectFlags,nsIChannel ** outChannel)1577 nsresult HttpChannelChild::SetupRedirect(nsIURI* uri,
1578                                          const nsHttpResponseHead* responseHead,
1579                                          const uint32_t& redirectFlags,
1580                                          nsIChannel** outChannel) {
1581   LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
1582 
1583   nsresult rv;
1584   nsCOMPtr<nsIIOService> ioService;
1585   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
1586   NS_ENSURE_SUCCESS(rv, rv);
1587 
1588   nsCOMPtr<nsIChannel> newChannel;
1589   nsCOMPtr<nsILoadInfo> redirectLoadInfo =
1590       CloneLoadInfoForRedirect(uri, redirectFlags);
1591   rv = NS_NewChannelInternal(getter_AddRefs(newChannel), uri, redirectLoadInfo,
1592                              nullptr,  // PerformanceStorage
1593                              nullptr,  // aLoadGroup
1594                              nullptr,  // aCallbacks
1595                              nsIRequest::LOAD_NORMAL, ioService);
1596   NS_ENSURE_SUCCESS(rv, rv);
1597 
1598   // We won't get OnStartRequest, set cookies here.
1599   mResponseHead = MakeUnique<nsHttpResponseHead>(*responseHead);
1600 
1601   bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
1602       mResponseHead->Status(), mRequestHead.ParsedMethod());
1603 
1604   rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, redirectFlags);
1605   NS_ENSURE_SUCCESS(rv, rv);
1606 
1607   nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
1608       do_QueryInterface(newChannel);
1609   if (httpChannelChild) {
1610     bool shouldUpgrade = false;
1611     auto channelChild = static_cast<HttpChannelChild*>(httpChannelChild.get());
1612     if (mShouldInterceptSubsequentRedirect) {
1613       // In the case where there was a synthesized response that caused a
1614       // redirection, we must force the new channel to intercept the request in
1615       // the parent before a network transaction is initiated.
1616       rv = httpChannelChild->ForceIntercepted(false, false);
1617     } else if (mRedirectMode == nsIHttpChannelInternal::REDIRECT_MODE_MANUAL &&
1618                ((redirectFlags & (nsIChannelEventSink::REDIRECT_TEMPORARY |
1619                                   nsIChannelEventSink::REDIRECT_PERMANENT)) !=
1620                 0) &&
1621                channelChild->ShouldInterceptURI(uri, shouldUpgrade)) {
1622       // In the case where the redirect mode is manual, we need to check whether
1623       // the post-redirect channel needs to be intercepted.  If that is the
1624       // case, force the new channel to intercept the request in the parent
1625       // similar to the case above, but also remember that ShouldInterceptURI()
1626       // returned true to avoid calling it a second time.
1627       rv = httpChannelChild->ForceIntercepted(true, shouldUpgrade);
1628     }
1629     MOZ_ASSERT(NS_SUCCEEDED(rv));
1630   }
1631 
1632   mRedirectChannelChild = do_QueryInterface(newChannel);
1633   newChannel.forget(outChannel);
1634 
1635   return NS_OK;
1636 }
1637 
Redirect1Begin(const uint32_t & registrarId,const URIParams & newOriginalURI,const uint32_t & newLoadFlags,const uint32_t & redirectFlags,const ParentLoadInfoForwarderArgs & loadInfoForwarder,const nsHttpResponseHead & responseHead,const nsACString & securityInfoSerialization,const uint64_t & channelId,const ResourceTimingStructArgs & timing)1638 void HttpChannelChild::Redirect1Begin(
1639     const uint32_t& registrarId, const URIParams& newOriginalURI,
1640     const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
1641     const ParentLoadInfoForwarderArgs& loadInfoForwarder,
1642     const nsHttpResponseHead& responseHead,
1643     const nsACString& securityInfoSerialization, const uint64_t& channelId,
1644     const ResourceTimingStructArgs& timing) {
1645   nsresult rv;
1646 
1647   LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
1648 
1649   ipc::MergeParentLoadInfoForwarder(loadInfoForwarder, mLoadInfo);
1650 
1651   nsCOMPtr<nsIURI> uri = DeserializeURI(newOriginalURI);
1652 
1653   ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings);
1654 
1655 #ifdef MOZ_GECKO_PROFILER
1656   if (profiler_can_accept_markers()) {
1657     nsAutoCString contentType;
1658     responseHead.ContentType(contentType);
1659 
1660     profiler_add_network_marker(
1661         mURI, mPriority, mChannelId, NetworkLoadType::LOAD_REDIRECT,
1662         mLastStatusReported, TimeStamp::Now(), 0, kCacheUnknown,
1663         mLoadInfo->GetInnerWindowID(), &mTransactionTimings, uri,
1664         std::move(mSource), Some(nsDependentCString(contentType.get())));
1665   }
1666 #endif
1667 
1668   if (!securityInfoSerialization.IsEmpty()) {
1669     rv = NS_DeserializeObject(securityInfoSerialization,
1670                               getter_AddRefs(mSecurityInfo));
1671     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
1672                           "Deserializing security info should not fail");
1673   }
1674 
1675   nsCOMPtr<nsIChannel> newChannel;
1676   rv = SetupRedirect(uri, &responseHead, redirectFlags,
1677                      getter_AddRefs(newChannel));
1678 
1679   if (NS_SUCCEEDED(rv)) {
1680     MOZ_ALWAYS_SUCCEEDS(newChannel->SetLoadFlags(newLoadFlags));
1681 
1682     if (mRedirectChannelChild) {
1683       // Set the channelId allocated in parent to the child instance
1684       nsCOMPtr<nsIHttpChannel> httpChannel =
1685           do_QueryInterface(mRedirectChannelChild);
1686       if (httpChannel) {
1687         rv = httpChannel->SetChannelId(channelId);
1688         MOZ_ASSERT(NS_SUCCEEDED(rv));
1689       }
1690       mRedirectChannelChild->ConnectParent(registrarId);
1691     }
1692 
1693     nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
1694     MOZ_ASSERT(target);
1695 
1696     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags,
1697                                               target);
1698   }
1699 
1700   if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1701 }
1702 
BeginNonIPCRedirect(nsIURI * responseURI,const nsHttpResponseHead * responseHead,bool aResponseRedirected)1703 void HttpChannelChild::BeginNonIPCRedirect(
1704     nsIURI* responseURI, const nsHttpResponseHead* responseHead,
1705     bool aResponseRedirected) {
1706   LOG(("HttpChannelChild::BeginNonIPCRedirect [this=%p]\n", this));
1707 
1708   // This method is only used by child-side service workers.  It should not be
1709   // used by new code.  We want to remove it in the future.
1710   MOZ_DIAGNOSTIC_ASSERT(mSynthesizedResponse);
1711 
1712   // If the response has been redirected, propagate all the URLs to content.
1713   // Thus, the exact value of the redirect flag does not matter as long as it's
1714   // not REDIRECT_INTERNAL.
1715   const uint32_t redirectFlag = aResponseRedirected
1716                                     ? nsIChannelEventSink::REDIRECT_TEMPORARY
1717                                     : nsIChannelEventSink::REDIRECT_INTERNAL;
1718 
1719   nsCOMPtr<nsIChannel> newChannel;
1720   nsresult rv = SetupRedirect(responseURI, responseHead, redirectFlag,
1721                               getter_AddRefs(newChannel));
1722 
1723   if (NS_SUCCEEDED(rv)) {
1724     // Ensure that the new channel shares the original channel's security
1725     // information, since it won't be provided via IPC. In particular, if the
1726     // target of this redirect is a synthesized response that has its own
1727     // security info, the pre-redirect channel has already received it and it
1728     // must be propagated to the post-redirect channel.
1729     nsCOMPtr<nsIHttpChannelChild> channelChild = do_QueryInterface(newChannel);
1730     if (mSecurityInfo && channelChild) {
1731       HttpChannelChild* httpChannelChild =
1732           static_cast<HttpChannelChild*>(channelChild.get());
1733       httpChannelChild->OverrideSecurityInfoForNonIPCRedirect(mSecurityInfo);
1734     }
1735 
1736     // Normally we don't propagate the LoadInfo's service worker tainting
1737     // synthesis flag on redirect.  A real redirect normally will want to allow
1738     // normal tainting to proceed from its starting taint.  For this particular
1739     // redirect, though, we are performing a redirect to communicate the URL of
1740     // the service worker synthetic response itself.  This redirect still
1741     // represents the synthetic response, so we must preserve the flag.
1742     if (mLoadInfo->GetServiceWorkerTaintingSynthesized()) {
1743       nsCOMPtr<nsILoadInfo> newChannelLoadInfo = newChannel->LoadInfo();
1744       if (newChannelLoadInfo) {
1745         newChannelLoadInfo->SynthesizeServiceWorkerTainting(
1746             mLoadInfo->GetTainting());
1747       }
1748     }
1749 
1750     nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
1751     MOZ_ASSERT(target);
1752 
1753     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlag,
1754                                               target);
1755   }
1756 
1757   if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1758 }
1759 
OverrideSecurityInfoForNonIPCRedirect(nsISupports * securityInfo)1760 void HttpChannelChild::OverrideSecurityInfoForNonIPCRedirect(
1761     nsISupports* securityInfo) {
1762   mResponseCouldBeSynthesized = true;
1763   DebugOnly<nsresult> rv = OverrideSecurityInfo(securityInfo);
1764   MOZ_ASSERT(NS_SUCCEEDED(rv));
1765 }
1766 
RecvRedirect3Complete()1767 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect3Complete() {
1768   LOG(("HttpChannelChild::RecvRedirect3Complete [this=%p]\n", this));
1769   nsCOMPtr<nsIChannel> redirectChannel =
1770       do_QueryInterface(mRedirectChannelChild);
1771   MOZ_ASSERT(redirectChannel);
1772   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1773       this, [self = UnsafePtr<HttpChannelChild>(this), redirectChannel]() {
1774         nsresult rv = NS_OK;
1775         Unused << self->GetStatus(&rv);
1776         if (NS_FAILED(rv)) {
1777           // Pre-redirect channel was canceled. Call |HandleAsyncAbort|, so
1778           // mListener's OnStart/StopRequest can be called. Nothing else will
1779           // trigger these notification after this point.
1780           // We do this before |CompleteRedirectSetup|, so post-redirect channel
1781           // stays unopened and we also make sure that OnStart/StopRequest won't
1782           // be called twice.
1783           self->HandleAsyncAbort();
1784 
1785           nsCOMPtr<nsIHttpChannelChild> chan =
1786               do_QueryInterface(redirectChannel);
1787           RefPtr<HttpChannelChild> httpChannelChild =
1788               static_cast<HttpChannelChild*>(chan.get());
1789           if (httpChannelChild) {
1790             // For sending an IPC message to parent channel so that the loading
1791             // can be cancelled.
1792             Unused << httpChannelChild->Cancel(rv);
1793 
1794             // The post-redirect channel could still get OnStart/StopRequest IPC
1795             // messages from parent, but the mListener is still null. So, we
1796             // call |DoNotifyListener| to pretend that OnStart/StopRequest are
1797             // already called.
1798             httpChannelChild->DoNotifyListener();
1799           }
1800           return;
1801         }
1802 
1803         self->Redirect3Complete(nullptr);
1804       }));
1805   return IPC_OK();
1806 }
1807 
ProcessFlushedForDiversion()1808 void HttpChannelChild::ProcessFlushedForDiversion() {
1809   LOG(("HttpChannelChild::ProcessFlushedForDiversion [this=%p]\n", this));
1810   MOZ_ASSERT(OnSocketThread());
1811   MOZ_RELEASE_ASSERT(mDivertingToParent);
1812 
1813   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1814                             this,
1815                             [self = UnsafePtr<HttpChannelChild>(this)]() {
1816                               self->FlushedForDiversion();
1817                             }),
1818                         true);
1819 }
1820 
RecvNotifyClassificationFlags(const uint32_t & aClassificationFlags,const bool & aIsThirdParty)1821 mozilla::ipc::IPCResult HttpChannelChild::RecvNotifyClassificationFlags(
1822     const uint32_t& aClassificationFlags, const bool& aIsThirdParty) {
1823   LOG(
1824       ("HttpChannelChild::RecvNotifyClassificationFlags thirdparty=%d "
1825        "flags=%" PRIu32 " [this=%p]\n",
1826        static_cast<int>(aIsThirdParty), aClassificationFlags, this));
1827   MOZ_ASSERT(NS_IsMainThread());
1828 
1829   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1830       this, [self = UnsafePtr<HttpChannelChild>(this), aClassificationFlags,
1831              aIsThirdParty] {
1832         self->AddClassificationFlags(aClassificationFlags, aIsThirdParty);
1833       }));
1834 
1835   return IPC_OK();
1836 }
1837 
RecvNotifyFlashPluginStateChanged(const nsIHttpChannel::FlashPluginState & aState)1838 mozilla::ipc::IPCResult HttpChannelChild::RecvNotifyFlashPluginStateChanged(
1839     const nsIHttpChannel::FlashPluginState& aState) {
1840   LOG(("HttpChannelChild::RecvNotifyFlashPluginStateChanged [this=%p]\n",
1841        this));
1842   MOZ_ASSERT(NS_IsMainThread());
1843 
1844   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1845       this, [self = UnsafePtr<HttpChannelChild>(this), aState] {
1846         self->SetFlashPluginState(aState);
1847       }));
1848 
1849   return IPC_OK();
1850 }
1851 
FlushedForDiversion()1852 void HttpChannelChild::FlushedForDiversion() {
1853   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
1854   MOZ_RELEASE_ASSERT(mDivertingToParent);
1855 
1856   // Once this is set, it should not be unset before HttpChannelChild is taken
1857   // down. After it is set, no OnStart/OnData/OnStop callbacks should be
1858   // received from the parent channel, nor dequeued from the ChannelEventQueue.
1859   mFlushedForDiversion = true;
1860 
1861   // If we're synthesized, it's up to the SyntheticDiversionListener to invoke
1862   // SendDivertComplete after it has sent the DivertOnStopRequestMessage.
1863   if (!mSynthesizedResponse) {
1864     SendDivertComplete();
1865   }
1866 }
1867 
RecvSetClassifierMatchedInfo(const ClassifierInfo & aInfo)1868 mozilla::ipc::IPCResult HttpChannelChild::RecvSetClassifierMatchedInfo(
1869     const ClassifierInfo& aInfo) {
1870   LOG(("HttpChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n", this));
1871   MOZ_ASSERT(NS_IsMainThread());
1872 
1873   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1874       this, [self = UnsafePtr<HttpChannelChild>(this), aInfo]() {
1875         self->SetMatchedInfo(aInfo.list(), aInfo.provider(), aInfo.fullhash());
1876       }));
1877 
1878   return IPC_OK();
1879 }
1880 
RecvSetClassifierMatchedTrackingInfo(const ClassifierInfo & aInfo)1881 mozilla::ipc::IPCResult HttpChannelChild::RecvSetClassifierMatchedTrackingInfo(
1882     const ClassifierInfo& aInfo) {
1883   LOG(("HttpChannelChild::RecvSetClassifierMatchedTrackingInfo [this=%p]\n",
1884        this));
1885   MOZ_ASSERT(NS_IsMainThread());
1886 
1887   nsTArray<nsCString> lists, fullhashes;
1888   for (const nsACString& token : aInfo.list().Split(',')) {
1889     lists.AppendElement(token);
1890   }
1891   for (const nsACString& token : aInfo.fullhash().Split(',')) {
1892     fullhashes.AppendElement(token);
1893   }
1894 
1895   mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1896       this, [self = UnsafePtr<HttpChannelChild>(this),
1897              lists = CopyableTArray{std::move(lists)},
1898              fullhashes = CopyableTArray{std::move(fullhashes)}]() {
1899         self->SetMatchedTrackingInfo(lists, fullhashes);
1900       }));
1901 
1902   return IPC_OK();
1903 }
1904 
ProcessDivertMessages()1905 void HttpChannelChild::ProcessDivertMessages() {
1906   LOG(("HttpChannelChild::ProcessDivertMessages [this=%p]\n", this));
1907   MOZ_ASSERT(OnSocketThread());
1908   MOZ_RELEASE_ASSERT(mDivertingToParent);
1909 
1910   // DivertTo() has been called on parent, so we can now start sending queued
1911   // IPDL messages back to parent listener.
1912   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
1913   MOZ_ASSERT(neckoTarget);
1914   nsresult rv =
1915       neckoTarget->Dispatch(NewRunnableMethod("HttpChannelChild::Resume", this,
1916                                               &HttpChannelChild::Resume),
1917                             NS_DISPATCH_NORMAL);
1918 
1919   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
1920 }
1921 
1922 // Returns true if has actually completed the redirect and cleaned up the
1923 // channel, or false the interception logic kicked in and we need to asyncly
1924 // call FinishInterceptedRedirect and CleanupRedirectingChannel.
1925 // The argument is an optional OverrideRunnable that we pass to the redirected
1926 // channel.
Redirect3Complete(OverrideRunnable * aRunnable)1927 bool HttpChannelChild::Redirect3Complete(OverrideRunnable* aRunnable) {
1928   LOG(("HttpChannelChild::Redirect3Complete [this=%p]\n", this));
1929   MOZ_ASSERT(NS_IsMainThread());
1930   nsresult rv = NS_OK;
1931 
1932   nsCOMPtr<nsIRedirectResultListener> vetoHook;
1933   GetCallback(vetoHook);
1934   if (vetoHook) {
1935     vetoHook->OnRedirectResult(true);
1936   }
1937 
1938   nsCOMPtr<nsIHttpChannelChild> chan = do_QueryInterface(mRedirectChannelChild);
1939   RefPtr<HttpChannelChild> httpChannelChild =
1940       static_cast<HttpChannelChild*>(chan.get());
1941   // Chrome channel has been AsyncOpen'd.  Reflect this in child.
1942   if (mRedirectChannelChild) {
1943     if (httpChannelChild) {
1944       httpChannelChild->mOverrideRunnable = aRunnable;
1945       httpChannelChild->mInterceptingChannel = this;
1946     }
1947     rv = mRedirectChannelChild->CompleteRedirectSetup(mListener);
1948 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1949     mSuccesfullyRedirected = NS_SUCCEEDED(rv);
1950 #endif
1951   }
1952 
1953   if (!httpChannelChild || !httpChannelChild->mShouldParentIntercept) {
1954     // The redirect channel either isn't a HttpChannelChild, or the interception
1955     // logic wasn't triggered, so we can clean it up right here.
1956     CleanupRedirectingChannel(rv);
1957     if (httpChannelChild) {
1958       httpChannelChild->mOverrideRunnable = nullptr;
1959       httpChannelChild->mInterceptingChannel = nullptr;
1960     }
1961     return true;
1962   }
1963   return false;
1964 }
1965 
RecvCancelRedirected()1966 mozilla::ipc::IPCResult HttpChannelChild::RecvCancelRedirected() {
1967   CleanupRedirectingChannel(NS_BINDING_REDIRECTED);
1968   return IPC_OK();
1969 }
1970 
CleanupRedirectingChannel(nsresult rv)1971 void HttpChannelChild::CleanupRedirectingChannel(nsresult rv) {
1972   // Redirecting to new channel: shut this down and init new channel
1973   if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED);
1974 
1975   if (NS_SUCCEEDED(rv)) {
1976     nsCString remoteAddress;
1977     Unused << GetRemoteAddress(remoteAddress);
1978     nsCOMPtr<nsIURI> referrer;
1979     if (mReferrerInfo) {
1980       referrer = mReferrerInfo->GetComputedReferrer();
1981     }
1982 
1983     nsCOMPtr<nsIRedirectHistoryEntry> entry =
1984         new nsRedirectHistoryEntry(GetURIPrincipal(), referrer, remoteAddress);
1985 
1986     mLoadInfo->AppendRedirectHistoryEntry(entry, false);
1987   } else {
1988     NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?");
1989   }
1990 
1991   // Release ref to new channel.
1992   mRedirectChannelChild = nullptr;
1993 
1994   if (mInterceptListener) {
1995     mInterceptListener->Cleanup();
1996     mInterceptListener = nullptr;
1997   }
1998   ReleaseListeners();
1999 }
2000 
2001 //-----------------------------------------------------------------------------
2002 // HttpChannelChild::nsIChildChannel
2003 //-----------------------------------------------------------------------------
2004 
2005 NS_IMETHODIMP
ConnectParent(uint32_t registrarId)2006 HttpChannelChild::ConnectParent(uint32_t registrarId) {
2007   LOG(("HttpChannelChild::ConnectParent [this=%p, id=%" PRIu32 "]\n", this,
2008        registrarId));
2009   MOZ_ASSERT(NS_IsMainThread());
2010   mozilla::dom::BrowserChild* browserChild = nullptr;
2011   nsCOMPtr<nsIBrowserChild> iBrowserChild;
2012   GetCallback(iBrowserChild);
2013   if (iBrowserChild) {
2014     browserChild =
2015         static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
2016   }
2017 
2018   if (browserChild && !browserChild->IPCOpen()) {
2019     return NS_ERROR_FAILURE;
2020   }
2021 
2022   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
2023   if (cc->IsShuttingDown()) {
2024     return NS_ERROR_FAILURE;
2025   }
2026 
2027   HttpBaseChannel::SetDocshellUserAgentOverride();
2028 
2029   // This must happen before the constructor message is sent. Otherwise messages
2030   // from the parent could arrive quickly and be delivered to the wrong event
2031   // target.
2032   SetEventTarget();
2033 
2034   HttpChannelConnectArgs connectArgs(registrarId, mShouldParentIntercept);
2035   if (!gNeckoChild->SendPHttpChannelConstructor(
2036           this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
2037     return NS_ERROR_FAILURE;
2038   }
2039 
2040   {
2041     MutexAutoLock lock(mBgChildMutex);
2042 
2043     MOZ_ASSERT(!mBgChild);
2044     MOZ_ASSERT(!mBgInitFailCallback);
2045 
2046     mBgInitFailCallback = NewRunnableMethod<nsresult>(
2047         "HttpChannelChild::OnRedirectVerifyCallback", this,
2048         &HttpChannelChild::OnRedirectVerifyCallback, NS_ERROR_FAILURE);
2049 
2050     RefPtr<HttpBackgroundChannelChild> bgChild =
2051         new HttpBackgroundChannelChild();
2052 
2053     MOZ_RELEASE_ASSERT(gSocketTransportService);
2054 
2055     RefPtr<HttpChannelChild> self = this;
2056     nsresult rv = gSocketTransportService->Dispatch(
2057         NewRunnableMethod<RefPtr<HttpChannelChild>>(
2058             "HttpBackgroundChannelChild::Init", bgChild,
2059             &HttpBackgroundChannelChild::Init, std::move(self)),
2060         NS_DISPATCH_NORMAL);
2061 
2062     if (NS_WARN_IF(NS_FAILED(rv))) {
2063       return rv;
2064     }
2065 
2066     mBgChild = std::move(bgChild);
2067   }
2068 
2069   MaybeConnectToSocketProcess();
2070 
2071   return NS_OK;
2072 }
2073 
2074 NS_IMETHODIMP
CompleteRedirectSetup(nsIStreamListener * aListener)2075 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
2076   LOG(("HttpChannelChild::FinishRedirectSetup [this=%p]\n", this));
2077   MOZ_ASSERT(NS_IsMainThread());
2078 
2079   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
2080   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
2081 
2082   if (mShouldParentIntercept) {
2083     // This is a redirected channel, and the corresponding parent channel has
2084     // started AsyncOpen but was intercepted and suspended. We must tear it down
2085     // and start fresh - we will intercept the child channel this time, before
2086     // creating a new parent channel unnecessarily.
2087 
2088     // Since this method is called from RecvRedirect3Complete which itself is
2089     // called from either OnRedirectVerifyCallback via OverrideRunnable, or from
2090     // RecvRedirect3Complete. The order of events must always be:
2091     //  1. Teardown the IPDL connection
2092     //  2. AsyncOpen the connection again
2093     //  3. Cleanup the redirecting channel (the one calling Redirect3Complete)
2094     //  4. [optional] Call OverrideWithSynthesizedResponse on the redirected
2095     //  channel if the call came from OverrideRunnable.
2096     mInterceptedRedirectListener = aListener;
2097     // This will send a message to the parent notifying it that we are closing
2098     // down. After closing the IPC channel, we will proceed to execute
2099     // FinishInterceptedRedirect() which AsyncOpen's the channel again.
2100     SendFinishInterceptedRedirect();
2101 
2102     // XXX valentin: The interception logic should be rewritten to avoid
2103     // calling AsyncOpen on the channel _after_ we call Send__delete__()
2104     return NS_OK;
2105   }
2106 
2107   /*
2108    * No need to check for cancel: we don't get here if nsHttpChannel canceled
2109    * before AsyncOpen(); if it's canceled after that, OnStart/Stop will just
2110    * get called with error code as usual.  So just setup mListener and make the
2111    * channel reflect AsyncOpen'ed state.
2112    */
2113 
2114   mLastStatusReported = TimeStamp::Now();
2115   PROFILER_ADD_NETWORK_MARKER(
2116       mURI, mPriority, mChannelId, NetworkLoadType::LOAD_START,
2117       mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
2118       mLoadInfo->GetInnerWindowID(), nullptr, nullptr);
2119   mIsPending = true;
2120   mWasOpened = true;
2121   mListener = aListener;
2122 
2123   // add ourselves to the load group.
2124   if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
2125 
2126   // We already have an open IPDL connection to the parent. If on-modify-request
2127   // listeners or load group observers canceled us, let the parent handle it
2128   // and send it back to us naturally.
2129   return NS_OK;
2130 }
2131 
2132 //-----------------------------------------------------------------------------
2133 // HttpChannelChild::nsIAsyncVerifyRedirectCallback
2134 //-----------------------------------------------------------------------------
2135 
2136 NS_IMETHODIMP
OnRedirectVerifyCallback(nsresult aResult)2137 HttpChannelChild::OnRedirectVerifyCallback(nsresult aResult) {
2138   LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
2139   MOZ_ASSERT(NS_IsMainThread());
2140   Maybe<URIParams> redirectURI;
2141   nsresult rv;
2142 
2143   nsCOMPtr<nsIHttpChannel> newHttpChannel =
2144       do_QueryInterface(mRedirectChannelChild);
2145 
2146   if (NS_SUCCEEDED(aResult) && !mRedirectChannelChild) {
2147     // mRedirectChannelChild doesn't exist means we're redirecting to a protocol
2148     // that doesn't implement nsIChildChannel. The redirect result should be set
2149     // as failed by veto listeners and shouldn't enter this condition. As the
2150     // last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
2151     // to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
2152     // another protocol and throw an error.
2153     LOG(("  redirecting to a protocol that doesn't implement nsIChildChannel"));
2154     aResult = NS_ERROR_DOM_BAD_URI;
2155   }
2156 
2157   nsCOMPtr<nsIReferrerInfo> referrerInfo;
2158   if (newHttpChannel) {
2159     // Must not be called until after redirect observers called.
2160     newHttpChannel->SetOriginalURI(mOriginalURI);
2161     referrerInfo = newHttpChannel->GetReferrerInfo();
2162   }
2163 
2164   if (mRedirectingForSubsequentSynthesizedResponse) {
2165     nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
2166         do_QueryInterface(mRedirectChannelChild);
2167     RefPtr<HttpChannelChild> redirectedChannel =
2168         static_cast<HttpChannelChild*>(httpChannelChild.get());
2169     // redirectChannel will be NULL if mRedirectChannelChild isn't a
2170     // nsIHttpChannelChild (it could be a DataChannelChild).
2171 
2172     RefPtr<InterceptStreamListener> streamListener =
2173         new InterceptStreamListener(redirectedChannel, nullptr);
2174 
2175     nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
2176     MOZ_ASSERT(neckoTarget);
2177 
2178     nsCOMPtr<nsIInterceptedBodyCallback> callback =
2179         std::move(mSynthesizedCallback);
2180 
2181     Unused << neckoTarget->Dispatch(
2182         new OverrideRunnable(this, redirectedChannel, streamListener,
2183                              mSynthesizedInput, callback,
2184                              std::move(mResponseHead), mSynthesizedCacheInfo),
2185         NS_DISPATCH_NORMAL);
2186 
2187     return NS_OK;
2188   }
2189 
2190   RequestHeaderTuples emptyHeaders;
2191   RequestHeaderTuples* headerTuples = &emptyHeaders;
2192   nsLoadFlags loadFlags = 0;
2193   Maybe<CorsPreflightArgs> corsPreflightArgs;
2194 
2195   nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild =
2196       do_QueryInterface(mRedirectChannelChild);
2197   if (newHttpChannelChild && NS_SUCCEEDED(aResult)) {
2198     rv = newHttpChannelChild->AddCookiesToRequest();
2199     MOZ_ASSERT(NS_SUCCEEDED(rv));
2200     rv = newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples);
2201     MOZ_ASSERT(NS_SUCCEEDED(rv));
2202     newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs);
2203   }
2204 
2205   /* If the redirect was canceled, bypass OMR and send an empty API
2206    * redirect URI */
2207   SerializeURI(nullptr, redirectURI);
2208 
2209   if (NS_SUCCEEDED(aResult)) {
2210     // Note: this is where we would notify "http-on-modify-response" observers.
2211     // We have deliberately disabled this for child processes (see bug 806753)
2212     //
2213     // After we verify redirect, nsHttpChannel may hit the network: must give
2214     // "http-on-modify-request" observers the chance to cancel before that.
2215     // base->CallOnModifyRequestObservers();
2216 
2217     nsCOMPtr<nsIHttpChannelInternal> newHttpChannelInternal =
2218         do_QueryInterface(mRedirectChannelChild);
2219     if (newHttpChannelInternal) {
2220       nsCOMPtr<nsIURI> apiRedirectURI;
2221       rv = newHttpChannelInternal->GetApiRedirectToURI(
2222           getter_AddRefs(apiRedirectURI));
2223       if (NS_SUCCEEDED(rv) && apiRedirectURI) {
2224         /* If there was an API redirect of this channel, we need to send it
2225          * up here, since it can't be sent via SendAsyncOpen. */
2226         SerializeURI(apiRedirectURI, redirectURI);
2227       }
2228     }
2229 
2230     nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
2231     if (request) {
2232       request->GetLoadFlags(&loadFlags);
2233     }
2234   }
2235 
2236   MaybeCallSynthesizedCallback();
2237 
2238   bool chooseAppcache = false;
2239   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
2240       do_QueryInterface(newHttpChannel);
2241   if (appCacheChannel) {
2242     appCacheChannel->GetChooseApplicationCache(&chooseAppcache);
2243   }
2244 
2245   uint32_t sourceRequestBlockingReason = 0;
2246   mLoadInfo->GetRequestBlockingReason(&sourceRequestBlockingReason);
2247 
2248   Maybe<ChildLoadInfoForwarderArgs> targetLoadInfoForwarder;
2249   nsCOMPtr<nsIChannel> newChannel = do_QueryInterface(mRedirectChannelChild);
2250   if (newChannel) {
2251     ChildLoadInfoForwarderArgs args;
2252     nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo();
2253     LoadInfoToChildLoadInfoForwarder(loadInfo, &args);
2254     targetLoadInfoForwarder.emplace(args);
2255   }
2256 
2257   if (CanSend())
2258     SendRedirect2Verify(aResult, *headerTuples, sourceRequestBlockingReason,
2259                         targetLoadInfoForwarder, loadFlags, referrerInfo,
2260                         redirectURI, corsPreflightArgs, chooseAppcache);
2261 
2262   return NS_OK;
2263 }
2264 
2265 //-----------------------------------------------------------------------------
2266 // HttpChannelChild::nsIRequest
2267 //-----------------------------------------------------------------------------
2268 
2269 NS_IMETHODIMP
Cancel(nsresult aStatus)2270 HttpChannelChild::Cancel(nsresult aStatus) {
2271   LOG(("HttpChannelChild::Cancel [this=%p, status=%" PRIx32 "]\n", this,
2272        static_cast<uint32_t>(aStatus)));
2273   LogCallingScriptLocation(this);
2274 
2275   MOZ_ASSERT(NS_IsMainThread());
2276 
2277   if (!mCanceled) {
2278     // If this cancel occurs before nsHttpChannel has been set up, AsyncOpen
2279     // is responsible for cleaning up.
2280     mCanceled = true;
2281     mStatus = aStatus;
2282     if (RemoteChannelExists()) {
2283       SendCancel(aStatus, mLoadInfo->GetRequestBlockingReason());
2284     }
2285 
2286     // If the channel is intercepted and already pumping, then just
2287     // cancel the pump.  This will call OnStopRequest().
2288     if (mSynthesizedResponsePump) {
2289       mSynthesizedResponsePump->Cancel(aStatus);
2290     }
2291 
2292     // If we are canceled while intercepting, but not yet pumping, then
2293     // we must call AsyncAbort() to trigger OnStopRequest().
2294     else if (mInterceptListener) {
2295       mInterceptListener->Cleanup();
2296       mInterceptListener = nullptr;
2297       Unused << AsyncAbort(aStatus);
2298     }
2299   }
2300   return NS_OK;
2301 }
2302 
2303 NS_IMETHODIMP
Suspend()2304 HttpChannelChild::Suspend() {
2305   LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 ", "
2306        "mDivertingToParent=%d]\n",
2307        this, mSuspendCount + 1, static_cast<bool>(mDivertingToParent)));
2308   MOZ_ASSERT(NS_IsMainThread());
2309   NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
2310                  NS_ERROR_NOT_AVAILABLE);
2311 
2312   // SendSuspend only once, when suspend goes from 0 to 1.
2313   // Don't SendSuspend at all if we're diverting callbacks to the parent;
2314   // suspend will be called at the correct time in the parent itself.
2315   if (!mSuspendCount++ && !mDivertingToParent) {
2316     if (RemoteChannelExists()) {
2317       SendSuspend();
2318       mSuspendSent = true;
2319     }
2320   }
2321   if (mSynthesizedResponsePump) {
2322     mSynthesizedResponsePump->Suspend();
2323   }
2324   mEventQ->Suspend();
2325 
2326   return NS_OK;
2327 }
2328 
2329 NS_IMETHODIMP
Resume()2330 HttpChannelChild::Resume() {
2331   LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 ", "
2332        "mDivertingToParent=%d]\n",
2333        this, mSuspendCount - 1, static_cast<bool>(mDivertingToParent)));
2334   MOZ_ASSERT(NS_IsMainThread());
2335   NS_ENSURE_TRUE(RemoteChannelExists() || mInterceptListener,
2336                  NS_ERROR_NOT_AVAILABLE);
2337   NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
2338 
2339   nsresult rv = NS_OK;
2340 
2341   // SendResume only once, when suspend count drops to 0.
2342   // Don't SendResume at all if we're diverting callbacks to the parent (unless
2343   // suspend was sent earlier); otherwise, resume will be called at the correct
2344   // time in the parent itself.
2345   if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) {
2346     if (RemoteChannelExists()) {
2347       SendResume();
2348     }
2349     if (mCallOnResume) {
2350       nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
2351       MOZ_ASSERT(neckoTarget);
2352 
2353       RefPtr<HttpChannelChild> self = this;
2354       std::function<nsresult(HttpChannelChild*)> callOnResume = nullptr;
2355       std::swap(callOnResume, mCallOnResume);
2356       rv = neckoTarget->Dispatch(
2357           NS_NewRunnableFunction(
2358               "net::HttpChannelChild::mCallOnResume",
2359               [callOnResume, self{std::move(self)}]() { callOnResume(self); }),
2360           NS_DISPATCH_NORMAL);
2361     }
2362   }
2363   if (mSynthesizedResponsePump) {
2364     mSynthesizedResponsePump->Resume();
2365   }
2366   mEventQ->Resume();
2367 
2368   return rv;
2369 }
2370 
2371 //-----------------------------------------------------------------------------
2372 // HttpChannelChild::nsIChannel
2373 //-----------------------------------------------------------------------------
2374 
2375 NS_IMETHODIMP
GetSecurityInfo(nsISupports ** aSecurityInfo)2376 HttpChannelChild::GetSecurityInfo(nsISupports** aSecurityInfo) {
2377   NS_ENSURE_ARG_POINTER(aSecurityInfo);
2378   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
2379   return NS_OK;
2380 }
2381 
2382 NS_IMETHODIMP
AsyncOpen(nsIStreamListener * aListener)2383 HttpChannelChild::AsyncOpen(nsIStreamListener* aListener) {
2384   LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
2385 
2386   nsresult rv = AsyncOpenInternal(aListener);
2387   if (NS_FAILED(rv)) {
2388     uint32_t blockingReason = 0;
2389     mLoadInfo->GetRequestBlockingReason(&blockingReason);
2390     LOG(
2391         ("HttpChannelChild::AsyncOpen failed [this=%p rv=0x%08x "
2392          "blocking-reason=%u]\n",
2393          this, static_cast<uint32_t>(rv), blockingReason));
2394 
2395     gHttpHandler->OnFailedOpeningRequest(this);
2396   }
2397 
2398 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2399   mAsyncOpenSucceeded = NS_SUCCEEDED(rv);
2400 #endif
2401   return rv;
2402 }
2403 
AsyncOpenInternal(nsIStreamListener * aListener)2404 nsresult HttpChannelChild::AsyncOpenInternal(nsIStreamListener* aListener) {
2405   nsresult rv;
2406 
2407   nsCOMPtr<nsIStreamListener> listener = aListener;
2408   rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
2409   if (NS_WARN_IF(NS_FAILED(rv))) {
2410     ReleaseListeners();
2411     return rv;
2412   }
2413 
2414   MOZ_ASSERT(
2415       mLoadInfo->GetSecurityMode() == 0 ||
2416           mLoadInfo->GetInitialSecurityCheckDone() ||
2417           (mLoadInfo->GetSecurityMode() ==
2418                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
2419            mLoadInfo->GetLoadingPrincipal() &&
2420            mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
2421       "security flags in loadInfo but doContentSecurityCheck() not called");
2422 
2423   LogCallingScriptLocation(this);
2424 
2425   if (!mLoadGroup && !mCallbacks) {
2426     // If no one called SetLoadGroup or SetNotificationCallbacks, the private
2427     // state has not been updated on PrivateBrowsingChannel (which we derive
2428     // from) Hence, we have to call UpdatePrivateBrowsing() here
2429     UpdatePrivateBrowsing();
2430   }
2431 
2432 #ifdef DEBUG
2433   AssertPrivateBrowsingId();
2434 #endif
2435 
2436   if (mCanceled) {
2437     ReleaseListeners();
2438     return mStatus;
2439   }
2440 
2441   NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
2442   NS_ENSURE_ARG_POINTER(listener);
2443   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
2444   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
2445 
2446   if (MaybeWaitForUploadStreamLength(listener, nullptr)) {
2447     return NS_OK;
2448   }
2449 
2450   if (!mAsyncOpenTimeOverriden) {
2451     mAsyncOpenTime = TimeStamp::Now();
2452   }
2453 
2454 #ifdef MOZ_TASK_TRACER
2455   if (tasktracer::IsStartLogging()) {
2456     nsCOMPtr<nsIURI> uri;
2457     GetURI(getter_AddRefs(uri));
2458     nsAutoCString urispec;
2459     uri->GetSpec(urispec);
2460     tasktracer::AddLabel("HttpChannelChild::AsyncOpen %s", urispec.get());
2461   }
2462 #endif
2463 
2464   // Port checked in parent, but duplicate here so we can return with error
2465   // immediately
2466   rv = NS_CheckPortSafety(mURI);
2467   if (NS_FAILED(rv)) {
2468     ReleaseListeners();
2469     return rv;
2470   }
2471 
2472   nsAutoCString cookie;
2473   if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookie))) {
2474     mUserSetCookieHeader = cookie;
2475   }
2476 
2477   DebugOnly<nsresult> check = AddCookiesToRequest();
2478   MOZ_ASSERT(NS_SUCCEEDED(check));
2479 
2480   //
2481   // NOTE: From now on we must return NS_OK; all errors must be handled via
2482   // OnStart/OnStopRequest
2483   //
2484 
2485   // We notify "http-on-opening-request" observers in the child
2486   // process so that devtools can capture a stack trace at the
2487   // appropriate spot.  See bug 806753 for some information about why
2488   // other http-* notifications are disabled in child processes.
2489   gHttpHandler->OnOpeningRequest(this);
2490 
2491   mLastStatusReported = TimeStamp::Now();
2492   PROFILER_ADD_NETWORK_MARKER(
2493       mURI, mPriority, mChannelId, NetworkLoadType::LOAD_START,
2494       mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
2495       mLoadInfo->GetInnerWindowID(), nullptr, nullptr);
2496 
2497   mIsPending = true;
2498   mWasOpened = true;
2499   mListener = listener;
2500 
2501   // add ourselves to the load group.
2502   if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
2503 
2504   if (mCanceled) {
2505     // We may have been canceled already, either by on-modify-request
2506     // listeners or by load group observers; in that case, don't create IPDL
2507     // connection. See nsHttpChannel::AsyncOpen().
2508     ReleaseListeners();
2509     return mStatus;
2510   }
2511 
2512   // Set user agent override from docshell
2513   HttpBaseChannel::SetDocshellUserAgentOverride();
2514 
2515   MOZ_ASSERT_IF(mPostRedirectChannelShouldUpgrade,
2516                 mPostRedirectChannelShouldIntercept);
2517   bool shouldUpgrade = mPostRedirectChannelShouldUpgrade;
2518   if (mPostRedirectChannelShouldIntercept ||
2519       ShouldInterceptURI(mURI, shouldUpgrade)) {
2520     RefPtr<HttpChannelChild> self = this;
2521 
2522     std::function<void(bool)> callback = [self,
2523                                           shouldUpgrade](bool aStorageAllowed) {
2524       if (!aStorageAllowed) {
2525         nsresult rv = self->ContinueAsyncOpen();
2526         if (NS_WARN_IF(NS_FAILED(rv))) {
2527           Unused << self->AsyncAbort(rv);
2528         }
2529         return;
2530       }
2531 
2532       self->mResponseCouldBeSynthesized = true;
2533 
2534       nsCOMPtr<nsINetworkInterceptController> controller;
2535       self->GetCallback(controller);
2536 
2537       self->mInterceptListener = new InterceptStreamListener(self, nullptr);
2538 
2539       RefPtr<InterceptedChannelContent> intercepted =
2540           new InterceptedChannelContent(
2541               self, controller, self->mInterceptListener, shouldUpgrade);
2542       intercepted->NotifyController();
2543     };
2544 
2545     ClassifierDummyChannel::StorageAllowedState state =
2546         ClassifierDummyChannel::StorageAllowed(this, callback);
2547     if (state == ClassifierDummyChannel::eStorageGranted) {
2548       callback(true);
2549       return NS_OK;
2550     }
2551 
2552     if (state == ClassifierDummyChannel::eAsyncNeeded) {
2553       // The async callback will be executed eventually.
2554       return NS_OK;
2555     }
2556 
2557     MOZ_ASSERT(state == ClassifierDummyChannel::eStorageDenied);
2558     // Fall through
2559   }
2560 
2561   rv = ContinueAsyncOpen();
2562   if (NS_FAILED(rv)) {
2563     ReleaseListeners();
2564   }
2565   return rv;
2566 }
2567 
2568 // Assigns an nsIEventTarget to our IPDL actor so that IPC messages are sent to
2569 // the correct DocGroup/TabGroup.
SetEventTarget()2570 void HttpChannelChild::SetEventTarget() {
2571   nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
2572 
2573   nsCOMPtr<nsIEventTarget> target =
2574       nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network);
2575 
2576   if (!target) {
2577     return;
2578   }
2579 
2580   gNeckoChild->SetEventTargetForActor(this, target);
2581 
2582   {
2583     MutexAutoLock lock(mEventTargetMutex);
2584     mNeckoTarget = target;
2585   }
2586 }
2587 
GetNeckoTarget()2588 already_AddRefed<nsIEventTarget> HttpChannelChild::GetNeckoTarget() {
2589   nsCOMPtr<nsIEventTarget> target;
2590   {
2591     MutexAutoLock lock(mEventTargetMutex);
2592     target = mNeckoTarget;
2593   }
2594 
2595   if (!target) {
2596     target = GetMainThreadEventTarget();
2597   }
2598   return target.forget();
2599 }
2600 
GetODATarget()2601 already_AddRefed<nsIEventTarget> HttpChannelChild::GetODATarget() {
2602   nsCOMPtr<nsIEventTarget> target;
2603   {
2604     MutexAutoLock lock(mEventTargetMutex);
2605     target = mODATarget ? mODATarget : mNeckoTarget;
2606   }
2607 
2608   if (!target) {
2609     target = GetMainThreadEventTarget();
2610   }
2611   return target.forget();
2612 }
2613 
ContinueAsyncOpen()2614 nsresult HttpChannelChild::ContinueAsyncOpen() {
2615   nsresult rv;
2616   nsCString appCacheClientId;
2617   if (mInheritApplicationCache) {
2618     // Pick up an application cache from the notification
2619     // callbacks if available
2620     nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
2621     GetCallback(appCacheContainer);
2622 
2623     if (appCacheContainer) {
2624       nsCOMPtr<nsIApplicationCache> appCache;
2625       nsresult rv =
2626           appCacheContainer->GetApplicationCache(getter_AddRefs(appCache));
2627       if (NS_SUCCEEDED(rv) && appCache) {
2628         appCache->GetClientID(appCacheClientId);
2629       }
2630     }
2631   }
2632 
2633   //
2634   // Send request to the chrome process...
2635   //
2636 
2637   mozilla::dom::BrowserChild* browserChild = nullptr;
2638   nsCOMPtr<nsIBrowserChild> iBrowserChild;
2639   GetCallback(iBrowserChild);
2640   if (iBrowserChild) {
2641     browserChild =
2642         static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
2643   }
2644 
2645   // This id identifies the inner window's top-level document,
2646   // which changes on every new load or navigation.
2647   uint64_t contentWindowId = 0;
2648   TimeStamp navigationStartTimeStamp;
2649   if (browserChild) {
2650     MOZ_ASSERT(browserChild->WebNavigation());
2651     if (RefPtr<Document> document = browserChild->GetTopLevelDocument()) {
2652       contentWindowId = document->InnerWindowID();
2653       nsDOMNavigationTiming* navigationTiming = document->GetNavigationTiming();
2654       if (navigationTiming) {
2655         navigationStartTimeStamp =
2656             navigationTiming->GetNavigationStartTimeStamp();
2657       }
2658       mTopLevelOuterContentWindowId = document->OuterWindowID();
2659     }
2660   }
2661   SetTopLevelContentWindowId(contentWindowId);
2662 
2663   HttpChannelOpenArgs openArgs;
2664   // No access to HttpChannelOpenArgs members, but they each have a
2665   // function with the struct name that returns a ref.
2666   SerializeURI(mURI, openArgs.uri());
2667   SerializeURI(mOriginalURI, openArgs.original());
2668   SerializeURI(mDocumentURI, openArgs.doc());
2669   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
2670   openArgs.loadFlags() = mLoadFlags;
2671   openArgs.requestHeaders() = mClientSetRequestHeaders;
2672   mRequestHead.Method(openArgs.requestMethod());
2673   openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes.Clone();
2674   openArgs.referrerInfo() = mReferrerInfo;
2675 
2676   AutoIPCStream autoStream(openArgs.uploadStream());
2677   if (mUploadStream) {
2678     autoStream.Serialize(mUploadStream, ContentChild::GetSingleton());
2679     autoStream.TakeOptionalValue();
2680   }
2681 
2682   if (mResponseHead) {
2683     openArgs.synthesizedResponseHead() = Some(*mResponseHead);
2684     openArgs.suspendAfterSynthesizeResponse() =
2685         mSuspendParentAfterSynthesizeResponse;
2686   } else {
2687     openArgs.suspendAfterSynthesizeResponse() = false;
2688   }
2689 
2690   nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(mSecurityInfo);
2691   if (secInfoSer) {
2692     NS_SerializeToString(secInfoSer,
2693                          openArgs.synthesizedSecurityInfoSerialization());
2694   }
2695 
2696   Maybe<CorsPreflightArgs> optionalCorsPreflightArgs;
2697   GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs);
2698 
2699   // NB: This call forces us to cache mTopWindowURI if we haven't already.
2700   nsCOMPtr<nsIURI> uri;
2701   GetTopWindowURI(mURI, getter_AddRefs(uri));
2702 
2703   SerializeURI(mTopWindowURI, openArgs.topWindowURI());
2704 
2705   openArgs.preflightArgs() = optionalCorsPreflightArgs;
2706 
2707   openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders;
2708   openArgs.priority() = mPriority;
2709   openArgs.classOfService() = mClassOfService;
2710   openArgs.redirectionLimit() = mRedirectionLimit;
2711   openArgs.allowSTS() = mAllowSTS;
2712   openArgs.thirdPartyFlags() = mThirdPartyFlags;
2713   openArgs.resumeAt() = mSendResumeAt;
2714   openArgs.startPos() = mStartPos;
2715   openArgs.entityID() = mEntityID;
2716   openArgs.chooseApplicationCache() = mChooseApplicationCache;
2717   openArgs.appCacheClientID() = appCacheClientId;
2718   openArgs.allowSpdy() = mAllowSpdy;
2719   openArgs.allowAltSvc() = mAllowAltSvc;
2720   openArgs.beConservative() = mBeConservative;
2721   openArgs.tlsFlags() = mTlsFlags;
2722   openArgs.initialRwin() = mInitialRwin;
2723 
2724   openArgs.cacheKey() = mCacheKey;
2725 
2726   openArgs.blockAuthPrompt() = mBlockAuthPrompt;
2727 
2728   openArgs.allowStaleCacheContent() = mAllowStaleCacheContent;
2729   openArgs.preferCacheLoadOverBypass() = mPreferCacheLoadOverBypass;
2730 
2731   openArgs.contentTypeHint() = mContentTypeHint;
2732 
2733   rv = mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &openArgs.loadInfo());
2734   NS_ENSURE_SUCCESS(rv, rv);
2735 
2736   EnsureRequestContextID();
2737   openArgs.requestContextID() = mRequestContextID;
2738 
2739   openArgs.corsMode() = mCorsMode;
2740   openArgs.redirectMode() = mRedirectMode;
2741 
2742   openArgs.channelId() = mChannelId;
2743 
2744   openArgs.integrityMetadata() = mIntegrityMetadata;
2745 
2746   openArgs.contentWindowId() = contentWindowId;
2747   openArgs.topLevelOuterContentWindowId() = mTopLevelOuterContentWindowId;
2748 
2749   LOG(("HttpChannelChild::ContinueAsyncOpen this=%p gid=%" PRIu64
2750        " topwinid=%" PRIx64,
2751        this, mChannelId, mTopLevelOuterContentWindowId));
2752 
2753   if (browserChild && !browserChild->IPCOpen()) {
2754     return NS_ERROR_FAILURE;
2755   }
2756 
2757   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
2758   if (cc->IsShuttingDown()) {
2759     return NS_ERROR_FAILURE;
2760   }
2761 
2762   openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
2763   openArgs.launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
2764   openArgs.dispatchFetchEventStart() = mDispatchFetchEventStart;
2765   openArgs.dispatchFetchEventEnd() = mDispatchFetchEventEnd;
2766   openArgs.handleFetchEventStart() = mHandleFetchEventStart;
2767   openArgs.handleFetchEventEnd() = mHandleFetchEventEnd;
2768 
2769   openArgs.forceMainDocumentChannel() = mForceMainDocumentChannel;
2770 
2771   openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
2772   openArgs.hasNonEmptySandboxingFlag() = GetHasNonEmptySandboxingFlag();
2773 
2774   // This must happen before the constructor message is sent. Otherwise messages
2775   // from the parent could arrive quickly and be delivered to the wrong event
2776   // target.
2777   SetEventTarget();
2778 
2779   if (!gNeckoChild->SendPHttpChannelConstructor(
2780           this, browserChild, IPC::SerializedLoadContext(this), openArgs)) {
2781     return NS_ERROR_FAILURE;
2782   }
2783 
2784   {
2785     MutexAutoLock lock(mBgChildMutex);
2786 
2787     MOZ_RELEASE_ASSERT(gSocketTransportService);
2788 
2789     // Service worker might use the same HttpChannelChild to do async open
2790     // twice. Need to disconnect with previous background channel before
2791     // creating the new one, to prevent receiving further notification
2792     // from it.
2793     if (mBgChild) {
2794       RefPtr<HttpBackgroundChannelChild> prevBgChild = std::move(mBgChild);
2795       gSocketTransportService->Dispatch(
2796           NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
2797                             prevBgChild,
2798                             &HttpBackgroundChannelChild::OnChannelClosed),
2799           NS_DISPATCH_NORMAL);
2800     }
2801 
2802     MOZ_ASSERT(!mBgInitFailCallback);
2803 
2804     mBgInitFailCallback = NewRunnableMethod<nsresult>(
2805         "HttpChannelChild::FailedAsyncOpen", this,
2806         &HttpChannelChild::FailedAsyncOpen, NS_ERROR_FAILURE);
2807 
2808     RefPtr<HttpBackgroundChannelChild> bgChild =
2809         new HttpBackgroundChannelChild();
2810 
2811     RefPtr<HttpChannelChild> self = this;
2812     nsresult rv = gSocketTransportService->Dispatch(
2813         NewRunnableMethod<RefPtr<HttpChannelChild>>(
2814             "HttpBackgroundChannelChild::Init", bgChild,
2815             &HttpBackgroundChannelChild::Init, self),
2816         NS_DISPATCH_NORMAL);
2817 
2818     if (NS_WARN_IF(NS_FAILED(rv))) {
2819       return rv;
2820     }
2821 
2822     mBgChild = std::move(bgChild);
2823   }
2824 
2825   MaybeConnectToSocketProcess();
2826 
2827   return NS_OK;
2828 }
2829 
2830 //-----------------------------------------------------------------------------
2831 // HttpChannelChild::nsIHttpChannel
2832 //-----------------------------------------------------------------------------
2833 
2834 NS_IMETHODIMP
SetRequestHeader(const nsACString & aHeader,const nsACString & aValue,bool aMerge)2835 HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
2836                                    const nsACString& aValue, bool aMerge) {
2837   LOG(("HttpChannelChild::SetRequestHeader [this=%p]\n", this));
2838   nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge);
2839   if (NS_FAILED(rv)) return rv;
2840 
2841   RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2842   if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2843 
2844   tuple->mHeader = aHeader;
2845   tuple->mValue = aValue;
2846   tuple->mMerge = aMerge;
2847   tuple->mEmpty = false;
2848   return NS_OK;
2849 }
2850 
2851 NS_IMETHODIMP
SetEmptyRequestHeader(const nsACString & aHeader)2852 HttpChannelChild::SetEmptyRequestHeader(const nsACString& aHeader) {
2853   LOG(("HttpChannelChild::SetEmptyRequestHeader [this=%p]\n", this));
2854   nsresult rv = HttpBaseChannel::SetEmptyRequestHeader(aHeader);
2855   if (NS_FAILED(rv)) return rv;
2856 
2857   RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2858   if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2859 
2860   tuple->mHeader = aHeader;
2861   tuple->mMerge = false;
2862   tuple->mEmpty = true;
2863   return NS_OK;
2864 }
2865 
2866 NS_IMETHODIMP
RedirectTo(nsIURI * newURI)2867 HttpChannelChild::RedirectTo(nsIURI* newURI) {
2868   // disabled until/unless addons run in child or something else needs this
2869   return NS_ERROR_NOT_IMPLEMENTED;
2870 }
2871 
2872 NS_IMETHODIMP
UpgradeToSecure()2873 HttpChannelChild::UpgradeToSecure() {
2874   // disabled until/unless addons run in child or something else needs this
2875   return NS_ERROR_NOT_IMPLEMENTED;
2876 }
2877 
2878 NS_IMETHODIMP
GetProtocolVersion(nsACString & aProtocolVersion)2879 HttpChannelChild::GetProtocolVersion(nsACString& aProtocolVersion) {
2880   aProtocolVersion = mProtocolVersion;
2881   return NS_OK;
2882 }
2883 
2884 //-----------------------------------------------------------------------------
2885 // HttpChannelChild::nsIHttpChannelInternal
2886 //-----------------------------------------------------------------------------
2887 
2888 NS_IMETHODIMP
SetupFallbackChannel(const char * aFallbackKey)2889 HttpChannelChild::SetupFallbackChannel(const char* aFallbackKey) {
2890   DROP_DEAD();
2891 }
2892 
2893 //-----------------------------------------------------------------------------
2894 // HttpChannelChild::nsICacheInfoChannel
2895 //-----------------------------------------------------------------------------
2896 
2897 NS_IMETHODIMP
GetCacheTokenFetchCount(int32_t * _retval)2898 HttpChannelChild::GetCacheTokenFetchCount(int32_t* _retval) {
2899   NS_ENSURE_ARG_POINTER(_retval);
2900   MOZ_ASSERT(NS_IsMainThread());
2901 
2902   if (mSynthesizedCacheInfo) {
2903     return mSynthesizedCacheInfo->GetCacheTokenFetchCount(_retval);
2904   }
2905 
2906   if (!mCacheEntryAvailable && !mAltDataCacheEntryAvailable) {
2907     return NS_ERROR_NOT_AVAILABLE;
2908   }
2909 
2910   *_retval = mCacheFetchCount;
2911   return NS_OK;
2912 }
2913 
2914 NS_IMETHODIMP
GetCacheTokenExpirationTime(uint32_t * _retval)2915 HttpChannelChild::GetCacheTokenExpirationTime(uint32_t* _retval) {
2916   NS_ENSURE_ARG_POINTER(_retval);
2917   MOZ_ASSERT(NS_IsMainThread());
2918 
2919   if (mSynthesizedCacheInfo) {
2920     return mSynthesizedCacheInfo->GetCacheTokenExpirationTime(_retval);
2921   }
2922 
2923   if (!mCacheEntryAvailable) return NS_ERROR_NOT_AVAILABLE;
2924 
2925   *_retval = mCacheExpirationTime;
2926   return NS_OK;
2927 }
2928 
2929 NS_IMETHODIMP
GetCacheTokenCachedCharset(nsACString & _retval)2930 HttpChannelChild::GetCacheTokenCachedCharset(nsACString& _retval) {
2931   MOZ_ASSERT(NS_IsMainThread());
2932   if (mSynthesizedCacheInfo) {
2933     return mSynthesizedCacheInfo->GetCacheTokenCachedCharset(_retval);
2934   }
2935 
2936   if (!mCacheEntryAvailable) return NS_ERROR_NOT_AVAILABLE;
2937 
2938   _retval = mCachedCharset;
2939   return NS_OK;
2940 }
2941 NS_IMETHODIMP
SetCacheTokenCachedCharset(const nsACString & aCharset)2942 HttpChannelChild::SetCacheTokenCachedCharset(const nsACString& aCharset) {
2943   if (mSynthesizedCacheInfo) {
2944     return mSynthesizedCacheInfo->SetCacheTokenCachedCharset(aCharset);
2945   }
2946 
2947   if (!mCacheEntryAvailable || !RemoteChannelExists())
2948     return NS_ERROR_NOT_AVAILABLE;
2949 
2950   mCachedCharset = aCharset;
2951   if (!SendSetCacheTokenCachedCharset(PromiseFlatCString(aCharset))) {
2952     return NS_ERROR_FAILURE;
2953   }
2954   return NS_OK;
2955 }
2956 
2957 NS_IMETHODIMP
IsFromCache(bool * value)2958 HttpChannelChild::IsFromCache(bool* value) {
2959   if (mSynthesizedCacheInfo) {
2960     return mSynthesizedCacheInfo->IsFromCache(value);
2961   }
2962 
2963   if (!mIsPending) return NS_ERROR_NOT_AVAILABLE;
2964 
2965   *value = mIsFromCache;
2966   return NS_OK;
2967 }
2968 
2969 NS_IMETHODIMP
GetCacheEntryId(uint64_t * aCacheEntryId)2970 HttpChannelChild::GetCacheEntryId(uint64_t* aCacheEntryId) {
2971   if (mSynthesizedCacheInfo) {
2972     return mSynthesizedCacheInfo->GetCacheEntryId(aCacheEntryId);
2973   }
2974 
2975   bool fromCache = false;
2976   if (NS_FAILED(IsFromCache(&fromCache)) || !fromCache ||
2977       !mCacheEntryAvailable) {
2978     return NS_ERROR_NOT_AVAILABLE;
2979   }
2980 
2981   *aCacheEntryId = mCacheEntryId;
2982   return NS_OK;
2983 }
2984 
2985 NS_IMETHODIMP
IsRacing(bool * aIsRacing)2986 HttpChannelChild::IsRacing(bool* aIsRacing) {
2987   if (!mAfterOnStartRequestBegun) {
2988     return NS_ERROR_NOT_AVAILABLE;
2989   }
2990   *aIsRacing = mIsRacing;
2991   return NS_OK;
2992 }
2993 
2994 NS_IMETHODIMP
GetCacheKey(uint32_t * cacheKey)2995 HttpChannelChild::GetCacheKey(uint32_t* cacheKey) {
2996   MOZ_ASSERT(NS_IsMainThread());
2997   if (mSynthesizedCacheInfo) {
2998     return mSynthesizedCacheInfo->GetCacheKey(cacheKey);
2999   }
3000 
3001   *cacheKey = mCacheKey;
3002   return NS_OK;
3003 }
3004 NS_IMETHODIMP
SetCacheKey(uint32_t cacheKey)3005 HttpChannelChild::SetCacheKey(uint32_t cacheKey) {
3006   if (mSynthesizedCacheInfo) {
3007     return mSynthesizedCacheInfo->SetCacheKey(cacheKey);
3008   }
3009 
3010   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
3011 
3012   mCacheKey = cacheKey;
3013   return NS_OK;
3014 }
3015 
3016 NS_IMETHODIMP
SetAllowStaleCacheContent(bool aAllowStaleCacheContent)3017 HttpChannelChild::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
3018   if (mSynthesizedCacheInfo) {
3019     return mSynthesizedCacheInfo->SetAllowStaleCacheContent(
3020         aAllowStaleCacheContent);
3021   }
3022 
3023   mAllowStaleCacheContent = aAllowStaleCacheContent;
3024   return NS_OK;
3025 }
3026 NS_IMETHODIMP
GetAllowStaleCacheContent(bool * aAllowStaleCacheContent)3027 HttpChannelChild::GetAllowStaleCacheContent(bool* aAllowStaleCacheContent) {
3028   if (mSynthesizedCacheInfo) {
3029     return mSynthesizedCacheInfo->GetAllowStaleCacheContent(
3030         aAllowStaleCacheContent);
3031   }
3032 
3033   NS_ENSURE_ARG(aAllowStaleCacheContent);
3034   *aAllowStaleCacheContent = mAllowStaleCacheContent;
3035   return NS_OK;
3036 }
3037 
3038 NS_IMETHODIMP
SetPreferCacheLoadOverBypass(bool aPreferCacheLoadOverBypass)3039 HttpChannelChild::SetPreferCacheLoadOverBypass(
3040     bool aPreferCacheLoadOverBypass) {
3041   if (mSynthesizedCacheInfo) {
3042     return mSynthesizedCacheInfo->SetPreferCacheLoadOverBypass(
3043         aPreferCacheLoadOverBypass);
3044   }
3045 
3046   mPreferCacheLoadOverBypass = aPreferCacheLoadOverBypass;
3047   return NS_OK;
3048 }
3049 NS_IMETHODIMP
GetPreferCacheLoadOverBypass(bool * aPreferCacheLoadOverBypass)3050 HttpChannelChild::GetPreferCacheLoadOverBypass(
3051     bool* aPreferCacheLoadOverBypass) {
3052   if (mSynthesizedCacheInfo) {
3053     return mSynthesizedCacheInfo->GetPreferCacheLoadOverBypass(
3054         aPreferCacheLoadOverBypass);
3055   }
3056 
3057   NS_ENSURE_ARG(aPreferCacheLoadOverBypass);
3058   *aPreferCacheLoadOverBypass = mPreferCacheLoadOverBypass;
3059   return NS_OK;
3060 }
3061 
3062 NS_IMETHODIMP
PreferAlternativeDataType(const nsACString & aType,const nsACString & aContentType,bool aDeliverAltData)3063 HttpChannelChild::PreferAlternativeDataType(const nsACString& aType,
3064                                             const nsACString& aContentType,
3065                                             bool aDeliverAltData) {
3066   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
3067 
3068   if (mSynthesizedCacheInfo) {
3069     return mSynthesizedCacheInfo->PreferAlternativeDataType(aType, aContentType,
3070                                                             aDeliverAltData);
3071   }
3072 
3073   mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
3074       nsCString(aType), nsCString(aContentType), aDeliverAltData));
3075   return NS_OK;
3076 }
3077 
3078 const nsTArray<PreferredAlternativeDataTypeParams>&
PreferredAlternativeDataTypes()3079 HttpChannelChild::PreferredAlternativeDataTypes() {
3080   return mPreferredCachedAltDataTypes;
3081 }
3082 
3083 NS_IMETHODIMP
GetAlternativeDataType(nsACString & aType)3084 HttpChannelChild::GetAlternativeDataType(nsACString& aType) {
3085   if (mSynthesizedCacheInfo) {
3086     return mSynthesizedCacheInfo->GetAlternativeDataType(aType);
3087   }
3088 
3089   // Must be called during or after OnStartRequest
3090   if (!mAfterOnStartRequestBegun) {
3091     return NS_ERROR_NOT_AVAILABLE;
3092   }
3093 
3094   aType = mAvailableCachedAltDataType;
3095   return NS_OK;
3096 }
3097 
3098 NS_IMETHODIMP
OpenAlternativeOutputStream(const nsACString & aType,int64_t aPredictedSize,nsIAsyncOutputStream ** _retval)3099 HttpChannelChild::OpenAlternativeOutputStream(const nsACString& aType,
3100                                               int64_t aPredictedSize,
3101                                               nsIAsyncOutputStream** _retval) {
3102   MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
3103 
3104   if (mSynthesizedCacheInfo) {
3105     return mSynthesizedCacheInfo->OpenAlternativeOutputStream(
3106         aType, aPredictedSize, _retval);
3107   }
3108 
3109   if (!CanSend()) {
3110     return NS_ERROR_NOT_AVAILABLE;
3111   }
3112   if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
3113     return NS_ERROR_NOT_AVAILABLE;
3114   }
3115 
3116   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
3117   MOZ_ASSERT(neckoTarget);
3118 
3119   RefPtr<AltDataOutputStreamChild> stream = new AltDataOutputStreamChild();
3120   stream->AddIPDLReference();
3121 
3122   gNeckoChild->SetEventTargetForActor(stream, neckoTarget);
3123 
3124   if (!gNeckoChild->SendPAltDataOutputStreamConstructor(
3125           stream, nsCString(aType), aPredictedSize, this)) {
3126     return NS_ERROR_FAILURE;
3127   }
3128 
3129   stream.forget(_retval);
3130   return NS_OK;
3131 }
3132 
3133 NS_IMETHODIMP
GetOriginalInputStream(nsIInputStreamReceiver * aReceiver)3134 HttpChannelChild::GetOriginalInputStream(nsIInputStreamReceiver* aReceiver) {
3135   if (aReceiver == nullptr) {
3136     return NS_ERROR_INVALID_ARG;
3137   }
3138 
3139   if (!CanSend()) {
3140     return NS_ERROR_NOT_AVAILABLE;
3141   }
3142 
3143   mOriginalInputStreamReceiver = aReceiver;
3144   Unused << SendOpenOriginalCacheInputStream();
3145 
3146   return NS_OK;
3147 }
3148 
3149 NS_IMETHODIMP
GetAltDataInputStream(const nsACString & aType,nsIInputStreamReceiver * aReceiver)3150 HttpChannelChild::GetAltDataInputStream(const nsACString& aType,
3151                                         nsIInputStreamReceiver* aReceiver) {
3152   if (aReceiver == nullptr) {
3153     return NS_ERROR_INVALID_ARG;
3154   }
3155 
3156   if (!CanSend()) {
3157     return NS_ERROR_NOT_AVAILABLE;
3158   }
3159 
3160   mAltDataInputStreamReceiver = aReceiver;
3161   Unused << SendOpenAltDataCacheInputStream(nsCString(aType));
3162 
3163   return NS_OK;
3164 }
3165 
RecvOriginalCacheInputStreamAvailable(const Maybe<IPCStream> & aStream)3166 mozilla::ipc::IPCResult HttpChannelChild::RecvOriginalCacheInputStreamAvailable(
3167     const Maybe<IPCStream>& aStream) {
3168   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
3169   nsCOMPtr<nsIInputStreamReceiver> receiver;
3170   receiver.swap(mOriginalInputStreamReceiver);
3171   if (receiver) {
3172     receiver->OnInputStreamReady(stream);
3173   }
3174 
3175   return IPC_OK();
3176 }
3177 
RecvAltDataCacheInputStreamAvailable(const Maybe<IPCStream> & aStream)3178 mozilla::ipc::IPCResult HttpChannelChild::RecvAltDataCacheInputStreamAvailable(
3179     const Maybe<IPCStream>& aStream) {
3180   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
3181   nsCOMPtr<nsIInputStreamReceiver> receiver;
3182   receiver.swap(mAltDataInputStreamReceiver);
3183   if (receiver) {
3184     receiver->OnInputStreamReady(stream);
3185   }
3186 
3187   return IPC_OK();
3188 }
3189 
3190 mozilla::ipc::IPCResult
RecvOverrideReferrerInfoDuringBeginConnect(nsIReferrerInfo * aReferrerInfo)3191 HttpChannelChild::RecvOverrideReferrerInfoDuringBeginConnect(
3192     nsIReferrerInfo* aReferrerInfo) {
3193   // The arguments passed to SetReferrerInfoInternal here should mirror the
3194   // arguments passed in
3195   // nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown(), except for
3196   // aRespectBeforeConnect which we pass false here since we're intentionally
3197   // overriding the referrer after BeginConnect().
3198   Unused << SetReferrerInfoInternal(aReferrerInfo, false, true, false);
3199 
3200   return IPC_OK();
3201 }
3202 
3203 //-----------------------------------------------------------------------------
3204 // HttpChannelChild::nsIResumableChannel
3205 //-----------------------------------------------------------------------------
3206 
3207 NS_IMETHODIMP
ResumeAt(uint64_t startPos,const nsACString & entityID)3208 HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID) {
3209   LOG(("HttpChannelChild::ResumeAt [this=%p]\n", this));
3210   ENSURE_CALLED_BEFORE_CONNECT();
3211   mStartPos = startPos;
3212   mEntityID = entityID;
3213   mSendResumeAt = true;
3214   return NS_OK;
3215 }
3216 
3217 // GetEntityID is shared in HttpBaseChannel
3218 
3219 //-----------------------------------------------------------------------------
3220 // HttpChannelChild::nsISupportsPriority
3221 //-----------------------------------------------------------------------------
3222 
3223 NS_IMETHODIMP
SetPriority(int32_t aPriority)3224 HttpChannelChild::SetPriority(int32_t aPriority) {
3225   LOG(("HttpChannelChild::SetPriority %p p=%d", this, aPriority));
3226 
3227   int16_t newValue = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX);
3228   if (mPriority == newValue) return NS_OK;
3229   mPriority = newValue;
3230   if (RemoteChannelExists()) SendSetPriority(mPriority);
3231   return NS_OK;
3232 }
3233 
3234 //-----------------------------------------------------------------------------
3235 // HttpChannelChild::nsIClassOfService
3236 //-----------------------------------------------------------------------------
3237 NS_IMETHODIMP
SetClassFlags(uint32_t inFlags)3238 HttpChannelChild::SetClassFlags(uint32_t inFlags) {
3239   if (mClassOfService == inFlags) {
3240     return NS_OK;
3241   }
3242 
3243   mClassOfService = inFlags;
3244 
3245   LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
3246 
3247   if (RemoteChannelExists()) {
3248     SendSetClassOfService(mClassOfService);
3249   }
3250   return NS_OK;
3251 }
3252 
3253 NS_IMETHODIMP
AddClassFlags(uint32_t inFlags)3254 HttpChannelChild::AddClassFlags(uint32_t inFlags) {
3255   mClassOfService |= inFlags;
3256 
3257   LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
3258 
3259   if (RemoteChannelExists()) {
3260     SendSetClassOfService(mClassOfService);
3261   }
3262   return NS_OK;
3263 }
3264 
3265 NS_IMETHODIMP
ClearClassFlags(uint32_t inFlags)3266 HttpChannelChild::ClearClassFlags(uint32_t inFlags) {
3267   mClassOfService &= ~inFlags;
3268 
3269   LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
3270 
3271   if (RemoteChannelExists()) {
3272     SendSetClassOfService(mClassOfService);
3273   }
3274   return NS_OK;
3275 }
3276 
3277 //-----------------------------------------------------------------------------
3278 // HttpChannelChild::nsIProxiedChannel
3279 //-----------------------------------------------------------------------------
3280 
3281 NS_IMETHODIMP
GetProxyInfo(nsIProxyInfo ** aProxyInfo)3282 HttpChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) { DROP_DEAD(); }
3283 
GetHttpProxyConnectResponseCode(int32_t * aResponseCode)3284 NS_IMETHODIMP HttpChannelChild::GetHttpProxyConnectResponseCode(
3285     int32_t* aResponseCode) {
3286   DROP_DEAD();
3287 }
3288 
3289 //-----------------------------------------------------------------------------
3290 // HttpChannelChild::nsIApplicationCacheContainer
3291 //-----------------------------------------------------------------------------
3292 
3293 NS_IMETHODIMP
GetApplicationCache(nsIApplicationCache ** aApplicationCache)3294 HttpChannelChild::GetApplicationCache(nsIApplicationCache** aApplicationCache) {
3295   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3296   return NS_OK;
3297 }
3298 NS_IMETHODIMP
SetApplicationCache(nsIApplicationCache * aApplicationCache)3299 HttpChannelChild::SetApplicationCache(nsIApplicationCache* aApplicationCache) {
3300   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
3301 
3302   mApplicationCache = aApplicationCache;
3303   return NS_OK;
3304 }
3305 
3306 //-----------------------------------------------------------------------------
3307 // HttpChannelChild::nsIApplicationCacheChannel
3308 //-----------------------------------------------------------------------------
3309 
3310 NS_IMETHODIMP
GetApplicationCacheForWrite(nsIApplicationCache ** aApplicationCache)3311 HttpChannelChild::GetApplicationCacheForWrite(
3312     nsIApplicationCache** aApplicationCache) {
3313   *aApplicationCache = nullptr;
3314   return NS_OK;
3315 }
3316 NS_IMETHODIMP
SetApplicationCacheForWrite(nsIApplicationCache * aApplicationCache)3317 HttpChannelChild::SetApplicationCacheForWrite(
3318     nsIApplicationCache* aApplicationCache) {
3319   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
3320 
3321   // Child channels are not intended to be used for cache writes
3322   return NS_ERROR_NOT_IMPLEMENTED;
3323 }
3324 
3325 NS_IMETHODIMP
GetLoadedFromApplicationCache(bool * aLoadedFromApplicationCache)3326 HttpChannelChild::GetLoadedFromApplicationCache(
3327     bool* aLoadedFromApplicationCache) {
3328   *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
3329   return NS_OK;
3330 }
3331 
3332 NS_IMETHODIMP
GetInheritApplicationCache(bool * aInherit)3333 HttpChannelChild::GetInheritApplicationCache(bool* aInherit) {
3334   *aInherit = mInheritApplicationCache;
3335   return NS_OK;
3336 }
3337 NS_IMETHODIMP
SetInheritApplicationCache(bool aInherit)3338 HttpChannelChild::SetInheritApplicationCache(bool aInherit) {
3339   mInheritApplicationCache = aInherit;
3340   return NS_OK;
3341 }
3342 
3343 NS_IMETHODIMP
GetChooseApplicationCache(bool * aChoose)3344 HttpChannelChild::GetChooseApplicationCache(bool* aChoose) {
3345   *aChoose = mChooseApplicationCache;
3346   return NS_OK;
3347 }
3348 
3349 NS_IMETHODIMP
SetChooseApplicationCache(bool aChoose)3350 HttpChannelChild::SetChooseApplicationCache(bool aChoose) {
3351   mChooseApplicationCache = aChoose;
3352   return NS_OK;
3353 }
3354 
3355 NS_IMETHODIMP
MarkOfflineCacheEntryAsForeign()3356 HttpChannelChild::MarkOfflineCacheEntryAsForeign() {
3357   SendMarkOfflineCacheEntryAsForeign();
3358   return NS_OK;
3359 }
3360 
3361 //-----------------------------------------------------------------------------
3362 // HttpChannelChild::nsIHttpChannelChild
3363 //-----------------------------------------------------------------------------
3364 
AddCookiesToRequest()3365 NS_IMETHODIMP HttpChannelChild::AddCookiesToRequest() {
3366   HttpBaseChannel::AddCookiesToRequest();
3367   return NS_OK;
3368 }
3369 
GetClientSetRequestHeaders(RequestHeaderTuples ** aRequestHeaders)3370 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(
3371     RequestHeaderTuples** aRequestHeaders) {
3372   *aRequestHeaders = &mClientSetRequestHeaders;
3373   return NS_OK;
3374 }
3375 
GetClientSetCorsPreflightParameters(Maybe<CorsPreflightArgs> & aArgs)3376 void HttpChannelChild::GetClientSetCorsPreflightParameters(
3377     Maybe<CorsPreflightArgs>& aArgs) {
3378   if (mRequireCORSPreflight) {
3379     CorsPreflightArgs args;
3380     args.unsafeHeaders() = mUnsafeHeaders.Clone();
3381     aArgs.emplace(args);
3382   } else {
3383     aArgs = Nothing();
3384   }
3385 }
3386 
3387 NS_IMETHODIMP
RemoveCorsPreflightCacheEntry(nsIURI * aURI,nsIPrincipal * aPrincipal)3388 HttpChannelChild::RemoveCorsPreflightCacheEntry(nsIURI* aURI,
3389                                                 nsIPrincipal* aPrincipal) {
3390   URIParams uri;
3391   SerializeURI(aURI, uri);
3392   PrincipalInfo principalInfo;
3393   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
3394   if (NS_WARN_IF(NS_FAILED(rv))) {
3395     return rv;
3396   }
3397   bool result = false;
3398   // Be careful to not attempt to send a message to the parent after the
3399   // actor has been destroyed.
3400   if (CanSend()) {
3401     result = SendRemoveCorsPreflightCacheEntry(uri, principalInfo);
3402   }
3403   return result ? NS_OK : NS_ERROR_FAILURE;
3404 }
3405 
3406 //-----------------------------------------------------------------------------
3407 // HttpChannelChild::nsIDivertableChannel
3408 //-----------------------------------------------------------------------------
3409 NS_IMETHODIMP
DivertToParent(ChannelDiverterChild ** aChild)3410 HttpChannelChild::DivertToParent(ChannelDiverterChild** aChild) {
3411   LOG(("HttpChannelChild::DivertToParent [this=%p]\n", this));
3412   MOZ_RELEASE_ASSERT(aChild);
3413   MOZ_RELEASE_ASSERT(gNeckoChild);
3414   MOZ_RELEASE_ASSERT(!mDivertingToParent);
3415   MOZ_ASSERT(NS_IsMainThread());
3416 
3417   if (mMultiPartID) {
3418     return NS_ERROR_NOT_IMPLEMENTED;
3419   }
3420 
3421   nsresult rv = NS_OK;
3422 
3423   // If the channel was intercepted, then we likely do not have an IPC actor
3424   // yet.  We need one, though, in order to have a parent side to divert to.
3425   // Therefore, create the actor just in time for us to suspend and divert it.
3426   if (mSynthesizedResponse && !RemoteChannelExists()) {
3427     mSuspendParentAfterSynthesizeResponse = true;
3428     rv = ContinueAsyncOpen();
3429     NS_ENSURE_SUCCESS(rv, rv);
3430   }
3431 
3432   // We must fail DivertToParent() if there's no parent end of the channel (and
3433   // won't be!) due to early failure.
3434   if (NS_FAILED(mStatus) && !RemoteChannelExists()) {
3435     return mStatus;
3436   }
3437 
3438   // Once this is set, it should not be unset before the child is taken down.
3439   mDivertingToParent = true;
3440 
3441   rv = Suspend();
3442   if (NS_WARN_IF(NS_FAILED(rv))) {
3443     return rv;
3444   }
3445   if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
3446     return NS_ERROR_FAILURE;
3447   }
3448 
3449   HttpChannelDiverterArgs args;
3450   args.mChannelChild() = this;
3451   args.mApplyConversion() = mApplyConversion;
3452 
3453   PChannelDiverterChild* diverter =
3454       gNeckoChild->SendPChannelDiverterConstructor(args);
3455   MOZ_RELEASE_ASSERT(diverter);
3456 
3457   *aChild = static_cast<ChannelDiverterChild*>(diverter);
3458 
3459   return NS_OK;
3460 }
3461 
3462 NS_IMETHODIMP
UnknownDecoderInvolvedKeepData()3463 HttpChannelChild::UnknownDecoderInvolvedKeepData() {
3464   LOG(("HttpChannelChild::UnknownDecoderInvolvedKeepData [this=%p]", this));
3465   MOZ_ASSERT(NS_IsMainThread());
3466 
3467   mUnknownDecoderInvolved = true;
3468   return NS_OK;
3469 }
3470 
3471 NS_IMETHODIMP
UnknownDecoderInvolvedOnStartRequestCalled()3472 HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled() {
3473   LOG(
3474       ("HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled "
3475        "[this=%p, mDivertingToParent=%d]",
3476        this, static_cast<bool>(mDivertingToParent)));
3477   MOZ_ASSERT(NS_IsMainThread());
3478 
3479   mUnknownDecoderInvolved = false;
3480 
3481   if (mDivertingToParent) {
3482     mEventQ->PrependEvents(mUnknownDecoderEventQ);
3483   }
3484   mUnknownDecoderEventQ.Clear();
3485 
3486   return NS_OK;
3487 }
3488 
3489 NS_IMETHODIMP
GetDivertingToParent(bool * aDiverting)3490 HttpChannelChild::GetDivertingToParent(bool* aDiverting) {
3491   NS_ENSURE_ARG_POINTER(aDiverting);
3492   *aDiverting = mDivertingToParent;
3493   return NS_OK;
3494 }
3495 
3496 //-----------------------------------------------------------------------------
3497 // HttpChannelChild::nsIMuliPartChannel
3498 //-----------------------------------------------------------------------------
3499 
3500 NS_IMETHODIMP
GetBaseChannel(nsIChannel ** aBaseChannel)3501 HttpChannelChild::GetBaseChannel(nsIChannel** aBaseChannel) {
3502   if (!mMultiPartID) {
3503     MOZ_ASSERT(false, "Not a multipart channel");
3504     return NS_ERROR_NOT_AVAILABLE;
3505   }
3506   nsCOMPtr<nsIChannel> channel = this;
3507   channel.forget(aBaseChannel);
3508   return NS_OK;
3509 }
3510 
3511 NS_IMETHODIMP
GetPartID(uint32_t * aPartID)3512 HttpChannelChild::GetPartID(uint32_t* aPartID) {
3513   if (!mMultiPartID) {
3514     MOZ_ASSERT(false, "Not a multipart channel");
3515     return NS_ERROR_NOT_AVAILABLE;
3516   }
3517   *aPartID = *mMultiPartID;
3518   return NS_OK;
3519 }
3520 
3521 NS_IMETHODIMP
GetIsLastPart(bool * aIsLastPart)3522 HttpChannelChild::GetIsLastPart(bool* aIsLastPart) {
3523   if (!mMultiPartID) {
3524     return NS_ERROR_NOT_AVAILABLE;
3525   }
3526   *aIsLastPart = mIsLastPartOfMultiPart;
3527   return NS_OK;
3528 }
3529 
3530 //-----------------------------------------------------------------------------
3531 // HttpChannelChild::nsIThreadRetargetableRequest
3532 //-----------------------------------------------------------------------------
3533 
3534 NS_IMETHODIMP
RetargetDeliveryTo(nsIEventTarget * aNewTarget)3535 HttpChannelChild::RetargetDeliveryTo(nsIEventTarget* aNewTarget) {
3536   LOG(("HttpChannelChild::RetargetDeliveryTo [this=%p, aNewTarget=%p]", this,
3537        aNewTarget));
3538 
3539   MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
3540   MOZ_ASSERT(!mODATarget);
3541   MOZ_ASSERT(aNewTarget);
3542 
3543   NS_ENSURE_ARG(aNewTarget);
3544   if (aNewTarget->IsOnCurrentThread()) {
3545     NS_WARNING("Retargeting delivery to same thread");
3546     mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::successMainThread;
3547     return NS_OK;
3548   }
3549 
3550   if (mMultiPartID) {
3551     // TODO: Maybe add a new label for this? Maybe it doesn't
3552     // matter though, since we also blocked QI, so we shouldn't
3553     // ever get here.
3554     mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
3555     return NS_ERROR_NO_INTERFACE;
3556   }
3557 
3558   // Ensure that |mListener| and any subsequent listeners can be retargeted
3559   // to another thread.
3560   nsresult rv = NS_OK;
3561   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
3562       do_QueryInterface(mListener, &rv);
3563   if (!retargetableListener || NS_FAILED(rv)) {
3564     NS_WARNING("Listener is not retargetable");
3565     mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
3566     return NS_ERROR_NO_INTERFACE;
3567   }
3568 
3569   rv = retargetableListener->CheckListenerChain();
3570   if (NS_FAILED(rv)) {
3571     NS_WARNING("Subsequent listeners are not retargetable");
3572     mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListenerChain;
3573     return rv;
3574   }
3575 
3576   {
3577     MutexAutoLock lock(mEventTargetMutex);
3578     mODATarget = aNewTarget;
3579   }
3580 
3581   mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::success;
3582   return NS_OK;
3583 }
3584 
3585 NS_IMETHODIMP
GetDeliveryTarget(nsIEventTarget ** aEventTarget)3586 HttpChannelChild::GetDeliveryTarget(nsIEventTarget** aEventTarget) {
3587   MutexAutoLock lock(mEventTargetMutex);
3588 
3589   nsCOMPtr<nsIEventTarget> target = mODATarget;
3590   if (!mODATarget) {
3591     target = GetCurrentThreadEventTarget();
3592   }
3593   target.forget(aEventTarget);
3594   return NS_OK;
3595 }
3596 
ResetInterception()3597 void HttpChannelChild::ResetInterception() {
3598   NS_ENSURE_TRUE_VOID(gNeckoChild != nullptr);
3599 
3600   if (mInterceptListener) {
3601     mInterceptListener->Cleanup();
3602   }
3603   mInterceptListener = nullptr;
3604 
3605   // The chance to intercept any further requests associated with this channel
3606   // (such as redirects) has passed.
3607   if (mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) {
3608     mLoadFlags |= LOAD_BYPASS_SERVICE_WORKER;
3609   }
3610 
3611   // If the channel has already been aborted or canceled, just stop.
3612   if (NS_FAILED(mStatus)) {
3613     return;
3614   }
3615 
3616   // Continue with the original cross-process request
3617   nsresult rv = ContinueAsyncOpen();
3618   if (NS_WARN_IF(NS_FAILED(rv))) {
3619     Unused << Cancel(rv);
3620   }
3621 }
3622 
TrySendDeletingChannel()3623 void HttpChannelChild::TrySendDeletingChannel() {
3624   AUTO_PROFILER_LABEL("HttpChannelChild::TrySendDeletingChannel", NETWORK);
3625   if (!mDeletingChannelSent.compareExchange(false, true)) {
3626     // SendDeletingChannel is already sent.
3627     return;
3628   }
3629 
3630   if (NS_IsMainThread()) {
3631     if (NS_WARN_IF(!CanSend())) {
3632       // IPC actor is detroyed already, do not send more messages.
3633       return;
3634     }
3635 
3636     Unused << PHttpChannelChild::SendDeletingChannel();
3637     return;
3638   }
3639 
3640   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
3641   MOZ_ASSERT(neckoTarget);
3642 
3643   DebugOnly<nsresult> rv = neckoTarget->Dispatch(
3644       NewNonOwningRunnableMethod(
3645           "net::HttpChannelChild::TrySendDeletingChannel", this,
3646           &HttpChannelChild::TrySendDeletingChannel),
3647       NS_DISPATCH_NORMAL);
3648   MOZ_ASSERT(NS_SUCCEEDED(rv));
3649 }
3650 
OnCopyComplete(nsresult aStatus)3651 void HttpChannelChild::OnCopyComplete(nsresult aStatus) {
3652   nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<nsresult>(
3653       "net::HttpBaseChannel::EnsureUploadStreamIsCloneableComplete", this,
3654       &HttpChannelChild::EnsureUploadStreamIsCloneableComplete, aStatus);
3655   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
3656   MOZ_ASSERT(neckoTarget);
3657 
3658   Unused << neckoTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
3659 }
3660 
AsyncCallImpl(void (HttpChannelChild::* funcPtr)(),nsRunnableMethod<HttpChannelChild> ** retval)3661 nsresult HttpChannelChild::AsyncCallImpl(
3662     void (HttpChannelChild::*funcPtr)(),
3663     nsRunnableMethod<HttpChannelChild>** retval) {
3664   nsresult rv;
3665 
3666   RefPtr<nsRunnableMethod<HttpChannelChild>> event =
3667       NewRunnableMethod("net::HttpChannelChild::AsyncCall", this, funcPtr);
3668   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
3669   MOZ_ASSERT(neckoTarget);
3670 
3671   rv = neckoTarget->Dispatch(event, NS_DISPATCH_NORMAL);
3672 
3673   if (NS_SUCCEEDED(rv) && retval) {
3674     *retval = event;
3675   }
3676 
3677   return rv;
3678 }
3679 
SetReferrerHeader(const nsACString & aReferrer,bool aRespectBeforeConnect)3680 nsresult HttpChannelChild::SetReferrerHeader(const nsACString& aReferrer,
3681                                              bool aRespectBeforeConnect) {
3682   // Normally this would be ENSURE_CALLED_BEFORE_CONNECT, but since the
3683   // "connect" is done in the main process, and mRequestObserversCalled is never
3684   // set in the ChannelChild, before connect basically means before asyncOpen.
3685   if (aRespectBeforeConnect) {
3686     ENSURE_CALLED_BEFORE_ASYNC_OPEN();
3687   }
3688 
3689   // remove old referrer if any, loop backwards
3690   for (int i = mClientSetRequestHeaders.Length() - 1; i >= 0; --i) {
3691     if (NS_LITERAL_CSTRING("Referer").Equals(
3692             mClientSetRequestHeaders[i].mHeader)) {
3693       mClientSetRequestHeaders.RemoveElementAt(i);
3694     }
3695   }
3696 
3697   return HttpBaseChannel::SetReferrerHeader(aReferrer, aRespectBeforeConnect);
3698 }
3699 
CancelOnMainThread(nsresult aRv)3700 void HttpChannelChild::CancelOnMainThread(nsresult aRv) {
3701   LOG(("HttpChannelChild::CancelOnMainThread [this=%p]", this));
3702 
3703   if (NS_IsMainThread()) {
3704     Cancel(aRv);
3705     return;
3706   }
3707 
3708   mEventQ->Suspend();
3709   // Cancel is expected to preempt any other channel events, thus we put this
3710   // event in the front of mEventQ to make sure nsIStreamListener not receiving
3711   // any ODA/OnStopRequest callbacks.
3712   mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
3713       this, [self = UnsafePtr<HttpChannelChild>(this), aRv]() {
3714         self->Cancel(aRv);
3715       }));
3716   mEventQ->Resume();
3717 }
3718 
OverrideWithSynthesizedResponse(UniquePtr<nsHttpResponseHead> & aResponseHead,nsIInputStream * aSynthesizedInput,nsIInterceptedBodyCallback * aSynthesizedCallback,InterceptStreamListener * aStreamListener,nsICacheInfoChannel * aCacheInfoChannel)3719 void HttpChannelChild::OverrideWithSynthesizedResponse(
3720     UniquePtr<nsHttpResponseHead>& aResponseHead,
3721     nsIInputStream* aSynthesizedInput,
3722     nsIInterceptedBodyCallback* aSynthesizedCallback,
3723     InterceptStreamListener* aStreamListener,
3724     nsICacheInfoChannel* aCacheInfoChannel) {
3725   MOZ_ASSERT(NS_IsMainThread());
3726   nsresult rv = NS_OK;
3727   auto autoCleanup = MakeScopeExit([&] {
3728     // Auto-cancel on failure.  Do this first to get mStatus set, if necessary.
3729     if (NS_FAILED(rv)) {
3730       Cancel(rv);
3731     }
3732 
3733     // If we early exit before taking ownership of the body, then automatically
3734     // invoke the callback.  This could be due to an error or because we're not
3735     // going to consume it due to a redirect, etc.
3736     if (aSynthesizedCallback) {
3737       aSynthesizedCallback->BodyComplete(mStatus);
3738     }
3739   });
3740 
3741   if (NS_FAILED(mStatus)) {
3742     return;
3743   }
3744 
3745   mInterceptListener = aStreamListener;
3746 
3747   // Intercepted responses should already be decoded.  If its a redirect,
3748   // however, we want to respect the encoding of the final result instead.
3749   if (!nsHttpChannel::WillRedirect(*aResponseHead)) {
3750     SetApplyConversion(false);
3751   }
3752 
3753   mResponseHead = std::move(aResponseHead);
3754   mSynthesizedResponse = true;
3755 
3756   mSynthesizedInput = aSynthesizedInput;
3757 
3758   if (!mSynthesizedInput) {
3759     rv = NS_NewCStringInputStream(getter_AddRefs(mSynthesizedInput),
3760                                   EmptyCString());
3761     NS_ENSURE_SUCCESS_VOID(rv);
3762   }
3763 
3764   if (nsHttpChannel::WillRedirect(*mResponseHead)) {
3765     // Normally we handle redirect limits in the parent process.  The way
3766     // e10s synthesized redirects work, however, the parent process does not
3767     // get an accurate redirect count.  Therefore we need to enforce it here.
3768     rv = CheckRedirectLimit(nsIChannelEventSink::REDIRECT_TEMPORARY);
3769     if (NS_WARN_IF(NS_FAILED(rv))) {
3770       Cancel(rv);
3771       return;
3772     }
3773 
3774     mShouldInterceptSubsequentRedirect = true;
3775     if (mInterceptListener) {
3776       mInterceptListener->Cleanup();
3777       mInterceptListener = nullptr;
3778     }
3779     // Continue with the original cross-process request
3780     rv = ContinueAsyncOpen();
3781     return;
3782   }
3783 
3784   // For progress we trust the content-length for the "maximum" size.
3785   // We can't determine the full size from the stream itself since we
3786   // only receive the data incrementally.  We can't trust Available()
3787   // here.
3788   // TODO: We could implement an nsIFixedLengthInputStream interface and
3789   //       QI to it here.  This would let us determine the total length
3790   //       for streams that support it.  See bug 1388774.
3791   rv = GetContentLength(&mSynthesizedStreamLength);
3792   if (NS_FAILED(rv)) {
3793     mSynthesizedStreamLength = -1;
3794   }
3795 
3796   nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
3797   MOZ_ASSERT(neckoTarget);
3798 
3799   rv = nsInputStreamPump::Create(getter_AddRefs(mSynthesizedResponsePump),
3800                                  mSynthesizedInput, 0, 0, true, neckoTarget);
3801   NS_ENSURE_SUCCESS_VOID(rv);
3802 
3803   mSynthesizedCacheInfo = aCacheInfoChannel;
3804 
3805   rv = mSynthesizedResponsePump->AsyncRead(aStreamListener, nullptr);
3806   NS_ENSURE_SUCCESS_VOID(rv);
3807 
3808   // The pump is started, so take ownership of the body callback.  We
3809   // clear the argument to avoid auto-completing it via the ScopeExit
3810   // lambda.
3811   mSynthesizedCallback = aSynthesizedCallback;
3812   aSynthesizedCallback = nullptr;
3813 
3814   // if this channel has been suspended previously, the pump needs to be
3815   // correspondingly suspended now that it exists.
3816   for (uint32_t i = 0; i < mSuspendCount; i++) {
3817     rv = mSynthesizedResponsePump->Suspend();
3818     NS_ENSURE_SUCCESS_VOID(rv);
3819   }
3820 
3821   MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
3822 }
3823 
3824 NS_IMETHODIMP
ForceIntercepted(bool aPostRedirectChannelShouldIntercept,bool aPostRedirectChannelShouldUpgrade)3825 HttpChannelChild::ForceIntercepted(bool aPostRedirectChannelShouldIntercept,
3826                                    bool aPostRedirectChannelShouldUpgrade) {
3827   MOZ_ASSERT(NS_IsMainThread());
3828   mShouldParentIntercept = true;
3829   mPostRedirectChannelShouldIntercept = aPostRedirectChannelShouldIntercept;
3830   mPostRedirectChannelShouldUpgrade = aPostRedirectChannelShouldUpgrade;
3831   return NS_OK;
3832 }
3833 
ForceIntercepted(nsIInputStream * aSynthesizedInput,nsIInterceptedBodyCallback * aSynthesizedCallback,nsICacheInfoChannel * aCacheInfo)3834 void HttpChannelChild::ForceIntercepted(
3835     nsIInputStream* aSynthesizedInput,
3836     nsIInterceptedBodyCallback* aSynthesizedCallback,
3837     nsICacheInfoChannel* aCacheInfo) {
3838   MOZ_ASSERT(NS_IsMainThread());
3839   mSynthesizedInput = aSynthesizedInput;
3840   mSynthesizedCallback = aSynthesizedCallback;
3841   mSynthesizedCacheInfo = aCacheInfo;
3842   mSynthesizedResponse = true;
3843   mRedirectingForSubsequentSynthesizedResponse = true;
3844 }
3845 
RecvIssueDeprecationWarning(const uint32_t & warning,const bool & asError)3846 mozilla::ipc::IPCResult HttpChannelChild::RecvIssueDeprecationWarning(
3847     const uint32_t& warning, const bool& asError) {
3848   nsCOMPtr<nsIDeprecationWarner> warner;
3849   GetCallback(warner);
3850   if (warner) {
3851     warner->IssueWarning(warning, asError);
3852   }
3853   return IPC_OK();
3854 }
3855 
ShouldInterceptURI(nsIURI * aURI,bool & aShouldUpgrade)3856 bool HttpChannelChild::ShouldInterceptURI(nsIURI* aURI, bool& aShouldUpgrade) {
3857   nsCOMPtr<nsIPrincipal> resultPrincipal;
3858   if (!aURI->SchemeIs("https")) {
3859     nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
3860         this, getter_AddRefs(resultPrincipal));
3861   }
3862   OriginAttributes originAttributes;
3863   NS_ENSURE_TRUE(
3864       StoragePrincipalHelper::GetOriginAttributes(
3865           this, originAttributes, StoragePrincipalHelper::eRegularPrincipal),
3866       false);
3867   bool notused = false;
3868   nsresult rv = NS_ShouldSecureUpgrade(
3869       aURI, mLoadInfo, resultPrincipal, mPrivateBrowsing, mAllowSTS,
3870       originAttributes, aShouldUpgrade, nullptr, notused);
3871   NS_ENSURE_SUCCESS(rv, false);
3872 
3873   nsCOMPtr<nsIURI> upgradedURI;
3874   if (aShouldUpgrade) {
3875     rv = NS_GetSecureUpgradedURI(aURI, getter_AddRefs(upgradedURI));
3876     NS_ENSURE_SUCCESS(rv, false);
3877   }
3878 
3879   return ShouldIntercept(upgradedURI ? upgradedURI.get() : aURI);
3880 }
3881 
RecvSetPriority(const int16_t & aPriority)3882 mozilla::ipc::IPCResult HttpChannelChild::RecvSetPriority(
3883     const int16_t& aPriority) {
3884   mPriority = aPriority;
3885   return IPC_OK();
3886 }
3887 
RecvAttachStreamFilter(Endpoint<extensions::PStreamFilterParent> && aEndpoint)3888 mozilla::ipc::IPCResult HttpChannelChild::RecvAttachStreamFilter(
3889     Endpoint<extensions::PStreamFilterParent>&& aEndpoint) {
3890   extensions::StreamFilterParent::Attach(this, std::move(aEndpoint));
3891   return IPC_OK();
3892 }
3893 
RecvCancelDiversion()3894 mozilla::ipc::IPCResult HttpChannelChild::RecvCancelDiversion() {
3895   MOZ_ASSERT(NS_IsMainThread());
3896 
3897   // This method is a very special case for cancellation of a diverted
3898   // intercepted channel.  Normally we would go through the mEventQ in order to
3899   // serialize event execution in the face of sync XHR and now background
3900   // channels.  However, similar to how CancelOnMainThread describes, Cancel()
3901   // pre-empts everything.  (And frankly, we want this stack frame on the stack
3902   // if a crash happens.)
3903   Cancel(NS_ERROR_ABORT);
3904   return IPC_OK();
3905 }
3906 
ActorDestroy(ActorDestroyReason aWhy)3907 void HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
3908   MOZ_ASSERT(NS_IsMainThread());
3909 
3910 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3911   mActorDestroyReason.emplace(aWhy);
3912 #endif
3913 
3914   // OnStartRequest might be dropped if IPDL is destroyed abnormally
3915   // and BackgroundChild might have pending IPC messages.
3916   // Clean up BackgroundChild at this time to prevent memleak.
3917   if (aWhy != Deletion) {
3918     // Make sure all the messages are processed.
3919     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
3920 
3921     mStatus = NS_ERROR_DOCSHELL_DYING;
3922     HandleAsyncAbort();
3923 
3924     // Cleanup the background channel before we resume the eventQ so we don't
3925     // get any other events.
3926     CleanupBackgroundChannel();
3927 
3928     mIPCActorDeleted = true;
3929     mCanceled = true;
3930   }
3931 }
3932 
RecvLogBlockedCORSRequest(const nsString & aMessage,const nsCString & aCategory)3933 mozilla::ipc::IPCResult HttpChannelChild::RecvLogBlockedCORSRequest(
3934     const nsString& aMessage, const nsCString& aCategory) {
3935   Unused << LogBlockedCORSRequest(aMessage, aCategory);
3936   return IPC_OK();
3937 }
3938 
3939 NS_IMETHODIMP
LogBlockedCORSRequest(const nsAString & aMessage,const nsACString & aCategory)3940 HttpChannelChild::LogBlockedCORSRequest(const nsAString& aMessage,
3941                                         const nsACString& aCategory) {
3942   uint64_t innerWindowID = mLoadInfo->GetInnerWindowID();
3943   bool privateBrowsing = !!mLoadInfo->GetOriginAttributes().mPrivateBrowsingId;
3944   bool fromChromeContext =
3945       mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal();
3946   nsCORSListenerProxy::LogBlockedCORSRequest(
3947       innerWindowID, privateBrowsing, fromChromeContext, aMessage, aCategory);
3948   return NS_OK;
3949 }
3950 
RecvLogMimeTypeMismatch(const nsCString & aMessageName,const bool & aWarning,const nsString & aURL,const nsString & aContentType)3951 mozilla::ipc::IPCResult HttpChannelChild::RecvLogMimeTypeMismatch(
3952     const nsCString& aMessageName, const bool& aWarning, const nsString& aURL,
3953     const nsString& aContentType) {
3954   Unused << LogMimeTypeMismatch(aMessageName, aWarning, aURL, aContentType);
3955   return IPC_OK();
3956 }
3957 
3958 NS_IMETHODIMP
LogMimeTypeMismatch(const nsACString & aMessageName,bool aWarning,const nsAString & aURL,const nsAString & aContentType)3959 HttpChannelChild::LogMimeTypeMismatch(const nsACString& aMessageName,
3960                                       bool aWarning, const nsAString& aURL,
3961                                       const nsAString& aContentType) {
3962   RefPtr<Document> doc;
3963   mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
3964 
3965   AutoTArray<nsString, 2> params;
3966   params.AppendElement(aURL);
3967   params.AppendElement(aContentType);
3968   nsContentUtils::ReportToConsole(
3969       aWarning ? nsIScriptError::warningFlag : nsIScriptError::errorFlag,
3970       NS_LITERAL_CSTRING("MIMEMISMATCH"), doc,
3971       nsContentUtils::eSECURITY_PROPERTIES, nsCString(aMessageName).get(),
3972       params);
3973   return NS_OK;
3974 }
3975 
MaybeCallSynthesizedCallback()3976 void HttpChannelChild::MaybeCallSynthesizedCallback() {
3977   if (!mSynthesizedCallback) {
3978     return;
3979   }
3980 
3981   mSynthesizedCallback->BodyComplete(mStatus);
3982   mSynthesizedCallback = nullptr;
3983 }
3984 
CrossProcessRedirectFinished(nsresult aStatus)3985 nsresult HttpChannelChild::CrossProcessRedirectFinished(nsresult aStatus) {
3986   if (!CanSend()) {
3987     return NS_BINDING_FAILED;
3988   }
3989 
3990   if (!mCanceled && NS_SUCCEEDED(mStatus)) {
3991     mStatus = aStatus;
3992   }
3993 
3994   return mStatus;
3995 }
3996 
DoDiagnosticAssertWhenOnStopNotCalledOnDestroy()3997 void HttpChannelChild::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
3998 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3999   mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy = true;
4000 #endif
4001 }
4002 
MaybeConnectToSocketProcess()4003 void HttpChannelChild::MaybeConnectToSocketProcess() {
4004   if (!nsIOService::UseSocketProcess()) {
4005     return;
4006   }
4007 
4008   if (!StaticPrefs::network_send_ODA_to_content_directly()) {
4009     return;
4010   }
4011 
4012   RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild;
4013   SocketProcessBridgeChild::GetSocketProcessBridge()->Then(
4014       GetCurrentThreadSerialEventTarget(), __func__,
4015       [bgChild]() {
4016         gSocketTransportService->Dispatch(
4017             NewRunnableMethod("HttpBackgroundChannelChild::CreateDataBridge",
4018                               bgChild,
4019                               &HttpBackgroundChannelChild::CreateDataBridge),
4020             NS_DISPATCH_NORMAL);
4021       },
4022       []() { NS_WARNING("Failed to create SocketProcessBridgeChild"); });
4023 }
4024 
4025 }  // namespace net
4026 }  // namespace mozilla
4027