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 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
9 
10 #include "mozilla/ConsoleReportCollector.h"
11 #include "mozilla/ipc/FileDescriptorSetParent.h"
12 #include "mozilla/ipc/IPCStreamUtils.h"
13 #include "mozilla/net/HttpChannelParent.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/ContentProcessManager.h"
16 #include "mozilla/dom/Element.h"
17 #include "mozilla/dom/ServiceWorkerUtils.h"
18 #include "mozilla/dom/BrowserParent.h"
19 #include "mozilla/net/NeckoParent.h"
20 #include "mozilla/InputStreamLengthHelper.h"
21 #include "mozilla/IntegerPrintfMacros.h"
22 #include "mozilla/ProfilerLabels.h"
23 #include "mozilla/StoragePrincipalHelper.h"
24 #include "mozilla/UniquePtr.h"
25 #include "mozilla/Unused.h"
26 #include "HttpBackgroundChannelParent.h"
27 #include "ParentChannelListener.h"
28 #include "nsICacheInfoChannel.h"
29 #include "nsHttpHandler.h"
30 #include "nsNetCID.h"
31 #include "nsNetUtil.h"
32 #include "nsISupportsPriority.h"
33 #include "mozilla/net/BackgroundChannelRegistrar.h"
34 #include "nsSerializationHelper.h"
35 #include "nsISerializable.h"
36 #include "mozilla/ipc/InputStreamUtils.h"
37 #include "mozilla/ipc/URIUtils.h"
38 #include "SerializedLoadContext.h"
39 #include "nsIAuthPrompt.h"
40 #include "nsIAuthPrompt2.h"
41 #include "mozilla/ipc/BackgroundUtils.h"
42 #include "mozilla/LoadInfo.h"
43 #include "nsQueryObject.h"
44 #include "mozilla/BasePrincipal.h"
45 #include "nsCORSListenerProxy.h"
46 #include "nsIIPCSerializableInputStream.h"
47 #include "nsIPrompt.h"
48 #include "nsIPromptFactory.h"
49 #include "mozilla/net/ChannelEventQueue.h"
50 #include "mozilla/net/RedirectChannelRegistrar.h"
51 #include "nsIWindowWatcher.h"
52 #include "mozilla/dom/Document.h"
53 #include "nsISecureBrowserUI.h"
54 #include "nsStreamUtils.h"
55 #include "nsStringStream.h"
56 #include "nsThreadUtils.h"
57 #include "nsQueryObject.h"
58 #include "nsIMultiPartChannel.h"
59 #include "nsIViewSourceChannel.h"
60 
61 using mozilla::BasePrincipal;
62 using namespace mozilla::dom;
63 using namespace mozilla::ipc;
64 
65 namespace mozilla {
66 namespace net {
67 
HttpChannelParent(dom::BrowserParent * iframeEmbedding,nsILoadContext * aLoadContext,PBOverrideStatus aOverrideStatus)68 HttpChannelParent::HttpChannelParent(dom::BrowserParent* iframeEmbedding,
69                                      nsILoadContext* aLoadContext,
70                                      PBOverrideStatus aOverrideStatus)
71     : mLoadContext(aLoadContext),
72       mIPCClosed(false),
73       mPBOverride(aOverrideStatus),
74       mStatus(NS_OK),
75       mIgnoreProgress(false),
76       mSentRedirect1BeginFailed(false),
77       mReceivedRedirect2Verify(false),
78       mHasSuspendedByBackPressure(false),
79       mCacheNeedFlowControlInitialized(false),
80       mNeedFlowControl(true),
81       mSuspendedForFlowControl(false),
82       mAfterOnStartRequestBegun(false),
83       mDataSentToChildProcess(false) {
84   LOG(("Creating HttpChannelParent [this=%p]\n", this));
85 
86   // Ensure gHttpHandler is initialized: we need the atom table up and running.
87   nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
88       do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
89 
90   MOZ_ASSERT(gHttpHandler);
91   mHttpHandler = gHttpHandler;
92 
93   mBrowserParent = iframeEmbedding;
94 
95   mSendWindowSize = gHttpHandler->SendWindowSize();
96 
97   mEventQ =
98       new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
99 }
100 
~HttpChannelParent()101 HttpChannelParent::~HttpChannelParent() {
102   LOG(("Destroying HttpChannelParent [this=%p]\n", this));
103   CleanupBackgroundChannel();
104 
105   MOZ_ASSERT(!mRedirectCallback);
106   if (NS_WARN_IF(mRedirectCallback)) {
107     mRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_UNEXPECTED);
108     mRedirectCallback = nullptr;
109   }
110   mEventQ->NotifyReleasingOwner();
111 }
112 
ActorDestroy(ActorDestroyReason why)113 void HttpChannelParent::ActorDestroy(ActorDestroyReason why) {
114   // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
115   // yet, but child process has crashed.  We must not try to send any more msgs
116   // to child, or IPDL will kill chrome process, too.
117   mIPCClosed = true;
118   CleanupBackgroundChannel();
119 }
120 
Init(const HttpChannelCreationArgs & aArgs)121 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
122   LOG(("HttpChannelParent::Init [this=%p]\n", this));
123   AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
124   switch (aArgs.type()) {
125     case HttpChannelCreationArgs::THttpChannelOpenArgs: {
126       const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
127       return DoAsyncOpen(
128           a.uri(), a.original(), a.doc(), a.referrerInfo(), a.apiRedirectTo(),
129           a.topWindowURI(), a.loadFlags(), a.requestHeaders(),
130           a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
131           a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
132           a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
133           a.allowSpdy(), a.allowHttp3(), a.allowAltSvc(), a.beConservative(),
134           a.bypassProxy(), a.tlsFlags(), a.loadInfo(), a.cacheKey(),
135           a.requestContextID(), a.preflightArgs(), a.initialRwin(),
136           a.blockAuthPrompt(), a.allowStaleCacheContent(),
137           a.preferCacheLoadOverBypass(), a.contentTypeHint(), a.corsMode(),
138           a.redirectMode(), a.channelId(), a.integrityMetadata(),
139           a.contentWindowId(), a.preferredAlternativeTypes(),
140           a.topBrowsingContextId(), a.launchServiceWorkerStart(),
141           a.launchServiceWorkerEnd(), a.dispatchFetchEventStart(),
142           a.dispatchFetchEventEnd(), a.handleFetchEventStart(),
143           a.handleFetchEventEnd(), a.forceMainDocumentChannel(),
144           a.navigationStartTimeStamp());
145     }
146     case HttpChannelCreationArgs::THttpChannelConnectArgs: {
147       const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
148       return ConnectChannel(cArgs.registrarId());
149     }
150     default:
151       MOZ_ASSERT_UNREACHABLE("unknown open type");
152       return false;
153   }
154 }
155 
TryInvokeAsyncOpen(nsresult aRv)156 void HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv) {
157   LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
158        "]\n",
159        this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
160   MOZ_ASSERT(NS_IsMainThread());
161   AUTO_PROFILER_LABEL("HttpChannelParent::TryInvokeAsyncOpen", NETWORK);
162 
163   // TryInvokeAsyncOpen is called more than we expected.
164   // Assert in nightly build but ignore it in release channel.
165   MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0);
166   if (NS_WARN_IF(!mAsyncOpenBarrier)) {
167     return;
168   }
169 
170   if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) {
171     // Need to wait for more events.
172     return;
173   }
174 
175   InvokeAsyncOpen(aRv);
176 }
177 
OnBackgroundParentReady(HttpBackgroundChannelParent * aBgParent)178 void HttpChannelParent::OnBackgroundParentReady(
179     HttpBackgroundChannelParent* aBgParent) {
180   LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
181        this, aBgParent));
182   MOZ_ASSERT(NS_IsMainThread());
183   MOZ_ASSERT(!mBgParent);
184 
185   mBgParent = aBgParent;
186 
187   mPromise.ResolveIfExists(true, __func__);
188 }
189 
OnBackgroundParentDestroyed()190 void HttpChannelParent::OnBackgroundParentDestroyed() {
191   LOG(("HttpChannelParent::OnBackgroundParentDestroyed [this=%p]\n", this));
192   MOZ_ASSERT(NS_IsMainThread());
193 
194   if (!mPromise.IsEmpty()) {
195     MOZ_ASSERT(!mBgParent);
196     mPromise.Reject(NS_ERROR_FAILURE, __func__);
197     return;
198   }
199 
200   if (!mBgParent) {
201     return;
202   }
203 
204   // Background channel is closed unexpectly, abort PHttpChannel operation.
205   mBgParent = nullptr;
206   Delete();
207 }
208 
CleanupBackgroundChannel()209 void HttpChannelParent::CleanupBackgroundChannel() {
210   LOG(("HttpChannelParent::CleanupBackgroundChannel [this=%p bgParent=%p]\n",
211        this, mBgParent.get()));
212   MOZ_ASSERT(NS_IsMainThread());
213 
214   if (mBgParent) {
215     RefPtr<HttpBackgroundChannelParent> bgParent = std::move(mBgParent);
216     bgParent->OnChannelClosed();
217     return;
218   }
219 
220   // The nsHttpChannel may have a reference to this parent, release it
221   // to avoid circular references.
222   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
223   if (httpChannelImpl) {
224     httpChannelImpl->SetWarningReporter(nullptr);
225   }
226 
227   if (!mPromise.IsEmpty()) {
228     mRequest.DisconnectIfExists();
229     mPromise.Reject(NS_ERROR_FAILURE, __func__);
230 
231     if (!mChannel) {
232       return;
233     }
234 
235     // This HttpChannelParent might still have a reference from
236     // BackgroundChannelRegistrar.
237     nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
238         BackgroundChannelRegistrar::GetOrCreate();
239     MOZ_ASSERT(registrar);
240 
241     registrar->DeleteChannel(mChannel->ChannelId());
242 
243     // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
244     // is still on going. we need to abort AsyncOpen with failure to destroy
245     // PHttpChannel actor.
246     if (mAsyncOpenBarrier) {
247       TryInvokeAsyncOpen(NS_ERROR_FAILURE);
248     }
249   }
250 }
251 
OtherPid() const252 base::ProcessId HttpChannelParent::OtherPid() const {
253   if (mIPCClosed) {
254     return 0;
255   }
256   return PHttpChannelParent::OtherPid();
257 }
258 
259 //-----------------------------------------------------------------------------
260 // HttpChannelParent::nsISupports
261 //-----------------------------------------------------------------------------
262 
263 NS_IMPL_ADDREF(HttpChannelParent)
NS_IMPL_RELEASE(HttpChannelParent)264 NS_IMPL_RELEASE(HttpChannelParent)
265 NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
266   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
267   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
268   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
269   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
270   NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
271   NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
272   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
273   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
274   NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
275   NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
276   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
277   NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelParent)
278 NS_INTERFACE_MAP_END
279 
280 //-----------------------------------------------------------------------------
281 // HttpChannelParent::nsIInterfaceRequestor
282 //-----------------------------------------------------------------------------
283 
284 NS_IMETHODIMP
285 HttpChannelParent::GetInterface(const nsIID& aIID, void** result) {
286   // A system XHR can be created without reference to a window, hence mTabParent
287   // may be null.  In that case we want to let the window watcher pick a prompt
288   // directly.
289   if (!mBrowserParent && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
290                           aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) {
291     nsresult rv;
292     nsCOMPtr<nsIWindowWatcher> wwatch =
293         do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
294     NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
295 
296     bool hasWindowCreator = false;
297     Unused << wwatch->HasWindowCreator(&hasWindowCreator);
298     if (!hasWindowCreator) {
299       return NS_ERROR_NO_INTERFACE;
300     }
301 
302     nsCOMPtr<nsIPromptFactory> factory = do_QueryInterface(wwatch);
303     if (!factory) {
304       return NS_ERROR_NO_INTERFACE;
305     }
306     rv = factory->GetPrompt(nullptr, aIID, reinterpret_cast<void**>(result));
307     if (NS_FAILED(rv)) {
308       return NS_ERROR_NO_INTERFACE;
309     }
310     return NS_OK;
311   }
312 
313   // Only support nsILoadContext if child channel's callbacks did too
314   if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
315     nsCOMPtr<nsILoadContext> copy = mLoadContext;
316     copy.forget(result);
317     return NS_OK;
318   }
319 
320   return QueryInterface(aIID, result);
321 }
322 
323 //-----------------------------------------------------------------------------
324 // HttpChannelParent::PHttpChannelParent
325 //-----------------------------------------------------------------------------
326 
AsyncOpenFailed(nsresult aRv)327 void HttpChannelParent::AsyncOpenFailed(nsresult aRv) {
328   MOZ_ASSERT(NS_IsMainThread());
329   MOZ_ASSERT(NS_FAILED(aRv));
330 
331   // Break the reference cycle among HttpChannelParent,
332   // ParentChannelListener, and nsHttpChannel to avoid memory leakage.
333   mChannel = nullptr;
334   mParentListener = nullptr;
335 
336   if (!mIPCClosed) {
337     Unused << SendFailedAsyncOpen(aRv);
338   }
339 }
340 
InvokeAsyncOpen(nsresult rv)341 void HttpChannelParent::InvokeAsyncOpen(nsresult rv) {
342   LOG(("HttpChannelParent::InvokeAsyncOpen [this=%p rv=%" PRIx32 "]\n", this,
343        static_cast<uint32_t>(rv)));
344   MOZ_ASSERT(NS_IsMainThread());
345 
346   if (NS_FAILED(rv)) {
347     AsyncOpenFailed(rv);
348     return;
349   }
350 
351   rv = mChannel->AsyncOpen(mParentListener);
352   if (NS_FAILED(rv)) {
353     AsyncOpenFailed(rv);
354   }
355 }
356 
DoAsyncOpen(const URIParams & aURI,const Maybe<URIParams> & aOriginalURI,const Maybe<URIParams> & aDocURI,nsIReferrerInfo * aReferrerInfo,const Maybe<URIParams> & aAPIRedirectToURI,const Maybe<URIParams> & aTopWindowURI,const uint32_t & aLoadFlags,const RequestHeaderTuples & requestHeaders,const nsCString & requestMethod,const Maybe<IPCStream> & uploadStream,const bool & uploadStreamHasHeaders,const int16_t & priority,const uint32_t & classOfService,const uint8_t & redirectionLimit,const bool & allowSTS,const uint32_t & thirdPartyFlags,const bool & doResumeAt,const uint64_t & startPos,const nsCString & entityID,const bool & allowSpdy,const bool & allowHttp3,const bool & allowAltSvc,const bool & beConservative,const bool & bypassProxy,const uint32_t & tlsFlags,const Maybe<LoadInfoArgs> & aLoadInfoArgs,const uint32_t & aCacheKey,const uint64_t & aRequestContextID,const Maybe<CorsPreflightArgs> & aCorsPreflightArgs,const uint32_t & aInitialRwin,const bool & aBlockAuthPrompt,const bool & aAllowStaleCacheContent,const bool & aPreferCacheLoadOverBypass,const nsCString & aContentTypeHint,const uint32_t & aCorsMode,const uint32_t & aRedirectMode,const uint64_t & aChannelId,const nsString & aIntegrityMetadata,const uint64_t & aContentWindowId,const nsTArray<PreferredAlternativeDataTypeParams> & aPreferredAlternativeTypes,const uint64_t & aTopBrowsingContextId,const TimeStamp & aLaunchServiceWorkerStart,const TimeStamp & aLaunchServiceWorkerEnd,const TimeStamp & aDispatchFetchEventStart,const TimeStamp & aDispatchFetchEventEnd,const TimeStamp & aHandleFetchEventStart,const TimeStamp & aHandleFetchEventEnd,const bool & aForceMainDocumentChannel,const TimeStamp & aNavigationStartTimeStamp)357 bool HttpChannelParent::DoAsyncOpen(
358     const URIParams& aURI, const Maybe<URIParams>& aOriginalURI,
359     const Maybe<URIParams>& aDocURI, nsIReferrerInfo* aReferrerInfo,
360     const Maybe<URIParams>& aAPIRedirectToURI,
361     const Maybe<URIParams>& aTopWindowURI, const uint32_t& aLoadFlags,
362     const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
363     const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
364     const int16_t& priority, const uint32_t& classOfService,
365     const uint8_t& redirectionLimit, const bool& allowSTS,
366     const uint32_t& thirdPartyFlags, const bool& doResumeAt,
367     const uint64_t& startPos, const nsCString& entityID, const bool& allowSpdy,
368     const bool& allowHttp3, const bool& allowAltSvc, const bool& beConservative,
369     const bool& bypassProxy, const uint32_t& tlsFlags,
370     const Maybe<LoadInfoArgs>& aLoadInfoArgs, const uint32_t& aCacheKey,
371     const uint64_t& aRequestContextID,
372     const Maybe<CorsPreflightArgs>& aCorsPreflightArgs,
373     const uint32_t& aInitialRwin, const bool& aBlockAuthPrompt,
374     const bool& aAllowStaleCacheContent, const bool& aPreferCacheLoadOverBypass,
375     const nsCString& aContentTypeHint, const uint32_t& aCorsMode,
376     const uint32_t& aRedirectMode, const uint64_t& aChannelId,
377     const nsString& aIntegrityMetadata, const uint64_t& aContentWindowId,
378     const nsTArray<PreferredAlternativeDataTypeParams>&
379         aPreferredAlternativeTypes,
380     const uint64_t& aTopBrowsingContextId,
381     const TimeStamp& aLaunchServiceWorkerStart,
382     const TimeStamp& aLaunchServiceWorkerEnd,
383     const TimeStamp& aDispatchFetchEventStart,
384     const TimeStamp& aDispatchFetchEventEnd,
385     const TimeStamp& aHandleFetchEventStart,
386     const TimeStamp& aHandleFetchEventEnd,
387     const bool& aForceMainDocumentChannel,
388     const TimeStamp& aNavigationStartTimeStamp) {
389   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
390   if (!uri) {
391     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
392     // null deref here.
393     return false;
394   }
395   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
396   nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
397   nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
398   nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
399 
400   LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64
401        " top bid=%" PRIx64 "]\n",
402        this, uri->GetSpecOrDefault().get(), aChannelId, aTopBrowsingContextId));
403 
404   nsresult rv;
405 
406   nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
407   if (NS_FAILED(rv)) return SendFailedAsyncOpen(rv);
408 
409   nsCOMPtr<nsILoadInfo> loadInfo;
410   rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
411                                             getter_AddRefs(loadInfo));
412   if (NS_FAILED(rv)) {
413     return SendFailedAsyncOpen(rv);
414   }
415 
416   nsCOMPtr<nsIChannel> channel;
417   rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, loadInfo, nullptr,
418                              nullptr, nullptr, aLoadFlags, ios);
419   if (NS_FAILED(rv)) {
420     return SendFailedAsyncOpen(rv);
421   }
422 
423   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(channel, &rv);
424   if (NS_FAILED(rv)) {
425     return SendFailedAsyncOpen(rv);
426   }
427 
428   // Set attributes needed to create a FetchEvent from this channel.
429   httpChannel->SetCorsMode(aCorsMode);
430   httpChannel->SetRedirectMode(aRedirectMode);
431 
432   // Set the channelId allocated in child to the parent instance
433   httpChannel->SetChannelId(aChannelId);
434   httpChannel->SetTopLevelContentWindowId(aContentWindowId);
435   httpChannel->SetTopBrowsingContextId(aTopBrowsingContextId);
436 
437   httpChannel->SetIntegrityMetadata(aIntegrityMetadata);
438 
439   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(httpChannel);
440   if (httpChannelImpl) {
441     httpChannelImpl->SetWarningReporter(this);
442   }
443   httpChannel->SetTimingEnabled(true);
444   if (mPBOverride != kPBOverride_Unset) {
445     httpChannel->SetPrivate(mPBOverride == kPBOverride_Private);
446   }
447 
448   if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
449 
450   if (originalUri) httpChannel->SetOriginalURI(originalUri);
451   if (docUri) httpChannel->SetDocumentURI(docUri);
452   if (aReferrerInfo) {
453     // Referrer header is computed in child no need to recompute here
454     rv =
455         httpChannel->SetReferrerInfoInternal(aReferrerInfo, false, false, true);
456     MOZ_ASSERT(NS_SUCCEEDED(rv));
457   }
458 
459   if (apiRedirectToUri) httpChannel->RedirectTo(apiRedirectToUri);
460   if (topWindowUri) {
461     httpChannel->SetTopWindowURI(topWindowUri);
462   }
463 
464   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
465     httpChannel->SetLoadFlags(aLoadFlags);
466   }
467 
468   if (aForceMainDocumentChannel) {
469     httpChannel->SetIsMainDocumentChannel(true);
470   }
471 
472   for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
473     if (requestHeaders[i].mEmpty) {
474       httpChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
475     } else {
476       httpChannel->SetRequestHeader(requestHeaders[i].mHeader,
477                                     requestHeaders[i].mValue,
478                                     requestHeaders[i].mMerge);
479     }
480   }
481 
482   RefPtr<ParentChannelListener> parentListener = new ParentChannelListener(
483       this, mBrowserParent ? mBrowserParent->GetBrowsingContext() : nullptr,
484       mLoadContext && mLoadContext->UsePrivateBrowsing());
485 
486   httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
487 
488   if (aCorsPreflightArgs.isSome()) {
489     const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
490     httpChannel->SetCorsPreflightParameters(args.unsafeHeaders(), false);
491   }
492 
493   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
494   if (stream) {
495     int64_t length;
496     if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
497       httpChannel->InternalSetUploadStreamLength(length >= 0 ? length : 0);
498     } else {
499       // Wait for the nputStreamLengthHelper::GetAsyncLength callback.
500       ++mAsyncOpenBarrier;
501 
502       // Let's resolve the size of the stream. The following operation is always
503       // async.
504       RefPtr<HttpChannelParent> self = this;
505       InputStreamLengthHelper::GetAsyncLength(stream, [self, httpChannel](
506                                                           int64_t aLength) {
507         httpChannel->InternalSetUploadStreamLength(aLength >= 0 ? aLength : 0);
508         self->TryInvokeAsyncOpen(NS_OK);
509       });
510     }
511 
512     httpChannel->InternalSetUploadStream(stream);
513     httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
514   }
515 
516   nsCOMPtr<nsICacheInfoChannel> cacheChannel =
517       do_QueryInterface(static_cast<nsIChannel*>(httpChannel.get()));
518   if (cacheChannel) {
519     cacheChannel->SetCacheKey(aCacheKey);
520     for (const auto& data : aPreferredAlternativeTypes) {
521       cacheChannel->PreferAlternativeDataType(data.type(), data.contentType(),
522                                               data.deliverAltData());
523     }
524 
525     cacheChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
526     cacheChannel->SetPreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
527 
528     // This is to mark that the results are going to the content process.
529     if (httpChannelImpl) {
530       httpChannelImpl->SetAltDataForChild(true);
531     }
532   }
533 
534   httpChannel->SetContentType(aContentTypeHint);
535 
536   if (priority != nsISupportsPriority::PRIORITY_NORMAL) {
537     httpChannel->SetPriority(priority);
538   }
539   if (classOfService) {
540     httpChannel->SetClassFlags(classOfService);
541   }
542   httpChannel->SetRedirectionLimit(redirectionLimit);
543   httpChannel->SetAllowSTS(allowSTS);
544   httpChannel->SetThirdPartyFlags(thirdPartyFlags);
545   httpChannel->SetAllowSpdy(allowSpdy);
546   httpChannel->SetAllowHttp3(allowHttp3);
547   httpChannel->SetAllowAltSvc(allowAltSvc);
548   httpChannel->SetBeConservative(beConservative);
549   httpChannel->SetTlsFlags(tlsFlags);
550   httpChannel->SetInitialRwin(aInitialRwin);
551   httpChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
552 
553   httpChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
554   httpChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
555   httpChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
556   httpChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
557   httpChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
558   httpChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
559 
560   httpChannel->SetNavigationStartTimeStamp(aNavigationStartTimeStamp);
561   httpChannel->SetRequestContextID(aRequestContextID);
562 
563   // Store the strong reference of channel and parent listener object until
564   // all the initialization procedure is complete without failure, to remove
565   // cycle reference in fail case and to avoid memory leakage.
566   mChannel = std::move(httpChannel);
567   mParentListener = std::move(parentListener);
568   mChannel->SetNotificationCallbacks(mParentListener);
569 
570   MOZ_ASSERT(!mBgParent);
571   MOZ_ASSERT(mPromise.IsEmpty());
572   // Wait for HttpBackgrounChannel to continue the async open procedure.
573   ++mAsyncOpenBarrier;
574   RefPtr<HttpChannelParent> self = this;
575   WaitForBgParent()
576       ->Then(
577           GetMainThreadSerialEventTarget(), __func__,
578           [self]() {
579             self->mRequest.Complete();
580             self->TryInvokeAsyncOpen(NS_OK);
581           },
582           [self](nsresult aStatus) {
583             self->mRequest.Complete();
584             self->TryInvokeAsyncOpen(aStatus);
585           })
586       ->Track(mRequest);
587 
588   // The stream, received from the child process, must be cloneable and seekable
589   // in order to allow devtools to inspect its content.
590   nsCOMPtr<nsIRunnable> r =
591       NS_NewRunnableFunction("HttpChannelParent::EnsureUploadStreamIsCloneable",
592                              [self]() { self->TryInvokeAsyncOpen(NS_OK); });
593   ++mAsyncOpenBarrier;
594   mChannel->EnsureUploadStreamIsCloneable(r);
595   return true;
596 }
597 
WaitForBgParent()598 RefPtr<GenericNonExclusivePromise> HttpChannelParent::WaitForBgParent() {
599   LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
600   MOZ_ASSERT(!mBgParent);
601 
602   if (!mChannel) {
603     return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
604                                                        __func__);
605   }
606 
607   nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
608       BackgroundChannelRegistrar::GetOrCreate();
609   MOZ_ASSERT(registrar);
610   registrar->LinkHttpChannel(mChannel->ChannelId(), this);
611 
612   if (mBgParent) {
613     return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
614   }
615 
616   return mPromise.Ensure(__func__);
617 }
618 
ConnectChannel(const uint32_t & registrarId)619 bool HttpChannelParent::ConnectChannel(const uint32_t& registrarId) {
620   nsresult rv;
621 
622   LOG(
623       ("HttpChannelParent::ConnectChannel: Looking for a registered channel "
624        "[this=%p, id=%" PRIu32 "]\n",
625        this, registrarId));
626   nsCOMPtr<nsIChannel> channel;
627   rv = NS_LinkRedirectChannels(registrarId, this, getter_AddRefs(channel));
628   if (NS_FAILED(rv)) {
629     NS_ERROR("Could not find the http channel to connect its IPC parent");
630     // This makes the channel delete itself safely.  It's the only thing
631     // we can do now, since this parent channel cannot be used and there is
632     // no other way to tell the child side there were something wrong.
633     Delete();
634     return true;
635   }
636 
637   LOG(("  found channel %p, rv=%08" PRIx32, channel.get(),
638        static_cast<uint32_t>(rv)));
639   mChannel = do_QueryObject(channel);
640   if (!mChannel) {
641     LOG(("  but it's not HttpBaseChannel"));
642     Delete();
643     return true;
644   }
645 
646   LOG(("  and it is HttpBaseChannel %p", mChannel.get()));
647 
648   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
649   if (httpChannelImpl) {
650     httpChannelImpl->SetWarningReporter(this);
651   }
652 
653   if (mPBOverride != kPBOverride_Unset) {
654     // redirected-to channel may not support PB
655     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
656     if (pbChannel) {
657       pbChannel->SetPrivate(mPBOverride == kPBOverride_Private);
658     }
659   }
660 
661   MOZ_ASSERT(!mBgParent);
662   MOZ_ASSERT(mPromise.IsEmpty());
663   // Waiting for background channel
664   RefPtr<HttpChannelParent> self = this;
665   WaitForBgParent()
666       ->Then(
667           GetMainThreadSerialEventTarget(), __func__,
668           [self]() { self->mRequest.Complete(); },
669           [self](const nsresult& aResult) {
670             NS_ERROR("failed to establish the background channel");
671             self->mRequest.Complete();
672           })
673       ->Track(mRequest);
674   return true;
675 }
676 
RecvSetPriority(const int16_t & priority)677 mozilla::ipc::IPCResult HttpChannelParent::RecvSetPriority(
678     const int16_t& priority) {
679   LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n", this,
680        priority));
681   AUTO_PROFILER_LABEL("HttpChannelParent::RecvSetPriority", NETWORK);
682 
683   if (mChannel) {
684     mChannel->SetPriority(priority);
685   }
686 
687   nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
688       do_QueryInterface(mRedirectChannel);
689   if (priorityRedirectChannel) priorityRedirectChannel->SetPriority(priority);
690 
691   return IPC_OK();
692 }
693 
RecvSetClassOfService(const uint32_t & cos)694 mozilla::ipc::IPCResult HttpChannelParent::RecvSetClassOfService(
695     const uint32_t& cos) {
696   if (mChannel) {
697     mChannel->SetClassFlags(cos);
698   }
699   return IPC_OK();
700 }
701 
RecvSuspend()702 mozilla::ipc::IPCResult HttpChannelParent::RecvSuspend() {
703   LOG(("HttpChannelParent::RecvSuspend [this=%p]\n", this));
704 
705   if (mChannel) {
706     mChannel->Suspend();
707   }
708   return IPC_OK();
709 }
710 
RecvResume()711 mozilla::ipc::IPCResult HttpChannelParent::RecvResume() {
712   LOG(("HttpChannelParent::RecvResume [this=%p]\n", this));
713 
714   if (mChannel) {
715     mChannel->Resume();
716   }
717   return IPC_OK();
718 }
719 
RecvCancel(const nsresult & status,const uint32_t & requestBlockingReason)720 mozilla::ipc::IPCResult HttpChannelParent::RecvCancel(
721     const nsresult& status, const uint32_t& requestBlockingReason) {
722   LOG(("HttpChannelParent::RecvCancel [this=%p]\n", this));
723 
724   // May receive cancel before channel has been constructed!
725   if (mChannel) {
726     mChannel->Cancel(status);
727 
728     if (MOZ_UNLIKELY(requestBlockingReason !=
729                      nsILoadInfo::BLOCKING_REASON_NONE)) {
730       nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
731       loadInfo->SetRequestBlockingReason(requestBlockingReason);
732     }
733 
734     // Once we receive |Cancel|, child will stop sending RecvBytesRead. Force
735     // the channel resumed if needed.
736     if (mSuspendedForFlowControl) {
737       LOG(("  resume the channel due to e10s backpressure relief by cancel"));
738       Unused << mChannel->Resume();
739       mSuspendedForFlowControl = false;
740     }
741   } else if (!mIPCClosed) {
742     // Make sure that the child correctly delivers all stream listener
743     // notifications.
744     Unused << SendFailedAsyncOpen(status);
745   }
746 
747   // We won't need flow control anymore. Toggle the flag to avoid |Suspend|
748   // since OnDataAvailable could be off-main-thread.
749   mCacheNeedFlowControlInitialized = true;
750   mNeedFlowControl = false;
751   return IPC_OK();
752 }
753 
RecvRedirect2Verify(const nsresult & aResult,const RequestHeaderTuples & changedHeaders,const uint32_t & aSourceRequestBlockingReason,const Maybe<ChildLoadInfoForwarderArgs> & aTargetLoadInfoForwarder,const uint32_t & loadFlags,nsIReferrerInfo * aReferrerInfo,const Maybe<URIParams> & aAPIRedirectURI,const Maybe<CorsPreflightArgs> & aCorsPreflightArgs)754 mozilla::ipc::IPCResult HttpChannelParent::RecvRedirect2Verify(
755     const nsresult& aResult, const RequestHeaderTuples& changedHeaders,
756     const uint32_t& aSourceRequestBlockingReason,
757     const Maybe<ChildLoadInfoForwarderArgs>& aTargetLoadInfoForwarder,
758     const uint32_t& loadFlags, nsIReferrerInfo* aReferrerInfo,
759     const Maybe<URIParams>& aAPIRedirectURI,
760     const Maybe<CorsPreflightArgs>& aCorsPreflightArgs) {
761   LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
762        this, static_cast<uint32_t>(aResult)));
763 
764   // Result from the child.  If something fails here, we might overwrite a
765   // success with a further failure.
766   nsresult result = aResult;
767 
768   // Local results.
769   nsresult rv;
770 
771   if (NS_SUCCEEDED(result)) {
772     nsCOMPtr<nsIHttpChannel> newHttpChannel =
773         do_QueryInterface(mRedirectChannel);
774 
775     if (newHttpChannel) {
776       nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
777 
778       if (apiRedirectUri) {
779         rv = newHttpChannel->RedirectTo(apiRedirectUri);
780         MOZ_ASSERT(NS_SUCCEEDED(rv));
781       }
782 
783       for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
784         if (changedHeaders[i].mEmpty) {
785           rv = newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
786         } else {
787           rv = newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
788                                                 changedHeaders[i].mValue,
789                                                 changedHeaders[i].mMerge);
790         }
791         MOZ_ASSERT(NS_SUCCEEDED(rv));
792       }
793 
794       // A successfully redirected channel must have the LOAD_REPLACE flag.
795       MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE);
796       if (loadFlags & nsIChannel::LOAD_REPLACE) {
797         newHttpChannel->SetLoadFlags(loadFlags);
798       }
799 
800       if (aCorsPreflightArgs.isSome()) {
801         nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
802             do_QueryInterface(newHttpChannel);
803         MOZ_RELEASE_ASSERT(newInternalChannel);
804         const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
805         newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
806                                                        false);
807       }
808 
809       if (aReferrerInfo) {
810         RefPtr<HttpBaseChannel> baseChannel = do_QueryObject(newHttpChannel);
811         MOZ_ASSERT(baseChannel);
812         if (baseChannel) {
813           // Referrer header is computed in child no need to recompute here
814           rv = baseChannel->SetReferrerInfoInternal(aReferrerInfo, false, false,
815                                                     true);
816           MOZ_ASSERT(NS_SUCCEEDED(rv));
817         }
818       }
819 
820       if (aTargetLoadInfoForwarder.isSome()) {
821         nsCOMPtr<nsILoadInfo> newLoadInfo = newHttpChannel->LoadInfo();
822         rv = MergeChildLoadInfoForwarder(aTargetLoadInfoForwarder.ref(),
823                                          newLoadInfo);
824         if (NS_FAILED(rv) && NS_SUCCEEDED(result)) {
825           result = rv;
826         }
827       }
828     }
829   }
830 
831   // If the redirect is vetoed, reason is set on the source (current) channel's
832   // load info, so we must carry iver the change.
833   // The channel may have already been cleaned up, so there is nothing we can
834   // do.
835   if (MOZ_UNLIKELY(aSourceRequestBlockingReason !=
836                    nsILoadInfo::BLOCKING_REASON_NONE) &&
837       mChannel) {
838     nsCOMPtr<nsILoadInfo> sourceLoadInfo = mChannel->LoadInfo();
839     sourceLoadInfo->SetRequestBlockingReason(aSourceRequestBlockingReason);
840   }
841 
842   // Continue the verification procedure if child has veto the redirection.
843   if (NS_FAILED(result)) {
844     ContinueRedirect2Verify(result);
845     return IPC_OK();
846   }
847 
848   // Wait for background channel ready on target channel
849   nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
850       RedirectChannelRegistrar::GetOrCreate();
851   MOZ_ASSERT(redirectReg);
852 
853   nsCOMPtr<nsIParentChannel> redirectParentChannel;
854   rv = redirectReg->GetParentChannel(mRedirectChannelId,
855                                      getter_AddRefs(redirectParentChannel));
856   MOZ_ASSERT(redirectParentChannel);
857   if (!redirectParentChannel) {
858     ContinueRedirect2Verify(rv);
859     return IPC_OK();
860   }
861 
862   nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
863       do_QueryInterface(redirectParentChannel);
864   if (!redirectedParent) {
865     // Continue verification procedure if redirecting to non-Http protocol
866     ContinueRedirect2Verify(result);
867     return IPC_OK();
868   }
869 
870   // Ask redirected channel if verification can proceed.
871   // ContinueRedirect2Verify will be invoked when redirected channel is ready.
872   redirectedParent->ContinueVerification(this);
873 
874   return IPC_OK();
875 }
876 
877 // from nsIParentRedirectingChannel
878 NS_IMETHODIMP
ContinueVerification(nsIAsyncVerifyRedirectReadyCallback * aCallback)879 HttpChannelParent::ContinueVerification(
880     nsIAsyncVerifyRedirectReadyCallback* aCallback) {
881   LOG(("HttpChannelParent::ContinueVerification [this=%p callback=%p]\n", this,
882        aCallback));
883 
884   MOZ_ASSERT(NS_IsMainThread());
885   MOZ_ASSERT(aCallback);
886 
887   // Continue the verification procedure if background channel is ready.
888   if (mBgParent) {
889     aCallback->ReadyToVerify(NS_OK);
890     return NS_OK;
891   }
892 
893   // ConnectChannel must be received before Redirect2Verify.
894   MOZ_ASSERT(!mPromise.IsEmpty());
895 
896   // Otherwise, wait for the background channel.
897   nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
898   WaitForBgParent()->Then(
899       GetMainThreadSerialEventTarget(), __func__,
900       [callback]() { callback->ReadyToVerify(NS_OK); },
901       [callback](const nsresult& aResult) {
902         NS_ERROR("failed to establish the background channel");
903         callback->ReadyToVerify(aResult);
904       });
905   return NS_OK;
906 }
907 
ContinueRedirect2Verify(const nsresult & aResult)908 void HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult) {
909   LOG(("HttpChannelParent::ContinueRedirect2Verify [this=%p result=%" PRIx32
910        "]\n",
911        this, static_cast<uint32_t>(aResult)));
912 
913   if (!mRedirectCallback) {
914     // This should according the logic never happen, log the situation.
915     if (mReceivedRedirect2Verify) {
916       LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
917     }
918     if (mSentRedirect1BeginFailed) {
919       LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
920     }
921     if ((mRedirectChannelId > 0) && NS_FAILED(aResult)) {
922       LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
923     }
924     if ((mRedirectChannelId > 0) && NS_SUCCEEDED(aResult)) {
925       LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
926     }
927     if (!mRedirectChannel) {
928       LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
929     }
930 
931     NS_ERROR(
932         "Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
933         "mRedirectCallback null");
934   }
935 
936   mReceivedRedirect2Verify = true;
937 
938   if (mRedirectCallback) {
939     LOG(
940         ("HttpChannelParent::ContinueRedirect2Verify call "
941          "OnRedirectVerifyCallback"
942          " [this=%p result=%" PRIx32 ", mRedirectCallback=%p]\n",
943          this, static_cast<uint32_t>(aResult), mRedirectCallback.get()));
944     mRedirectCallback->OnRedirectVerifyCallback(aResult);
945     mRedirectCallback = nullptr;
946   }
947 }
948 
RecvDocumentChannelCleanup(const bool & clearCacheEntry)949 mozilla::ipc::IPCResult HttpChannelParent::RecvDocumentChannelCleanup(
950     const bool& clearCacheEntry) {
951   CleanupBackgroundChannel();  // Background channel can be closed.
952   mChannel = nullptr;          // Reclaim some memory sooner.
953   if (clearCacheEntry) {
954     mCacheEntry = nullptr;  // Else we'll block other channels reading same URI
955   }
956   return IPC_OK();
957 }
958 
RecvRemoveCorsPreflightCacheEntry(const URIParams & uri,const mozilla::ipc::PrincipalInfo & requestingPrincipal,const OriginAttributes & originAttributes)959 mozilla::ipc::IPCResult HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(
960     const URIParams& uri,
961     const mozilla::ipc::PrincipalInfo& requestingPrincipal,
962     const OriginAttributes& originAttributes) {
963   nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(uri);
964   if (!deserializedURI) {
965     return IPC_FAIL_NO_REASON(this);
966   }
967   auto principalOrErr = PrincipalInfoToPrincipal(requestingPrincipal);
968   if (NS_WARN_IF(principalOrErr.isErr())) {
969     return IPC_FAIL_NO_REASON(this);
970   }
971   nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
972   nsCORSListenerProxy::RemoveFromCorsPreflightCache(deserializedURI, principal,
973                                                     originAttributes);
974   return IPC_OK();
975 }
976 
977 //-----------------------------------------------------------------------------
978 // HttpChannelParent::nsIRequestObserver
979 //-----------------------------------------------------------------------------
980 
GetTimingAttributes(HttpBaseChannel * aChannel)981 static ResourceTimingStructArgs GetTimingAttributes(HttpBaseChannel* aChannel) {
982   ResourceTimingStructArgs args;
983   TimeStamp timeStamp;
984   aChannel->GetDomainLookupStart(&timeStamp);
985   args.domainLookupStart() = timeStamp;
986   aChannel->GetDomainLookupEnd(&timeStamp);
987   args.domainLookupEnd() = timeStamp;
988   aChannel->GetConnectStart(&timeStamp);
989   args.connectStart() = timeStamp;
990   aChannel->GetTcpConnectEnd(&timeStamp);
991   args.tcpConnectEnd() = timeStamp;
992   aChannel->GetSecureConnectionStart(&timeStamp);
993   args.secureConnectionStart() = timeStamp;
994   aChannel->GetConnectEnd(&timeStamp);
995   args.connectEnd() = timeStamp;
996   aChannel->GetRequestStart(&timeStamp);
997   args.requestStart() = timeStamp;
998   aChannel->GetResponseStart(&timeStamp);
999   args.responseStart() = timeStamp;
1000   aChannel->GetResponseEnd(&timeStamp);
1001   args.responseEnd() = timeStamp;
1002   aChannel->GetAsyncOpen(&timeStamp);
1003   args.fetchStart() = timeStamp;
1004   aChannel->GetRedirectStart(&timeStamp);
1005   args.redirectStart() = timeStamp;
1006   aChannel->GetRedirectEnd(&timeStamp);
1007   args.redirectEnd() = timeStamp;
1008 
1009   uint64_t size = 0;
1010   aChannel->GetTransferSize(&size);
1011   args.transferSize() = size;
1012 
1013   aChannel->GetEncodedBodySize(&size);
1014   args.encodedBodySize() = size;
1015   // decodedBodySize can be computed in the child process so it doesn't need
1016   // to be passed down.
1017 
1018   nsCString protocolVersion;
1019   aChannel->GetProtocolVersion(protocolVersion);
1020   args.protocolVersion() = protocolVersion;
1021 
1022   aChannel->GetCacheReadStart(&timeStamp);
1023   args.cacheReadStart() = timeStamp;
1024 
1025   aChannel->GetCacheReadEnd(&timeStamp);
1026   args.cacheReadEnd() = timeStamp;
1027   return args;
1028 }
1029 
1030 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)1031 HttpChannelParent::OnStartRequest(nsIRequest* aRequest) {
1032   nsresult rv;
1033 
1034   LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n", this,
1035        aRequest));
1036   MOZ_ASSERT(NS_IsMainThread());
1037 
1038   Maybe<uint32_t> multiPartID;
1039   bool isFirstPartOfMultiPart = false;
1040   bool isLastPartOfMultiPart = false;
1041   DebugOnly<bool> isMultiPart = false;
1042 
1043   RefPtr<HttpBaseChannel> chan = do_QueryObject(aRequest);
1044   if (!chan) {
1045     if (nsCOMPtr<nsIMultiPartChannel> multiPartChannel =
1046             do_QueryInterface(aRequest)) {
1047       isMultiPart = true;
1048       nsCOMPtr<nsIChannel> baseChannel;
1049       multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
1050       chan = do_QueryObject(baseChannel);
1051 
1052       uint32_t partID = 0;
1053       multiPartChannel->GetPartID(&partID);
1054       multiPartID = Some(partID);
1055       multiPartChannel->GetIsFirstPart(&isFirstPartOfMultiPart);
1056       multiPartChannel->GetIsLastPart(&isLastPartOfMultiPart);
1057     } else if (nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
1058                    do_QueryInterface(aRequest)) {
1059       chan = do_QueryObject(viewSourceChannel->GetInnerChannel());
1060     }
1061   }
1062   MOZ_ASSERT(multiPartID || !isMultiPart, "Changed multi-part state?");
1063 
1064   if (!chan) {
1065     LOG(("  aRequest is not HttpBaseChannel"));
1066     NS_ERROR(
1067         "Expecting only HttpBaseChannel as aRequest in "
1068         "HttpChannelParent::OnStartRequest");
1069     return NS_ERROR_UNEXPECTED;
1070   }
1071 
1072   mAfterOnStartRequestBegun = true;
1073 
1074   // Todo: re-enable when bug 1589749 is fixed.
1075   /*MOZ_ASSERT(mChannel == chan,
1076              "HttpChannelParent getting OnStartRequest from a different "
1077              "HttpBaseChannel instance");*/
1078 
1079   HttpChannelOnStartRequestArgs args;
1080 
1081   // Send down any permissions/cookies which are relevant to this URL if we are
1082   // performing a document load. We can't do that if mIPCClosed is set.
1083   if (!mIPCClosed) {
1084     PContentParent* pcp = Manager()->Manager();
1085     MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
1086     DebugOnly<nsresult> rv =
1087         static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpDocumentForChild(
1088             chan, &args.shouldWaitForOnStartRequestSent());
1089     MOZ_ASSERT(NS_SUCCEEDED(rv));
1090   }
1091 
1092   args.multiPartID() = multiPartID;
1093   args.isFirstPartOfMultiPart() = isFirstPartOfMultiPart;
1094   args.isLastPartOfMultiPart() = isLastPartOfMultiPart;
1095 
1096   args.cacheExpirationTime() = nsICacheEntry::NO_EXPIRATION_TIME;
1097 
1098   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(chan);
1099 
1100   if (httpChannelImpl) {
1101     httpChannelImpl->IsFromCache(&args.isFromCache());
1102     httpChannelImpl->IsRacing(&args.isRacing());
1103     httpChannelImpl->GetCacheEntryId(&args.cacheEntryId());
1104     httpChannelImpl->GetCacheTokenFetchCount(&args.cacheFetchCount());
1105     httpChannelImpl->GetCacheTokenExpirationTime(&args.cacheExpirationTime());
1106 
1107     mDataSentToChildProcess = httpChannelImpl->DataSentToChildProcess();
1108 
1109     // If RCWN is enabled and cache wins, we can't use the ODA from socket
1110     // process.
1111     if (args.isRacing()) {
1112       mDataSentToChildProcess =
1113           httpChannelImpl->DataSentToChildProcess() && !args.isFromCache();
1114     }
1115     args.dataFromSocketProcess() = mDataSentToChildProcess;
1116   }
1117 
1118   // Propagate whether or not conversion should occur from the parent-side
1119   // channel to the child-side channel.  Then disable the parent-side
1120   // conversion so that it only occurs in the child.
1121   Unused << chan->GetApplyConversion(&args.applyConversion());
1122   chan->SetApplyConversion(false);
1123 
1124   // If we've already applied the conversion (as can happen if we installed
1125   // a multipart converted), then don't apply it again on the child.
1126   if (chan->HasAppliedConversion()) {
1127     args.applyConversion() = false;
1128   }
1129 
1130   chan->GetStatus(&args.channelStatus());
1131 
1132   // Keep the cache entry for future use when opening alternative streams.
1133   // It could be already released by nsHttpChannel at that time.
1134   nsCOMPtr<nsISupports> cacheEntry;
1135 
1136   if (httpChannelImpl) {
1137     httpChannelImpl->GetCacheToken(getter_AddRefs(cacheEntry));
1138     mCacheEntry = do_QueryInterface(cacheEntry);
1139     args.cacheEntryAvailable() = static_cast<bool>(mCacheEntry);
1140 
1141     httpChannelImpl->GetCacheKey(&args.cacheKey());
1142     httpChannelImpl->GetAlternativeDataType(args.altDataType());
1143   }
1144 
1145   args.altDataLength() = chan->GetAltDataLength();
1146   args.deliveringAltData() = chan->IsDeliveringAltData();
1147 
1148   UpdateAndSerializeSecurityInfo(args.securityInfoSerialization());
1149 
1150   chan->GetRedirectCount(&args.redirectCount());
1151   chan->GetHasHTTPSRR(&args.hasHTTPSRR());
1152 
1153   nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
1154   mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1155                                                   &args.loadInfoForwarder());
1156 
1157   nsHttpResponseHead* responseHead = chan->GetResponseHead();
1158   bool useResponseHead = !!responseHead;
1159   nsHttpResponseHead cleanedUpResponseHead;
1160 
1161   if (responseHead &&
1162       (responseHead->HasHeader(nsHttp::Set_Cookie) || multiPartID)) {
1163     cleanedUpResponseHead = *responseHead;
1164     cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1165     if (multiPartID) {
1166       nsCOMPtr<nsIChannel> multiPartChannel = do_QueryInterface(aRequest);
1167       // For the multipart channel, use the parsed subtype instead. Note that
1168       // `chan` is the underlying base channel of the multipart channel in this
1169       // case, which is different from `multiPartChannel`.
1170       MOZ_ASSERT(multiPartChannel);
1171       nsAutoCString contentType;
1172       multiPartChannel->GetContentType(contentType);
1173       cleanedUpResponseHead.SetContentType(contentType);
1174     }
1175     responseHead = &cleanedUpResponseHead;
1176   }
1177 
1178   if (!responseHead) {
1179     responseHead = &cleanedUpResponseHead;
1180   }
1181 
1182   chan->GetIsResolvedByTRR(&args.isResolvedByTRR());
1183   chan->GetAllRedirectsSameOrigin(&args.allRedirectsSameOrigin());
1184   chan->GetCrossOriginOpenerPolicy(&args.openerPolicy());
1185   args.selfAddr() = chan->GetSelfAddr();
1186   args.peerAddr() = chan->GetPeerAddr();
1187   args.timing() = GetTimingAttributes(mChannel);
1188   if (mOverrideReferrerInfo) {
1189     args.overrideReferrerInfo() = ToRefPtr(std::move(mOverrideReferrerInfo));
1190   }
1191   if (!mCookie.IsEmpty()) {
1192     args.cookie() = std::move(mCookie);
1193   }
1194 
1195   nsHttpRequestHead* requestHead = chan->GetRequestHead();
1196   // !!! We need to lock headers and please don't forget to unlock them !!!
1197   requestHead->Enter();
1198 
1199   nsHttpHeaderArray cleanedUpRequestHeaders;
1200   bool cleanedUpRequest = false;
1201   if (requestHead->HasHeader(nsHttp::Cookie)) {
1202     cleanedUpRequestHeaders = requestHead->Headers();
1203     cleanedUpRequestHeaders.ClearHeader(nsHttp::Cookie);
1204     cleanedUpRequest = true;
1205   }
1206 
1207   rv = NS_OK;
1208 
1209   nsCOMPtr<nsICacheEntry> altDataSource;
1210   nsCOMPtr<nsICacheInfoChannel> cacheChannel =
1211       do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1212   if (cacheChannel) {
1213     for (const auto& pref : cacheChannel->PreferredAlternativeDataTypes()) {
1214       if (pref.type() == args.altDataType() &&
1215           pref.deliverAltData() ==
1216               nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::
1217                   SERIALIZE) {
1218         altDataSource = mCacheEntry;
1219         break;
1220       }
1221     }
1222   }
1223 
1224   if (mIPCClosed ||
1225       !mBgParent->OnStartRequest(
1226           *responseHead, useResponseHead,
1227           cleanedUpRequest ? cleanedUpRequestHeaders : requestHead->Headers(),
1228           args, altDataSource)) {
1229     rv = NS_ERROR_UNEXPECTED;
1230   }
1231   requestHead->Exit();
1232 
1233   // Need to wait for the cookies/permissions to content process, which is sent
1234   // via PContent in AboutToLoadHttpFtpDocumentForChild. For multipart channel,
1235   // send only one time since the cookies/permissions are the same.
1236   if (NS_SUCCEEDED(rv) && args.shouldWaitForOnStartRequestSent() &&
1237       multiPartID.valueOr(0) == 0) {
1238     LOG(("HttpChannelParent::SendOnStartRequestSent\n"));
1239     Unused << SendOnStartRequestSent();
1240   }
1241 
1242   return rv;
1243 }
1244 
1245 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)1246 HttpChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1247   LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%" PRIx32
1248        "]\n",
1249        this, aRequest, static_cast<uint32_t>(aStatusCode)));
1250   MOZ_ASSERT(NS_IsMainThread());
1251 
1252   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1253   if (httpChannelImpl) {
1254     httpChannelImpl->SetWarningReporter(nullptr);
1255   }
1256 
1257   nsHttpHeaderArray* responseTrailer = mChannel->GetResponseTrailers();
1258 
1259   nsTArray<ConsoleReportCollected> consoleReports;
1260 
1261   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(mChannel);
1262   if (httpChannel) {
1263     httpChannel->StealConsoleReports(consoleReports);
1264   }
1265 
1266   // Either IPC channel is closed or background channel
1267   // is ready to send OnStopRequest.
1268   MOZ_ASSERT(mIPCClosed || mBgParent);
1269 
1270   if (mDataSentToChildProcess) {
1271     if (mIPCClosed || !mBgParent ||
1272         !mBgParent->OnConsoleReport(consoleReports)) {
1273       return NS_ERROR_UNEXPECTED;
1274     }
1275     return NS_OK;
1276   }
1277 
1278   // If we're handling a multi-part stream, then send this directly
1279   // over PHttpChannel to make synchronization easier.
1280   if (mIPCClosed || !mBgParent ||
1281       !mBgParent->OnStopRequest(
1282           aStatusCode, GetTimingAttributes(mChannel),
1283           responseTrailer ? *responseTrailer : nsHttpHeaderArray(),
1284           consoleReports)) {
1285     return NS_ERROR_UNEXPECTED;
1286   }
1287 
1288   if (NeedFlowControl()) {
1289     bool isLocal = false;
1290     NetAddr peerAddr = mChannel->GetPeerAddr();
1291 
1292 #if defined(XP_UNIX)
1293     // Unix-domain sockets are always local.
1294     isLocal = (peerAddr.raw.family == PR_AF_LOCAL);
1295 #endif
1296 
1297     isLocal = isLocal || peerAddr.IsLoopbackAddr();
1298 
1299     if (!isLocal) {
1300       if (!mHasSuspendedByBackPressure) {
1301         AccumulateCategorical(
1302             Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1303                 NotSuspended);
1304       } else {
1305         AccumulateCategorical(
1306             Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1307                 Suspended);
1308 
1309         // Only analyze non-local suspended cases, which we are interested in.
1310         nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1311         Telemetry::Accumulate(
1312             Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_CP_TYPE,
1313             loadInfo->InternalContentPolicyType());
1314       }
1315     } else {
1316       if (!mHasSuspendedByBackPressure) {
1317         AccumulateCategorical(
1318             Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1319                 NotSuspendedLocal);
1320       } else {
1321         AccumulateCategorical(
1322             Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1323                 SuspendedLocal);
1324       }
1325     }
1326   }
1327   return NS_OK;
1328 }
1329 
1330 //-----------------------------------------------------------------------------
1331 // HttpChannelParent::nsIMultiPartChannelListener
1332 //-----------------------------------------------------------------------------
1333 
1334 NS_IMETHODIMP
OnAfterLastPart(nsresult aStatus)1335 HttpChannelParent::OnAfterLastPart(nsresult aStatus) {
1336   LOG(("HttpChannelParent::OnAfterLastPart [this=%p]\n", this));
1337   MOZ_ASSERT(NS_IsMainThread());
1338 
1339   // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1340   if (mIPCClosed) {
1341     return NS_OK;
1342   }
1343 
1344   // If IPC channel is open, background channel should be ready to send
1345   // OnAfterLastPart.
1346   MOZ_ASSERT(mBgParent);
1347 
1348   if (!mBgParent || !mBgParent->OnAfterLastPart(aStatus)) {
1349     return NS_ERROR_UNEXPECTED;
1350   }
1351 
1352   return NS_OK;
1353 }
1354 
1355 //-----------------------------------------------------------------------------
1356 // HttpChannelParent::nsIStreamListener
1357 //-----------------------------------------------------------------------------
1358 
1359 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)1360 HttpChannelParent::OnDataAvailable(nsIRequest* aRequest,
1361                                    nsIInputStream* aInputStream,
1362                                    uint64_t aOffset, uint32_t aCount) {
1363   LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p offset=%" PRIu64
1364        " count=%" PRIu32 "]\n",
1365        this, aRequest, aOffset, aCount));
1366   MOZ_ASSERT(NS_IsMainThread());
1367 
1368   if (mDataSentToChildProcess) {
1369     uint32_t n;
1370     return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &n);
1371   }
1372 
1373   nsresult channelStatus = NS_OK;
1374   mChannel->GetStatus(&channelStatus);
1375 
1376   nsresult transportStatus = NS_NET_STATUS_RECEIVING_FROM;
1377   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1378   if (httpChannelImpl) {
1379     if (httpChannelImpl->IsReadingFromCache()) {
1380       transportStatus = NS_NET_STATUS_READING;
1381     }
1382   }
1383 
1384   nsCString data;
1385   nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
1386   if (NS_FAILED(rv)) {
1387     return rv;
1388   }
1389 
1390   // Either IPC channel is closed or background channel
1391   // is ready to send OnTransportAndData.
1392   MOZ_ASSERT(mIPCClosed || mBgParent);
1393 
1394   if (mIPCClosed || !mBgParent ||
1395       !mBgParent->OnTransportAndData(channelStatus, transportStatus, aOffset,
1396                                      aCount, data)) {
1397     return NS_ERROR_UNEXPECTED;
1398   }
1399 
1400   int32_t count = static_cast<int32_t>(aCount);
1401 
1402   if (NeedFlowControl()) {
1403     // We're going to run out of sending window size
1404     if (mSendWindowSize > 0 && mSendWindowSize <= count) {
1405       MOZ_ASSERT(!mSuspendedForFlowControl);
1406       LOG(("  suspend the channel due to e10s backpressure"));
1407       Unused << mChannel->Suspend();
1408       mSuspendedForFlowControl = true;
1409       mHasSuspendedByBackPressure = true;
1410     } else if (!mResumedTimestamp.IsNull()) {
1411       // Calculate the delay when the first packet arrived after resume
1412       Telemetry::AccumulateTimeDelta(
1413           Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS,
1414           mResumedTimestamp);
1415       mResumedTimestamp = TimeStamp();
1416     }
1417     mSendWindowSize -= count;
1418   }
1419 
1420   return NS_OK;
1421 }
1422 
NeedFlowControl()1423 bool HttpChannelParent::NeedFlowControl() {
1424   if (mCacheNeedFlowControlInitialized) {
1425     return mNeedFlowControl;
1426   }
1427 
1428   int64_t contentLength = -1;
1429 
1430   RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1431 
1432   // By design, we won't trigger the flow control if
1433   // a. pref-out
1434   // b. the resource is from cache or partial cache
1435   // c. the resource is small
1436   // d. data will be sent from socket process to child process directly
1437   // Note that we served the cached resource first for partical cache, which is
1438   // ignored here since we only take the first ODA into consideration.
1439   if (gHttpHandler->SendWindowSize() == 0 || !httpChannelImpl ||
1440       httpChannelImpl->IsReadingFromCache() ||
1441       NS_FAILED(httpChannelImpl->GetContentLength(&contentLength)) ||
1442       contentLength < gHttpHandler->SendWindowSize() ||
1443       mDataSentToChildProcess) {
1444     mNeedFlowControl = false;
1445   }
1446   mCacheNeedFlowControlInitialized = true;
1447   return mNeedFlowControl;
1448 }
1449 
RecvBytesRead(const int32_t & aCount)1450 mozilla::ipc::IPCResult HttpChannelParent::RecvBytesRead(
1451     const int32_t& aCount) {
1452   if (!NeedFlowControl()) {
1453     return IPC_OK();
1454   }
1455 
1456   LOG(("HttpChannelParent::RecvBytesRead [this=%p count=%" PRId32 "]\n", this,
1457        aCount));
1458 
1459   if (mSendWindowSize <= 0 && mSendWindowSize + aCount > 0) {
1460     MOZ_ASSERT(mSuspendedForFlowControl);
1461     LOG(("  resume the channel due to e10s backpressure relief"));
1462     Unused << mChannel->Resume();
1463     mSuspendedForFlowControl = false;
1464 
1465     mResumedTimestamp = TimeStamp::Now();
1466   }
1467   mSendWindowSize += aCount;
1468   return IPC_OK();
1469 }
1470 
RecvOpenOriginalCacheInputStream()1471 mozilla::ipc::IPCResult HttpChannelParent::RecvOpenOriginalCacheInputStream() {
1472   if (mIPCClosed) {
1473     return IPC_OK();
1474   }
1475   AutoIPCStream autoStream;
1476   if (mCacheEntry) {
1477     nsCOMPtr<nsIInputStream> inputStream;
1478     nsresult rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
1479     if (NS_SUCCEEDED(rv)) {
1480       PContentParent* pcp = Manager()->Manager();
1481       Unused << autoStream.Serialize(inputStream,
1482                                      static_cast<ContentParent*>(pcp));
1483     }
1484   }
1485 
1486   Unused << SendOriginalCacheInputStreamAvailable(
1487       autoStream.TakeOptionalValue());
1488   return IPC_OK();
1489 }
1490 
1491 //-----------------------------------------------------------------------------
1492 // HttpChannelParent::nsIProgressEventSink
1493 //-----------------------------------------------------------------------------
1494 
1495 NS_IMETHODIMP
OnProgress(nsIRequest * aRequest,int64_t aProgress,int64_t aProgressMax)1496 HttpChannelParent::OnProgress(nsIRequest* aRequest, int64_t aProgress,
1497                               int64_t aProgressMax) {
1498   LOG(("HttpChannelParent::OnProgress [this=%p progress=%" PRId64 "max=%" PRId64
1499        "]\n",
1500        this, aProgress, aProgressMax));
1501   MOZ_ASSERT(NS_IsMainThread());
1502 
1503   // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1504   if (mIPCClosed) {
1505     return NS_OK;
1506   }
1507 
1508   // If it indicates this precedes OnDataAvailable, child can derive the value
1509   // in ODA.
1510   if (mIgnoreProgress) {
1511     mIgnoreProgress = false;
1512     return NS_OK;
1513   }
1514 
1515   // If IPC channel is open, background channel should be ready to send
1516   // OnProgress.
1517   MOZ_ASSERT(mBgParent);
1518 
1519   // Send OnProgress events to the child for data upload progress notifications
1520   // (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
1521   // LOAD_BACKGROUND set.
1522   if (!mBgParent || !mBgParent->OnProgress(aProgress, aProgressMax)) {
1523     return NS_ERROR_UNEXPECTED;
1524   }
1525 
1526   return NS_OK;
1527 }
1528 
1529 NS_IMETHODIMP
OnStatus(nsIRequest * aRequest,nsresult aStatus,const char16_t * aStatusArg)1530 HttpChannelParent::OnStatus(nsIRequest* aRequest, nsresult aStatus,
1531                             const char16_t* aStatusArg) {
1532   LOG(("HttpChannelParent::OnStatus [this=%p status=%" PRIx32 "]\n", this,
1533        static_cast<uint32_t>(aStatus)));
1534   MOZ_ASSERT(NS_IsMainThread());
1535 
1536   // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1537   if (mIPCClosed) {
1538     return NS_OK;
1539   }
1540 
1541   // If this precedes OnDataAvailable, transportStatus will be derived in ODA.
1542   if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
1543       aStatus == NS_NET_STATUS_READING) {
1544     // The transport status and progress generated by ODA will be coalesced
1545     // into one IPC message. Therefore, we can ignore the next OnProgress event
1546     // since it is generated by ODA as well.
1547     mIgnoreProgress = true;
1548     return NS_OK;
1549   }
1550 
1551   // If IPC channel is open, background channel should be ready to send
1552   // OnStatus.
1553   MOZ_ASSERT(mIPCClosed || mBgParent);
1554 
1555   // Otherwise, send to child now
1556   if (!mBgParent || !mBgParent->OnStatus(aStatus)) {
1557     return NS_ERROR_UNEXPECTED;
1558   }
1559 
1560   return NS_OK;
1561 }
1562 
1563 //-----------------------------------------------------------------------------
1564 // HttpChannelParent::nsIParentChannel
1565 //-----------------------------------------------------------------------------
1566 
1567 NS_IMETHODIMP
SetParentListener(ParentChannelListener * aListener)1568 HttpChannelParent::SetParentListener(ParentChannelListener* aListener) {
1569   LOG(("HttpChannelParent::SetParentListener [this=%p aListener=%p]\n", this,
1570        aListener));
1571   MOZ_ASSERT(aListener);
1572   MOZ_ASSERT(!mParentListener,
1573              "SetParentListener should only be called for "
1574              "new HttpChannelParents after a redirect, when "
1575              "mParentListener is null.");
1576   mParentListener = aListener;
1577   return NS_OK;
1578 }
1579 
1580 NS_IMETHODIMP
SetClassifierMatchedInfo(const nsACString & aList,const nsACString & aProvider,const nsACString & aFullHash)1581 HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
1582                                             const nsACString& aProvider,
1583                                             const nsACString& aFullHash) {
1584   LOG(("HttpChannelParent::SetClassifierMatchedInfo [this=%p]\n", this));
1585   if (!mIPCClosed) {
1586     MOZ_ASSERT(mBgParent);
1587     Unused << mBgParent->OnSetClassifierMatchedInfo(aList, aProvider,
1588                                                     aFullHash);
1589   }
1590   return NS_OK;
1591 }
1592 
1593 NS_IMETHODIMP
SetClassifierMatchedTrackingInfo(const nsACString & aLists,const nsACString & aFullHashes)1594 HttpChannelParent::SetClassifierMatchedTrackingInfo(
1595     const nsACString& aLists, const nsACString& aFullHashes) {
1596   LOG(("HttpChannelParent::SetClassifierMatchedTrackingInfo [this=%p]\n",
1597        this));
1598   if (!mIPCClosed) {
1599     MOZ_ASSERT(mBgParent);
1600     Unused << mBgParent->OnSetClassifierMatchedTrackingInfo(aLists,
1601                                                             aFullHashes);
1602   }
1603   return NS_OK;
1604 }
1605 
1606 NS_IMETHODIMP
NotifyClassificationFlags(uint32_t aClassificationFlags,bool aIsThirdParty)1607 HttpChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
1608                                              bool aIsThirdParty) {
1609   LOG(
1610       ("HttpChannelParent::NotifyClassificationFlags "
1611        "classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
1612        aClassificationFlags, static_cast<int>(aIsThirdParty), this));
1613   if (!mIPCClosed) {
1614     MOZ_ASSERT(mBgParent);
1615     Unused << mBgParent->OnNotifyClassificationFlags(aClassificationFlags,
1616                                                      aIsThirdParty);
1617   }
1618   return NS_OK;
1619 }
1620 
1621 NS_IMETHODIMP
NotifyFlashPluginStateChanged(nsIHttpChannel::FlashPluginState aState)1622 HttpChannelParent::NotifyFlashPluginStateChanged(
1623     nsIHttpChannel::FlashPluginState aState) {
1624   LOG(("HttpChannelParent::NotifyFlashPluginStateChanged [this=%p]\n", this));
1625   if (!mIPCClosed) {
1626     MOZ_ASSERT(mBgParent);
1627     Unused << mBgParent->OnNotifyFlashPluginStateChanged(aState);
1628   }
1629   return NS_OK;
1630 }
1631 
1632 NS_IMETHODIMP
Delete()1633 HttpChannelParent::Delete() {
1634   if (!mIPCClosed) Unused << DoSendDeleteSelf();
1635 
1636   return NS_OK;
1637 }
1638 
1639 NS_IMETHODIMP
GetRemoteType(nsACString & aRemoteType)1640 HttpChannelParent::GetRemoteType(nsACString& aRemoteType) {
1641   if (!CanSend()) {
1642     return NS_ERROR_UNEXPECTED;
1643   }
1644 
1645   dom::PContentParent* pcp = Manager()->Manager();
1646   aRemoteType = static_cast<dom::ContentParent*>(pcp)->GetRemoteType();
1647   return NS_OK;
1648 }
1649 
1650 //-----------------------------------------------------------------------------
1651 // HttpChannelParent::nsIParentRedirectingChannel
1652 //-----------------------------------------------------------------------------
1653 
1654 NS_IMETHODIMP
StartRedirect(nsIChannel * newChannel,uint32_t redirectFlags,nsIAsyncVerifyRedirectCallback * callback)1655 HttpChannelParent::StartRedirect(nsIChannel* newChannel, uint32_t redirectFlags,
1656                                  nsIAsyncVerifyRedirectCallback* callback) {
1657   nsresult rv;
1658 
1659   LOG(("HttpChannelParent::StartRedirect [this=%p, newChannel=%p callback=%p]",
1660        this, newChannel, callback));
1661 
1662   // Register the new channel and obtain id for it
1663   nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
1664       RedirectChannelRegistrar::GetOrCreate();
1665   MOZ_ASSERT(registrar);
1666 
1667   mRedirectChannelId = nsContentUtils::GenerateLoadIdentifier();
1668   rv = registrar->RegisterChannel(newChannel, mRedirectChannelId);
1669   NS_ENSURE_SUCCESS(rv, rv);
1670 
1671   LOG(("Registered %p channel under id=%" PRIx64, newChannel,
1672        mRedirectChannelId));
1673 
1674   if (mIPCClosed) {
1675     return NS_BINDING_ABORTED;
1676   }
1677 
1678   // If this is an internal redirect for service worker interception, then
1679   // hide it from the child process.  The original e10s interception code
1680   // was not designed with this in mind and its not necessary to replace
1681   // the HttpChannelChild/Parent objects in this case.
1682   if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
1683     nsCOMPtr<nsIInterceptedChannel> oldIntercepted =
1684         do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1685     nsCOMPtr<nsIInterceptedChannel> newIntercepted =
1686         do_QueryInterface(newChannel);
1687 
1688     // We only want to hide the special internal redirect from nsHttpChannel
1689     // to InterceptedHttpChannel.  We want to allow through internal redirects
1690     // initiated from the InterceptedHttpChannel even if they are to another
1691     // InterceptedHttpChannel.
1692     if (!oldIntercepted && newIntercepted) {
1693       // We need to move across the reserved and initial client information
1694       // to the new channel.  Normally this would be handled by the child
1695       // ClientChannelHelper, but that is not notified of this redirect since
1696       // we're not propagating it back to the child process.
1697       nsCOMPtr<nsILoadInfo> oldLoadInfo = mChannel->LoadInfo();
1698 
1699       nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->LoadInfo();
1700 
1701       Maybe<ClientInfo> reservedClientInfo(
1702           oldLoadInfo->GetReservedClientInfo());
1703       if (reservedClientInfo.isSome()) {
1704         newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
1705       }
1706 
1707       Maybe<ClientInfo> initialClientInfo(oldLoadInfo->GetInitialClientInfo());
1708       if (initialClientInfo.isSome()) {
1709         newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
1710       }
1711 
1712       // Re-link the HttpChannelParent to the new InterceptedHttpChannel.
1713       nsCOMPtr<nsIChannel> linkedChannel;
1714       rv = NS_LinkRedirectChannels(mRedirectChannelId, this,
1715                                    getter_AddRefs(linkedChannel));
1716       NS_ENSURE_SUCCESS(rv, rv);
1717       MOZ_ASSERT(linkedChannel == newChannel);
1718 
1719       // We immediately store the InterceptedHttpChannel as our nested
1720       // mChannel.  None of the redirect IPC messaging takes place.
1721       mChannel = do_QueryObject(newChannel);
1722 
1723       callback->OnRedirectVerifyCallback(NS_OK);
1724       return NS_OK;
1725     }
1726   }
1727 
1728   // Sending down the original URI, because that is the URI we have
1729   // to construct the channel from - this is the URI we've been actually
1730   // redirected to.  URI of the channel may be an inner channel URI.
1731   // URI of the channel will be reconstructed by the protocol handler
1732   // on the child process, no need to send it then.
1733   nsCOMPtr<nsIURI> newOriginalURI;
1734   newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
1735 
1736   URIParams uriParams;
1737   SerializeURI(newOriginalURI, uriParams);
1738 
1739   uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
1740   MOZ_ALWAYS_SUCCEEDS(newChannel->GetLoadFlags(&newLoadFlags));
1741 
1742   nsCString secInfoSerialization;
1743   UpdateAndSerializeSecurityInfo(secInfoSerialization);
1744 
1745   // If the channel is a HTTP channel, we also want to inform the child
1746   // about the parent's channelId attribute, so that both parent and child
1747   // share the same ID. Useful for monitoring channel activity in devtools.
1748   uint64_t channelId = 0;
1749   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
1750   if (httpChannel) {
1751     rv = httpChannel->GetChannelId(&channelId);
1752     NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
1753   }
1754 
1755   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1756 
1757   ParentLoadInfoForwarderArgs loadInfoForwarderArg;
1758   mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1759                                                   &loadInfoForwarderArg);
1760 
1761   nsHttpResponseHead* responseHead = mChannel->GetResponseHead();
1762 
1763   nsHttpResponseHead cleanedUpResponseHead;
1764   if (responseHead && responseHead->HasHeader(nsHttp::Set_Cookie)) {
1765     cleanedUpResponseHead = *responseHead;
1766     cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1767     responseHead = &cleanedUpResponseHead;
1768   }
1769 
1770   if (!responseHead) {
1771     responseHead = &cleanedUpResponseHead;
1772   }
1773 
1774   bool result = false;
1775   if (!mIPCClosed) {
1776     result = SendRedirect1Begin(
1777         mRedirectChannelId, uriParams, newLoadFlags, redirectFlags,
1778         loadInfoForwarderArg, *responseHead, secInfoSerialization, channelId,
1779         mChannel->GetPeerAddr(), GetTimingAttributes(mChannel));
1780   }
1781   if (!result) {
1782     // Bug 621446 investigation
1783     mSentRedirect1BeginFailed = true;
1784     return NS_BINDING_ABORTED;
1785   }
1786 
1787   // Result is handled in RecvRedirect2Verify above
1788 
1789   mRedirectChannel = newChannel;
1790   mRedirectCallback = callback;
1791   return NS_OK;
1792 }
1793 
1794 NS_IMETHODIMP
CompleteRedirect(bool succeeded)1795 HttpChannelParent::CompleteRedirect(bool succeeded) {
1796   LOG(("HttpChannelParent::CompleteRedirect [this=%p succeeded=%d]\n", this,
1797        succeeded));
1798 
1799   // If this was an internal redirect for a service worker interception then
1800   // we will not have a redirecting channel here.  Hide this redirect from
1801   // the child.
1802   if (!mRedirectChannel) {
1803     return NS_OK;
1804   }
1805 
1806   if (succeeded && !mIPCClosed) {
1807     // TODO: check return value: assume child dead if failed
1808     Unused << SendRedirect3Complete();
1809   }
1810 
1811   mRedirectChannel = nullptr;
1812   return NS_OK;
1813 }
1814 
OpenAlternativeOutputStream(const nsACString & type,int64_t predictedSize,nsIAsyncOutputStream ** _retval)1815 nsresult HttpChannelParent::OpenAlternativeOutputStream(
1816     const nsACString& type, int64_t predictedSize,
1817     nsIAsyncOutputStream** _retval) {
1818   // We need to make sure the child does not call SendDocumentChannelCleanup()
1819   // before opening the altOutputStream, because that clears mCacheEntry.
1820   if (!mCacheEntry) {
1821     return NS_ERROR_NOT_AVAILABLE;
1822   }
1823   nsresult rv =
1824       mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
1825   if (NS_SUCCEEDED(rv)) {
1826     mCacheEntry->SetMetaDataElement("alt-data-from-child", "1");
1827   }
1828   return rv;
1829 }
1830 
UpdateAndSerializeSecurityInfo(nsACString & aSerializedSecurityInfoOut)1831 void HttpChannelParent::UpdateAndSerializeSecurityInfo(
1832     nsACString& aSerializedSecurityInfoOut) {
1833   nsCOMPtr<nsISupports> secInfoSupp;
1834   mChannel->GetSecurityInfo(getter_AddRefs(secInfoSupp));
1835   if (secInfoSupp) {
1836     nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp);
1837     if (secInfoSer) {
1838       NS_SerializeToString(secInfoSer, aSerializedSecurityInfoOut);
1839     }
1840   }
1841 }
1842 
DoSendDeleteSelf()1843 bool HttpChannelParent::DoSendDeleteSelf() {
1844   mIPCClosed = true;
1845   bool rv = SendDeleteSelf();
1846 
1847   CleanupBackgroundChannel();
1848 
1849   return rv;
1850 }
1851 
RecvDeletingChannel()1852 mozilla::ipc::IPCResult HttpChannelParent::RecvDeletingChannel() {
1853   // We need to ensure that the parent channel will not be sending any more IPC
1854   // messages after this, as the child is going away. DoSendDeleteSelf will
1855   // set mIPCClosed = true;
1856   if (!DoSendDeleteSelf()) {
1857     return IPC_FAIL_NO_REASON(this);
1858   }
1859   return IPC_OK();
1860 }
1861 
1862 //-----------------------------------------------------------------------------
1863 // HttpChannelSecurityWarningReporter
1864 //-----------------------------------------------------------------------------
1865 
ReportSecurityMessage(const nsAString & aMessageTag,const nsAString & aMessageCategory)1866 nsresult HttpChannelParent::ReportSecurityMessage(
1867     const nsAString& aMessageTag, const nsAString& aMessageCategory) {
1868   if (mIPCClosed || NS_WARN_IF(!SendReportSecurityMessage(
1869                         nsString(aMessageTag), nsString(aMessageCategory)))) {
1870     return NS_ERROR_UNEXPECTED;
1871   }
1872   return NS_OK;
1873 }
1874 
1875 //-----------------------------------------------------------------------------
1876 // nsIAsyncVerifyRedirectReadyCallback
1877 //-----------------------------------------------------------------------------
1878 
1879 NS_IMETHODIMP
ReadyToVerify(nsresult aResult)1880 HttpChannelParent::ReadyToVerify(nsresult aResult) {
1881   LOG(("HttpChannelParent::ReadyToVerify [this=%p result=%" PRIx32 "]\n", this,
1882        static_cast<uint32_t>(aResult)));
1883   MOZ_ASSERT(NS_IsMainThread());
1884 
1885   ContinueRedirect2Verify(aResult);
1886 
1887   return NS_OK;
1888 }
1889 
DoSendSetPriority(int16_t aValue)1890 void HttpChannelParent::DoSendSetPriority(int16_t aValue) {
1891   if (!mIPCClosed) {
1892     Unused << SendSetPriority(aValue);
1893   }
1894 }
1895 
LogBlockedCORSRequest(const nsAString & aMessage,const nsACString & aCategory)1896 nsresult HttpChannelParent::LogBlockedCORSRequest(const nsAString& aMessage,
1897                                                   const nsACString& aCategory) {
1898   if (mIPCClosed || NS_WARN_IF(!SendLogBlockedCORSRequest(
1899                         nsString(aMessage), nsCString(aCategory)))) {
1900     return NS_ERROR_UNEXPECTED;
1901   }
1902   return NS_OK;
1903 }
1904 
LogMimeTypeMismatch(const nsACString & aMessageName,bool aWarning,const nsAString & aURL,const nsAString & aContentType)1905 nsresult HttpChannelParent::LogMimeTypeMismatch(const nsACString& aMessageName,
1906                                                 bool aWarning,
1907                                                 const nsAString& aURL,
1908                                                 const nsAString& aContentType) {
1909   if (mIPCClosed || NS_WARN_IF(!SendLogMimeTypeMismatch(
1910                         nsCString(aMessageName), aWarning, nsString(aURL),
1911                         nsString(aContentType)))) {
1912     return NS_ERROR_UNEXPECTED;
1913   }
1914   return NS_OK;
1915 }
1916 
1917 //-----------------------------------------------------------------------------
1918 // nsIChannelEventSink
1919 //-----------------------------------------------------------------------------
1920 
1921 NS_IMETHODIMP
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aRedirectFlags,nsIAsyncVerifyRedirectCallback * aCallback)1922 HttpChannelParent::AsyncOnChannelRedirect(
1923     nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirectFlags,
1924     nsIAsyncVerifyRedirectCallback* aCallback) {
1925   LOG(
1926       ("HttpChannelParent::AsyncOnChannelRedirect [this=%p, old=%p, "
1927        "new=%p, flags=%u]",
1928        this, aOldChannel, aNewChannel, aRedirectFlags));
1929 
1930   return StartRedirect(aNewChannel, aRedirectFlags, aCallback);
1931 }
1932 
1933 //-----------------------------------------------------------------------------
1934 // nsIRedirectResultListener
1935 //-----------------------------------------------------------------------------
1936 
1937 NS_IMETHODIMP
OnRedirectResult(bool succeeded)1938 HttpChannelParent::OnRedirectResult(bool succeeded) {
1939   LOG(("HttpChannelParent::OnRedirectResult [this=%p, suc=%d]", this,
1940        succeeded));
1941 
1942   nsresult rv;
1943 
1944   nsCOMPtr<nsIParentChannel> redirectChannel;
1945   if (mRedirectChannelId) {
1946     nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
1947         RedirectChannelRegistrar::GetOrCreate();
1948     MOZ_ASSERT(registrar);
1949 
1950     rv = registrar->GetParentChannel(mRedirectChannelId,
1951                                      getter_AddRefs(redirectChannel));
1952     if (NS_FAILED(rv) || !redirectChannel) {
1953       // Redirect might get canceled before we got AsyncOnChannelRedirect
1954       LOG(("Registered parent channel not found under id=%" PRIx64,
1955            mRedirectChannelId));
1956 
1957       nsCOMPtr<nsIChannel> newChannel;
1958       rv = registrar->GetRegisteredChannel(mRedirectChannelId,
1959                                            getter_AddRefs(newChannel));
1960       MOZ_ASSERT(newChannel, "Already registered channel not found");
1961 
1962       if (NS_SUCCEEDED(rv)) {
1963         newChannel->Cancel(NS_BINDING_ABORTED);
1964       }
1965     }
1966 
1967     // Release all previously registered channels, they are no longer need to be
1968     // kept in the registrar from this moment.
1969     registrar->DeregisterChannels(mRedirectChannelId);
1970 
1971     mRedirectChannelId = 0;
1972   }
1973 
1974   if (!redirectChannel) {
1975     succeeded = false;
1976   }
1977 
1978   CompleteRedirect(succeeded);
1979 
1980   if (succeeded) {
1981     if (!SameCOMIdentity(redirectChannel,
1982                          static_cast<nsIParentRedirectingChannel*>(this))) {
1983       Delete();
1984       mParentListener->SetListenerAfterRedirect(redirectChannel);
1985       redirectChannel->SetParentListener(mParentListener);
1986     }
1987   } else if (redirectChannel) {
1988     // Delete the redirect target channel: continue using old channel
1989     redirectChannel->Delete();
1990   }
1991 
1992   return NS_OK;
1993 }
1994 
OverrideReferrerInfoDuringBeginConnect(nsIReferrerInfo * aReferrerInfo)1995 void HttpChannelParent::OverrideReferrerInfoDuringBeginConnect(
1996     nsIReferrerInfo* aReferrerInfo) {
1997   MOZ_ASSERT(aReferrerInfo);
1998   MOZ_ASSERT(!mAfterOnStartRequestBegun);
1999 
2000   mOverrideReferrerInfo = aReferrerInfo;
2001 }
2002 
AttachStreamFilter(Endpoint<extensions::PStreamFilterParent> && aParentEndpoint,Endpoint<extensions::PStreamFilterChild> && aChildEndpoint)2003 auto HttpChannelParent::AttachStreamFilter(
2004     Endpoint<extensions::PStreamFilterParent>&& aParentEndpoint,
2005     Endpoint<extensions::PStreamFilterChild>&& aChildEndpoint)
2006     -> RefPtr<ChildEndpointPromise> {
2007   LOG(("HttpChannelParent::AttachStreamFilter [this=%p]", this));
2008   MOZ_ASSERT(!mAfterOnStartRequestBegun);
2009 
2010   if (mIPCClosed) {
2011     return ChildEndpointPromise::CreateAndReject(false, __func__);
2012   }
2013 
2014   // If IPC channel is open, background channel should be ready to send
2015   // SendAttachStreamFilter.
2016   MOZ_ASSERT(mBgParent);
2017   return InvokeAsync(mBgParent->GetBackgroundTarget(), mBgParent.get(),
2018                      __func__, &HttpBackgroundChannelParent::AttachStreamFilter,
2019                      std::move(aParentEndpoint), std::move(aChildEndpoint));
2020 }
2021 
SetCookie(nsCString && aCookie)2022 void HttpChannelParent::SetCookie(nsCString&& aCookie) {
2023   LOG(("HttpChannelParent::SetCookie [this=%p]", this));
2024   MOZ_ASSERT(!mAfterOnStartRequestBegun);
2025   MOZ_ASSERT(mCookie.IsEmpty());
2026   mCookie = std::move(aCookie);
2027 }
2028 
2029 }  // namespace net
2030 }  // namespace mozilla
2031