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