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/PerfStats.h"
15 #include "mozilla/Unused.h"
16 #include "mozilla/dom/ContentChild.h"
17 #include "mozilla/dom/DocGroup.h"
18 #include "mozilla/dom/ServiceWorkerUtils.h"
19 #include "mozilla/dom/BrowserChild.h"
20 #include "mozilla/extensions/StreamFilterParent.h"
21 #include "mozilla/ipc/FileDescriptorSetChild.h"
22 #include "mozilla/ipc/IPCStreamUtils.h"
23 #include "mozilla/net/NeckoChild.h"
24 #include "mozilla/net/HttpChannelChild.h"
25 #include "mozilla/net/UrlClassifierCommon.h"
26 #include "mozilla/net/UrlClassifierFeatureFactory.h"
27
28 #include "AltDataOutputStreamChild.h"
29 #include "CookieServiceChild.h"
30 #include "HttpBackgroundChannelChild.h"
31 #include "nsCOMPtr.h"
32 #include "nsContentPolicyUtils.h"
33 #include "nsDOMNavigationTiming.h"
34 #include "nsGlobalWindow.h"
35 #include "nsStringStream.h"
36 #include "nsHttpChannel.h"
37 #include "nsHttpHandler.h"
38 #include "nsNetUtil.h"
39 #include "nsSerializationHelper.h"
40 #include "mozilla/Attributes.h"
41 #include "mozilla/dom/PerformanceStorage.h"
42 #include "mozilla/ipc/InputStreamUtils.h"
43 #include "mozilla/ipc/URIUtils.h"
44 #include "mozilla/ipc/BackgroundUtils.h"
45 #include "mozilla/net/DNS.h"
46 #include "mozilla/net/SocketProcessBridgeChild.h"
47 #include "mozilla/ScopeExit.h"
48 #include "mozilla/StaticPrefs_network.h"
49 #include "mozilla/StoragePrincipalHelper.h"
50 #include "SerializedLoadContext.h"
51 #include "nsInputStreamPump.h"
52 #include "InterceptedChannel.h"
53 #include "nsContentSecurityManager.h"
54 #include "nsICompressConvStats.h"
55 #include "nsIDeprecationWarner.h"
56 #include "mozilla/dom/Document.h"
57 #include "nsIScriptError.h"
58 #include "nsISerialEventTarget.h"
59 #include "nsRedirectHistoryEntry.h"
60 #include "nsSocketTransportService2.h"
61 #include "nsStreamUtils.h"
62 #include "nsThreadUtils.h"
63 #include "nsCORSListenerProxy.h"
64 #include "ClassifierDummyChannel.h"
65 #include "nsIOService.h"
66
67 #include <functional>
68
69 using namespace mozilla::dom;
70 using namespace mozilla::ipc;
71
72 namespace mozilla {
73 namespace net {
74
75 //-----------------------------------------------------------------------------
76 // HttpChannelChild
77 //-----------------------------------------------------------------------------
78
HttpChannelChild()79 HttpChannelChild::HttpChannelChild()
80 : HttpAsyncAborter<HttpChannelChild>(this),
81 NeckoTargetHolder(nullptr),
82 mCacheEntryAvailable(false),
83 mAltDataCacheEntryAvailable(false),
84 mSendResumeAt(false),
85 mKeptAlive(false),
86 mIPCActorDeleted(false),
87 mSuspendSent(false),
88 mIsLastPartOfMultiPart(false),
89 mSuspendForWaitCompleteRedirectSetup(false),
90 mRecvOnStartRequestSentCalled(false),
91 mSuspendedByWaitingForPermissionCookie(false) {
92 LOG(("Creating HttpChannelChild @%p\n", this));
93
94 mChannelCreationTime = PR_Now();
95 mChannelCreationTimestamp = TimeStamp::Now();
96 mLastStatusReported =
97 mChannelCreationTimestamp; // in case we enable the profiler after Init()
98 mAsyncOpenTime = TimeStamp::Now();
99 mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
100
101 // Ensure that the cookie service is initialized before the first
102 // IPC HTTP channel is created.
103 // We require that the parent cookie service actor exists while
104 // processing HTTP responses.
105 RefPtr<CookieServiceChild> cookieService = CookieServiceChild::GetSingleton();
106 }
107
~HttpChannelChild()108 HttpChannelChild::~HttpChannelChild() {
109 LOG(("Destroying HttpChannelChild @%p\n", this));
110
111 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
112 if (mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy && mAsyncOpenSucceeded &&
113 !mSuccesfullyRedirected && !LoadOnStopRequestCalled()) {
114 bool emptyBgChildQueue, nullBgChild;
115 {
116 MutexAutoLock lock(mBgChildMutex);
117 nullBgChild = !mBgChild;
118 emptyBgChildQueue = !nullBgChild && mBgChild->IsQueueEmpty();
119 }
120
121 uint32_t flags =
122 (mRedirectChannelChild ? 1 << 0 : 0) |
123 (mEventQ->IsEmpty() ? 1 << 1 : 0) | (nullBgChild ? 1 << 2 : 0) |
124 (emptyBgChildQueue ? 1 << 3 : 0) |
125 (LoadOnStartRequestCalled() ? 1 << 4 : 0) |
126 (mBackgroundChildQueueFinalState == BCKCHILD_EMPTY ? 1 << 5 : 0) |
127 (mBackgroundChildQueueFinalState == BCKCHILD_NON_EMPTY ? 1 << 6 : 0) |
128 (mRemoteChannelExistedAtCancel ? 1 << 7 : 0) |
129 (mEverHadBgChildAtAsyncOpen ? 1 << 8 : 0) |
130 (mEverHadBgChildAtConnectParent ? 1 << 9 : 0) |
131 (mCreateBackgroundChannelFailed ? 1 << 10 : 0) |
132 (mBgInitFailCallbackTriggered ? 1 << 11 : 0) |
133 (mCanSendAtCancel ? 1 << 12 : 0) | (!!mSuspendCount ? 1 << 13 : 0) |
134 (!!mCallOnResume ? 1 << 14 : 0);
135 MOZ_CRASH_UNSAFE_PRINTF(
136 "~HttpChannelChild, LoadOnStopRequestCalled()=false, mStatus=0x%08x, "
137 "mActorDestroyReason=%d, 20200717 flags=%u",
138 static_cast<uint32_t>(nsresult(mStatus)),
139 static_cast<int32_t>(mActorDestroyReason ? *mActorDestroyReason : -1),
140 flags);
141 }
142 #endif
143
144 mEventQ->NotifyReleasingOwner();
145
146 ReleaseMainThreadOnlyReferences();
147 }
148
ReleaseMainThreadOnlyReferences()149 void HttpChannelChild::ReleaseMainThreadOnlyReferences() {
150 if (NS_IsMainThread()) {
151 // Already on main thread, let dtor to
152 // take care of releasing references
153 return;
154 }
155
156 NS_ReleaseOnMainThread("HttpChannelChild::mRedirectChannelChild",
157 mRedirectChannelChild.forget());
158 }
159 //-----------------------------------------------------------------------------
160 // HttpChannelChild::nsISupports
161 //-----------------------------------------------------------------------------
162
163 NS_IMPL_ADDREF(HttpChannelChild)
164
NS_IMETHODIMP_(MozExternalRefCountType)165 NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
166 if (!NS_IsMainThread()) {
167 nsrefcnt count = mRefCnt;
168 nsresult rv = NS_DispatchToMainThread(NewNonOwningRunnableMethod(
169 "HttpChannelChild::Release", this, &HttpChannelChild::Release));
170
171 // Continue Release procedure if failed to dispatch to main thread.
172 if (!NS_WARN_IF(NS_FAILED(rv))) {
173 return count - 1;
174 }
175 }
176
177 nsrefcnt count = --mRefCnt;
178 MOZ_ASSERT(int32_t(count) >= 0, "dup release");
179
180 // Normally we Send_delete in OnStopRequest, but when we need to retain the
181 // remote channel for security info IPDL itself holds 1 reference, so we
182 // Send_delete when refCnt==1. But if !CanSend(), then there's nobody to send
183 // to, so we fall through.
184 if (mKeptAlive && count == 1 && CanSend()) {
185 NS_LOG_RELEASE(this, 1, "HttpChannelChild");
186 mKeptAlive = false;
187 // We send a message to the parent, which calls SendDelete, and then the
188 // child calling Send__delete__() to finally drop the refcount to 0.
189 TrySendDeletingChannel();
190 return 1;
191 }
192
193 if (count == 0) {
194 mRefCnt = 1; /* stabilize */
195
196 // We don't have a listener when AsyncOpen has failed or when this channel
197 // has been sucessfully redirected.
198 if (MOZ_LIKELY(LoadOnStartRequestCalled() && LoadOnStopRequestCalled()) ||
199 !mListener) {
200 NS_LOG_RELEASE(this, 0, "HttpChannelChild");
201 delete this;
202 return 0;
203 }
204
205 // This makes sure we fulfill the stream listener contract all the time.
206 if (NS_SUCCEEDED(mStatus)) {
207 mStatus = NS_ERROR_ABORT;
208 }
209
210 // Turn the stabilization refcount into a regular strong reference.
211
212 // 1) We tell refcount logging about the "stabilization" AddRef, which
213 // will become the reference for |channel|. We do this first so that we
214 // don't tell refcount logging that the refcount has dropped to zero, which
215 // it will interpret as destroying the object.
216 NS_LOG_ADDREF(this, 2, "HttpChannelChild", sizeof(*this));
217
218 // 2) We tell refcount logging about the original call to Release().
219 NS_LOG_RELEASE(this, 1, "HttpChannelChild");
220
221 // 3) Finally, we turn the reference into a regular smart pointer.
222 RefPtr<HttpChannelChild> channel = dont_AddRef(this);
223
224 // This runnable will create a strong reference to |this|.
225 NS_DispatchToMainThread(
226 NewRunnableMethod("~HttpChannelChild>DoNotifyListener", channel,
227 &HttpChannelChild::DoNotifyListener));
228
229 // If NS_DispatchToMainThread failed then we're going to leak the runnable,
230 // and thus the channel, so there's no need to do anything else.
231
232 // We should have already done any special handling for the refcount = 1
233 // case when the refcount first went from 2 to 1. We don't want it to happen
234 // when |channel| is destroyed.
235 MOZ_ASSERT(!mKeptAlive || !CanSend());
236
237 // XXX If std::move(channel) is allowed, then we don't have to have extra
238 // checks for the refcount going from 2 to 1. See bug 1680217.
239
240 // This will release the stabilization refcount, which is necessary to avoid
241 // a leak.
242 channel = nullptr;
243
244 return mRefCnt;
245 }
246
247 NS_LOG_RELEASE(this, count, "HttpChannelChild");
248 return count;
249 }
250
251 NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
NS_INTERFACE_MAP_ENTRY(nsIRequest)252 NS_INTERFACE_MAP_ENTRY(nsIRequest)
253 NS_INTERFACE_MAP_ENTRY(nsIChannel)
254 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
255 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
256 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICacheInfoChannel,
257 !mMultiPartID.isSome())
258 NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
259 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
260 NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
261 NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
262 NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
263 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
264 NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
265 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
266 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiPartChannel, mMultiPartID.isSome())
267 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIThreadRetargetableRequest,
268 !mMultiPartID.isSome())
269 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelChild)
270 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
271
272 //-----------------------------------------------------------------------------
273 // HttpChannelChild::PHttpChannelChild
274 //-----------------------------------------------------------------------------
275
276 void HttpChannelChild::OnBackgroundChildReady(
277 HttpBackgroundChannelChild* aBgChild) {
278 LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n", this,
279 aBgChild));
280 MOZ_ASSERT(OnSocketThread());
281
282 {
283 MutexAutoLock lock(mBgChildMutex);
284
285 // mBgChild might be removed or replaced while the original background
286 // channel is inited on STS thread.
287 if (mBgChild != aBgChild) {
288 return;
289 }
290
291 MOZ_ASSERT(mBgInitFailCallback);
292 mBgInitFailCallback = nullptr;
293 }
294 }
295
OnBackgroundChildDestroyed(HttpBackgroundChannelChild * aBgChild)296 void HttpChannelChild::OnBackgroundChildDestroyed(
297 HttpBackgroundChannelChild* aBgChild) {
298 LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
299 // This function might be called during shutdown phase, so OnSocketThread()
300 // might return false even on STS thread. Use IsOnCurrentThreadInfallible()
301 // to get correct information.
302 MOZ_ASSERT(gSocketTransportService);
303 MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
304
305 nsCOMPtr<nsIRunnable> callback;
306 {
307 MutexAutoLock lock(mBgChildMutex);
308
309 // mBgChild might be removed or replaced while the original background
310 // channel is destroyed on STS thread.
311 if (aBgChild != mBgChild) {
312 return;
313 }
314
315 mBgChild = nullptr;
316 callback = std::move(mBgInitFailCallback);
317 }
318
319 if (callback) {
320 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
321 mBgInitFailCallbackTriggered = true;
322 #endif
323 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
324 neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
325 }
326 }
327
RecvOnStartRequestSent()328 mozilla::ipc::IPCResult HttpChannelChild::RecvOnStartRequestSent() {
329 LOG(("HttpChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
330 MOZ_ASSERT(NS_IsMainThread());
331 MOZ_ASSERT(!mRecvOnStartRequestSentCalled);
332
333 mRecvOnStartRequestSentCalled = true;
334
335 if (mSuspendedByWaitingForPermissionCookie) {
336 mSuspendedByWaitingForPermissionCookie = false;
337 mEventQ->Resume();
338 }
339 return IPC_OK();
340 }
341
ProcessOnStartRequest(const nsHttpResponseHead & aResponseHead,const bool & aUseResponseHead,const nsHttpHeaderArray & aRequestHeaders,const HttpChannelOnStartRequestArgs & aArgs)342 void HttpChannelChild::ProcessOnStartRequest(
343 const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
344 const nsHttpHeaderArray& aRequestHeaders,
345 const HttpChannelOnStartRequestArgs& aArgs) {
346 LOG(("HttpChannelChild::ProcessOnStartRequest [this=%p]\n", this));
347 MOZ_ASSERT(OnSocketThread());
348
349 #ifdef NIGHTLY_BUILD
350 TimeStamp start = TimeStamp::Now();
351 #endif
352
353 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
354 this, [self = UnsafePtr<HttpChannelChild>(this), aResponseHead,
355 #ifdef NIGHTLY_BUILD
356 aUseResponseHead, aRequestHeaders, aArgs, start]() {
357 if (self->mLoadFlags & nsIRequest::LOAD_RECORD_START_REQUEST_DELAY) {
358 TimeDuration delay = TimeStamp::Now() - start;
359 Telemetry::Accumulate(
360 Telemetry::HTTP_PRELOAD_IMAGE_STARTREQUEST_DELAY,
361 static_cast<uint32_t>(delay.ToMilliseconds()));
362 }
363 #else
364 aUseResponseHead, aRequestHeaders, aArgs]() {
365 #endif
366
367 self->OnStartRequest(aResponseHead, aUseResponseHead, aRequestHeaders,
368 aArgs);
369 }));
370 }
371
372 static void ResourceTimingStructArgsToTimingsStruct(
373 const ResourceTimingStructArgs& aArgs, TimingStruct& aTimings) {
374 aTimings.domainLookupStart = aArgs.domainLookupStart();
375 aTimings.domainLookupEnd = aArgs.domainLookupEnd();
376 aTimings.connectStart = aArgs.connectStart();
377 aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
378 aTimings.secureConnectionStart = aArgs.secureConnectionStart();
379 aTimings.connectEnd = aArgs.connectEnd();
380 aTimings.requestStart = aArgs.requestStart();
381 aTimings.responseStart = aArgs.responseStart();
382 aTimings.responseEnd = aArgs.responseEnd();
383 }
384
385 void HttpChannelChild::OnStartRequest(
386 const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
387 const nsHttpHeaderArray& aRequestHeaders,
388 const HttpChannelOnStartRequestArgs& aArgs) {
389 LOG(("HttpChannelChild::OnStartRequest [this=%p]\n", this));
390
391 // If this channel was aborted by ActorDestroy, then there may be other
392 // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
393 // be handled. In that case we just ignore them to avoid calling the listener
394 // twice.
395 if (LoadOnStartRequestCalled() && mIPCActorDeleted) {
396 return;
397 }
398
399 // Copy arguments only. It's possible to handle other IPC between
400 // OnStartRequest and DoOnStartRequest.
401 mComputedCrossOriginOpenerPolicy = aArgs.openerPolicy();
402
403 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
404 mStatus = aArgs.channelStatus();
405 }
406
407 // Cookies headers should not be visible to the child process
408 MOZ_ASSERT(!aRequestHeaders.HasHeader(nsHttp::Cookie));
409 MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
410
411 if (aUseResponseHead && !mCanceled) {
412 mResponseHead = MakeUnique<nsHttpResponseHead>(aResponseHead);
413 }
414
415 if (!aArgs.securityInfoSerialization().IsEmpty()) {
416 [[maybe_unused]] nsresult rv = NS_DeserializeObject(
417 aArgs.securityInfoSerialization(), getter_AddRefs(mSecurityInfo));
418 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
419 "Deserializing security info should not fail");
420 }
421
422 ipc::MergeParentLoadInfoForwarder(aArgs.loadInfoForwarder(), mLoadInfo);
423
424 mIsFromCache = aArgs.isFromCache();
425 mIsRacing = aArgs.isRacing();
426 mCacheEntryAvailable = aArgs.cacheEntryAvailable();
427 mCacheEntryId = aArgs.cacheEntryId();
428 mCacheFetchCount = aArgs.cacheFetchCount();
429 mCacheExpirationTime = aArgs.cacheExpirationTime();
430 mSelfAddr = aArgs.selfAddr();
431 mPeerAddr = aArgs.peerAddr();
432
433 mRedirectCount = aArgs.redirectCount();
434 mAvailableCachedAltDataType = aArgs.altDataType();
435 StoreDeliveringAltData(aArgs.deliveringAltData());
436 mAltDataLength = aArgs.altDataLength();
437 StoreResolvedByTRR(aArgs.isResolvedByTRR());
438
439 SetApplyConversion(aArgs.applyConversion());
440
441 StoreAfterOnStartRequestBegun(true);
442 StoreHasHTTPSRR(aArgs.hasHTTPSRR());
443
444 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
445
446 mCacheKey = aArgs.cacheKey();
447
448 // replace our request headers with what actually got sent in the parent
449 mRequestHead.SetHeaders(aRequestHeaders);
450
451 // Note: this is where we would notify "http-on-examine-response" observers.
452 // We have deliberately disabled this for child processes (see bug 806753)
453 //
454 // gHttpHandler->OnExamineResponse(this);
455
456 ResourceTimingStructArgsToTimingsStruct(aArgs.timing(), mTransactionTimings);
457
458 StoreAllRedirectsSameOrigin(aArgs.allRedirectsSameOrigin());
459
460 mMultiPartID = aArgs.multiPartID();
461 mIsLastPartOfMultiPart = aArgs.isLastPartOfMultiPart();
462
463 if (aArgs.overrideReferrerInfo()) {
464 // The arguments passed to SetReferrerInfoInternal here should mirror the
465 // arguments passed in
466 // nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown(), except for
467 // aRespectBeforeConnect which we pass false here since we're intentionally
468 // overriding the referrer after BeginConnect().
469 Unused << SetReferrerInfoInternal(aArgs.overrideReferrerInfo(), false, true,
470 false);
471 }
472
473 if (!aArgs.cookie().IsEmpty()) {
474 SetCookie(aArgs.cookie());
475 }
476
477 if (aArgs.shouldWaitForOnStartRequestSent() &&
478 !mRecvOnStartRequestSentCalled) {
479 LOG((" > pending DoOnStartRequest until RecvOnStartRequestSent\n"));
480 MOZ_ASSERT(NS_IsMainThread());
481
482 mEventQ->Suspend();
483 mSuspendedByWaitingForPermissionCookie = true;
484 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
485 this, [self = UnsafePtr<HttpChannelChild>(this)]() {
486 self->DoOnStartRequest(self, nullptr);
487 }));
488 return;
489 }
490
491 // Remember whether HTTP3 is supported
492 if (mResponseHead) {
493 mSupportsHTTP3 =
494 nsHttpHandler::IsHttp3SupportedByServer(mResponseHead.get());
495 }
496
497 DoOnStartRequest(this, nullptr);
498 }
499
500 void HttpChannelChild::ProcessOnAfterLastPart(const nsresult& aStatus) {
501 LOG(("HttpChannelChild::ProcessOnAfterLastPart [this=%p]\n", this));
502 MOZ_ASSERT(OnSocketThread());
503 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
504 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
505 self->OnAfterLastPart(aStatus);
506 }));
507 }
508
509 void HttpChannelChild::OnAfterLastPart(const nsresult& aStatus) {
510 if (LoadOnStopRequestCalled()) {
511 return;
512 }
513 StoreOnStopRequestCalled(true);
514
515 // notify "http-on-stop-connect" observers
516 gHttpHandler->OnStopRequest(this);
517
518 ReleaseListeners();
519
520 // If a preferred alt-data type was set, the parent would hold a reference to
521 // the cache entry in case the child calls openAlternativeOutputStream().
522 // (see nsHttpChannel::OnStopRequest)
523 if (!mPreferredCachedAltDataTypes.IsEmpty()) {
524 mAltDataCacheEntryAvailable = mCacheEntryAvailable;
525 }
526 mCacheEntryAvailable = false;
527
528 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
529 CleanupBackgroundChannel();
530
531 if (mLoadFlags & LOAD_DOCUMENT_URI) {
532 // Keep IPDL channel open, but only for updating security info.
533 // If IPDL is already closed, then do nothing.
534 if (CanSend()) {
535 mKeptAlive = true;
536 SendDocumentChannelCleanup(true);
537 }
538 } else {
539 // The parent process will respond by sending a DeleteSelf message and
540 // making sure not to send any more messages after that.
541 TrySendDeletingChannel();
542 }
543 }
544
545 void HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest,
546 nsISupports* aContext) {
547 nsresult rv;
548
549 LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
550
551 // We handle all the listener chaining before OnStartRequest at this moment.
552 // Prevent additional listeners being added to the chain after the request
553 // as started.
554 StoreTracingEnabled(false);
555
556 // mListener could be null if the redirect setup is not completed.
557 MOZ_ASSERT(mListener || LoadOnStartRequestCalled());
558 if (!mListener) {
559 Cancel(NS_ERROR_FAILURE);
560 return;
561 }
562
563 if (mListener) {
564 nsCOMPtr<nsIStreamListener> listener(mListener);
565 StoreOnStartRequestCalled(true);
566 rv = listener->OnStartRequest(aRequest);
567 } else {
568 rv = NS_ERROR_UNEXPECTED;
569 }
570 StoreOnStartRequestCalled(true);
571
572 if (NS_FAILED(rv)) {
573 Cancel(rv);
574 return;
575 }
576
577 nsCOMPtr<nsIStreamListener> listener;
578 rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
579 if (NS_FAILED(rv)) {
580 Cancel(rv);
581 } else if (listener) {
582 mListener = listener;
583 mCompressListener = listener;
584 }
585 }
586
587 void HttpChannelChild::ProcessOnTransportAndData(
588 const nsresult& aChannelStatus, const nsresult& aTransportStatus,
589 const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) {
590 LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
591 MOZ_ASSERT(OnSocketThread());
592 mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
593 [self = UnsafePtr<HttpChannelChild>(this)]() {
594 return self->GetODATarget();
595 },
596 [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
597 aTransportStatus, aOffset, aCount, aData]() {
598 self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
599 aCount, aData);
600 }));
601 }
602
603 void HttpChannelChild::OnTransportAndData(const nsresult& aChannelStatus,
604 const nsresult& aTransportStatus,
605 const uint64_t& aOffset,
606 const uint32_t& aCount,
607 const nsCString& aData) {
608 LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
609
610 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
611 mStatus = aChannelStatus;
612 }
613
614 if (mCanceled || NS_FAILED(mStatus)) {
615 return;
616 }
617
618 // Hold queue lock throughout all three calls, else we might process a later
619 // necko msg in between them.
620 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
621
622 int64_t progressMax;
623 if (NS_FAILED(GetContentLength(&progressMax))) {
624 progressMax = -1;
625 }
626
627 const int64_t progress = aOffset + aCount;
628
629 // OnTransportAndData will be run on retargeted thread if applicable, however
630 // OnStatus/OnProgress event can only be fired on main thread. We need to
631 // dispatch the status/progress event handling back to main thread with the
632 // appropriate event target for networking.
633 if (NS_IsMainThread()) {
634 DoOnStatus(this, aTransportStatus);
635 DoOnProgress(this, progress, progressMax);
636 } else {
637 RefPtr<HttpChannelChild> self = this;
638 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
639 MOZ_ASSERT(neckoTarget);
640
641 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
642 NS_NewRunnableFunction(
643 "net::HttpChannelChild::OnTransportAndData",
644 [self, aTransportStatus, progress, progressMax]() {
645 self->DoOnStatus(self, aTransportStatus);
646 self->DoOnProgress(self, progress, progressMax);
647 }),
648 NS_DISPATCH_NORMAL);
649 MOZ_ASSERT(NS_SUCCEEDED(rv));
650 }
651
652 // OnDataAvailable
653 //
654 // NOTE: the OnDataAvailable contract requires the client to read all the data
655 // in the inputstream. This code relies on that ('data' will go away after
656 // this function). Apparently the previous, non-e10s behavior was to actually
657 // support only reading part of the data, allowing later calls to read the
658 // rest.
659 nsCOMPtr<nsIInputStream> stringStream;
660 nsresult rv =
661 NS_NewByteInputStream(getter_AddRefs(stringStream),
662 Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
663 if (NS_FAILED(rv)) {
664 Cancel(rv);
665 return;
666 }
667
668 DoOnDataAvailable(this, nullptr, stringStream, aOffset, aCount);
669 stringStream->Close();
670
671 // TODO: Bug 1523916 backpressure needs to take into account if the data is
672 // coming from the main process or from the socket process via PBackground.
673 if (NeedToReportBytesRead()) {
674 mUnreportBytesRead += aCount;
675 if (mUnreportBytesRead >= gHttpHandler->SendWindowSize() >> 2) {
676 if (NS_IsMainThread()) {
677 Unused << SendBytesRead(mUnreportBytesRead);
678 } else {
679 // PHttpChannel connects to the main thread
680 RefPtr<HttpChannelChild> self = this;
681 int32_t bytesRead = mUnreportBytesRead;
682 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
683 MOZ_ASSERT(neckoTarget);
684
685 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
686 NS_NewRunnableFunction("net::HttpChannelChild::SendBytesRead",
687 [self, bytesRead]() {
688 Unused << self->SendBytesRead(bytesRead);
689 }),
690 NS_DISPATCH_NORMAL);
691 MOZ_ASSERT(NS_SUCCEEDED(rv));
692 }
693 mUnreportBytesRead = 0;
694 }
695 }
696 }
697
698 bool HttpChannelChild::NeedToReportBytesRead() {
699 if (mCacheNeedToReportBytesReadInitialized) {
700 return mNeedToReportBytesRead;
701 }
702
703 // Might notify parent for partial cache, and the IPC message is ignored by
704 // parent.
705 int64_t contentLength = -1;
706 if (gHttpHandler->SendWindowSize() == 0 || mIsFromCache ||
707 NS_FAILED(GetContentLength(&contentLength)) ||
708 contentLength < gHttpHandler->SendWindowSize()) {
709 mNeedToReportBytesRead = false;
710 }
711
712 mCacheNeedToReportBytesReadInitialized = true;
713 return mNeedToReportBytesRead;
714 }
715
716 void HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status) {
717 LOG(("HttpChannelChild::DoOnStatus [this=%p]\n", this));
718 MOZ_ASSERT(NS_IsMainThread());
719
720 if (mCanceled) return;
721
722 // cache the progress sink so we don't have to query for it each time.
723 if (!mProgressSink) GetCallback(mProgressSink);
724
725 // block status/progress after Cancel or OnStopRequest has been called,
726 // or if channel has LOAD_BACKGROUND set.
727 if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending() &&
728 !(mLoadFlags & LOAD_BACKGROUND)) {
729 nsAutoCString host;
730 mURI->GetHost(host);
731 mProgressSink->OnStatus(aRequest, status,
732 NS_ConvertUTF8toUTF16(host).get());
733 }
734 }
735
736 void HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress,
737 int64_t progressMax) {
738 LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
739 MOZ_ASSERT(NS_IsMainThread());
740
741 if (mCanceled) return;
742
743 // cache the progress sink so we don't have to query for it each time.
744 if (!mProgressSink) GetCallback(mProgressSink);
745
746 // block status/progress after Cancel or OnStopRequest has been called,
747 // or if channel has LOAD_BACKGROUND set.
748 if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending()) {
749 // OnProgress
750 //
751 if (progress > 0) {
752 mProgressSink->OnProgress(aRequest, progress, progressMax);
753 }
754 }
755 }
756
757 void HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest,
758 nsISupports* aContext,
759 nsIInputStream* aStream,
760 uint64_t aOffset, uint32_t aCount) {
761 AUTO_PROFILER_LABEL("HttpChannelChild::DoOnDataAvailable", NETWORK);
762 LOG(("HttpChannelChild::DoOnDataAvailable [this=%p]\n", this));
763 if (mCanceled) return;
764
765 if (mListener) {
766 nsCOMPtr<nsIStreamListener> listener(mListener);
767 nsresult rv = listener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
768 if (NS_FAILED(rv)) {
769 CancelOnMainThread(rv);
770 }
771 }
772 }
773
774 void HttpChannelChild::ProcessOnStopRequest(
775 const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
776 const nsHttpHeaderArray& aResponseTrailers,
777 nsTArray<ConsoleReportCollected>&& aConsoleReports,
778 bool aFromSocketProcess) {
779 LOG(
780 ("HttpChannelChild::ProcessOnStopRequest [this=%p, "
781 "aFromSocketProcess=%d]\n",
782 this, aFromSocketProcess));
783 MOZ_ASSERT(OnSocketThread());
784
785 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
786 this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
787 aResponseTrailers,
788 consoleReports = CopyableTArray{aConsoleReports.Clone()},
789 aFromSocketProcess]() mutable {
790 self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers);
791 if (!aFromSocketProcess) {
792 self->DoOnConsoleReport(std::move(consoleReports));
793 self->ContinueOnStopRequest();
794 }
795 }));
796 }
797
798 void HttpChannelChild::ProcessOnConsoleReport(
799 nsTArray<ConsoleReportCollected>&& aConsoleReports) {
800 LOG(("HttpChannelChild::ProcessOnConsoleReport [this=%p]\n", this));
801 MOZ_ASSERT(OnSocketThread());
802
803 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
804 this,
805 [self = UnsafePtr<HttpChannelChild>(this),
806 consoleReports = CopyableTArray{aConsoleReports.Clone()}]() mutable {
807 self->DoOnConsoleReport(std::move(consoleReports));
808 self->ContinueOnStopRequest();
809 }));
810 }
811
812 void HttpChannelChild::DoOnConsoleReport(
813 nsTArray<ConsoleReportCollected>&& aConsoleReports) {
814 if (aConsoleReports.IsEmpty()) {
815 return;
816 }
817
818 for (ConsoleReportCollected& report : aConsoleReports) {
819 if (report.propertiesFile() <
820 nsContentUtils::PropertiesFile::PropertiesFile_COUNT) {
821 AddConsoleReport(report.errorFlags(), report.category(),
822 nsContentUtils::PropertiesFile(report.propertiesFile()),
823 report.sourceFileURI(), report.lineNumber(),
824 report.columnNumber(), report.messageName(),
825 report.stringParams());
826 }
827 }
828 MaybeFlushConsoleReports();
829 }
830
831 void HttpChannelChild::OnStopRequest(
832 const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
833 const nsHttpHeaderArray& aResponseTrailers) {
834 LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
835 static_cast<uint32_t>(aChannelStatus)));
836 MOZ_ASSERT(NS_IsMainThread());
837
838 // If this channel was aborted by ActorDestroy, then there may be other
839 // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
840 // be handled. In that case we just ignore them to avoid calling the listener
841 // twice.
842 if (LoadOnStopRequestCalled() && mIPCActorDeleted) {
843 return;
844 }
845
846 nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
847 if (conv) {
848 conv->GetDecodedDataLength(&mDecodedBodySize);
849 }
850
851 ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
852
853 // Do not overwrite or adjust the original mAsyncOpenTime by timing.fetchStart
854 // We must use the original child process time in order to account for child
855 // side work and IPC transit overhead.
856 // XXX: This depends on TimeStamp being equivalent across processes.
857 // This is true for modern hardware but for older platforms it is not always
858 // true.
859
860 mRedirectStartTimeStamp = aTiming.redirectStart();
861 mRedirectEndTimeStamp = aTiming.redirectEnd();
862 mTransferSize = aTiming.transferSize();
863 mEncodedBodySize = aTiming.encodedBodySize();
864 mProtocolVersion = aTiming.protocolVersion();
865
866 mCacheReadStart = aTiming.cacheReadStart();
867 mCacheReadEnd = aTiming.cacheReadEnd();
868
869 #ifdef MOZ_GECKO_PROFILER
870 if (profiler_can_accept_markers()) {
871 nsAutoCString requestMethod;
872 GetRequestMethod(requestMethod);
873 nsAutoCString contentType;
874 if (mResponseHead) {
875 mResponseHead->ContentType(contentType);
876 }
877 int32_t priority = PRIORITY_NORMAL;
878 GetPriority(&priority);
879 profiler_add_network_marker(
880 mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP,
881 mLastStatusReported, TimeStamp::Now(), mTransferSize, kCacheUnknown,
882 mLoadInfo->GetInnerWindowID(), &mTransactionTimings, std::move(mSource),
883 Some(nsDependentCString(contentType.get())));
884 }
885 #endif
886
887 TimeDuration channelCompletionDuration = TimeStamp::Now() - mAsyncOpenTime;
888 if (mIsFromCache) {
889 PerfStats::RecordMeasurement(PerfStats::Metric::HttpChannelCompletion_Cache,
890 channelCompletionDuration);
891 } else {
892 PerfStats::RecordMeasurement(
893 PerfStats::Metric::HttpChannelCompletion_Network,
894 channelCompletionDuration);
895 }
896 PerfStats::RecordMeasurement(PerfStats::Metric::HttpChannelCompletion,
897 channelCompletionDuration);
898
899 mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers);
900
901 DoPreOnStopRequest(aChannelStatus);
902
903 { // We must flush the queue before we Send__delete__
904 // (although we really shouldn't receive any msgs after OnStop),
905 // so make sure this goes out of scope before then.
906 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
907
908 DoOnStopRequest(this, aChannelStatus, nullptr);
909 // DoOnStopRequest() calls ReleaseListeners()
910 }
911 }
912
913 void HttpChannelChild::ContinueOnStopRequest() {
914 // If we're a multi-part stream, then don't cleanup yet, and we'll do so
915 // in OnAfterLastPart.
916 if (mMultiPartID) {
917 LOG(
918 ("HttpChannelChild::OnStopRequest - Expecting future parts on a "
919 "multipart channel postpone cleaning up."));
920 return;
921 }
922
923 CleanupBackgroundChannel();
924
925 // If there is a possibility we might want to write alt data to the cache
926 // entry, we keep the channel alive. We still send the DocumentChannelCleanup
927 // message but request the cache entry to be kept by the parent.
928 // If the channel has failed, the cache entry is in a non-writtable state and
929 // we want to release it to not block following consumers.
930 if (NS_SUCCEEDED(mStatus) && !mPreferredCachedAltDataTypes.IsEmpty()) {
931 mKeptAlive = true;
932 SendDocumentChannelCleanup(false); // don't clear cache entry
933 return;
934 }
935
936 if (mLoadFlags & LOAD_DOCUMENT_URI) {
937 // Keep IPDL channel open, but only for updating security info.
938 // If IPDL is already closed, then do nothing.
939 if (CanSend()) {
940 mKeptAlive = true;
941 SendDocumentChannelCleanup(true);
942 }
943 } else {
944 // The parent process will respond by sending a DeleteSelf message and
945 // making sure not to send any more messages after that.
946 TrySendDeletingChannel();
947 }
948 }
949
950 void HttpChannelChild::DoPreOnStopRequest(nsresult aStatus) {
951 AUTO_PROFILER_LABEL("HttpChannelChild::DoPreOnStopRequest", NETWORK);
952 LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
953 this, static_cast<uint32_t>(aStatus)));
954 StoreIsPending(false);
955
956 MaybeReportTimingData();
957
958 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
959 mStatus = aStatus;
960 }
961
962 CollectOMTTelemetry();
963 }
964
965 void HttpChannelChild::CollectOMTTelemetry() {
966 MOZ_ASSERT(NS_IsMainThread());
967
968 // Only collect telemetry for HTTP channel that is loaded successfully and
969 // completely.
970 if (mCanceled || NS_FAILED(mStatus)) {
971 return;
972 }
973
974 // Use content policy type to accumulate data by usage.
975 nsAutoCString key(
976 NS_CP_ContentTypeName(mLoadInfo->InternalContentPolicyType()));
977
978 Telemetry::AccumulateCategoricalKeyed(key, mOMTResult);
979 }
980
981 void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
982 nsresult aChannelStatus,
983 nsISupports* aContext) {
984 AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
985 LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
986 MOZ_ASSERT(NS_IsMainThread());
987 MOZ_ASSERT(!LoadIsPending());
988
989 auto checkForBlockedContent = [&]() {
990 // NB: We use aChannelStatus here instead of mStatus because if there was an
991 // nsCORSListenerProxy on this request, it will override the tracking
992 // protection's return value.
993 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
994 aChannelStatus) ||
995 aChannelStatus == NS_ERROR_MALWARE_URI ||
996 aChannelStatus == NS_ERROR_UNWANTED_URI ||
997 aChannelStatus == NS_ERROR_BLOCKED_URI ||
998 aChannelStatus == NS_ERROR_HARMFUL_URI ||
999 aChannelStatus == NS_ERROR_PHISHING_URI) {
1000 nsCString list, provider, fullhash;
1001
1002 nsresult rv = GetMatchedList(list);
1003 NS_ENSURE_SUCCESS_VOID(rv);
1004
1005 rv = GetMatchedProvider(provider);
1006 NS_ENSURE_SUCCESS_VOID(rv);
1007
1008 rv = GetMatchedFullHash(fullhash);
1009 NS_ENSURE_SUCCESS_VOID(rv);
1010
1011 UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
1012 provider, fullhash);
1013 }
1014 };
1015 checkForBlockedContent();
1016
1017 // See bug 1587686. If the redirect setup is not completed, the post-redirect
1018 // channel will be not opened and mListener will be null.
1019 MOZ_ASSERT(mListener || !LoadWasOpened());
1020 if (!mListener) {
1021 return;
1022 }
1023
1024 MOZ_ASSERT(!LoadOnStopRequestCalled(),
1025 "We should not call OnStopRequest twice");
1026
1027 if (mListener) {
1028 nsCOMPtr<nsIStreamListener> listener(mListener);
1029 StoreOnStopRequestCalled(true);
1030 listener->OnStopRequest(aRequest, mStatus);
1031 }
1032 StoreOnStopRequestCalled(true);
1033
1034 // If we're a multi-part stream, then don't cleanup yet, and we'll do so
1035 // in OnAfterLastPart.
1036 if (mMultiPartID) {
1037 LOG(
1038 ("HttpChannelChild::DoOnStopRequest - Expecting future parts on a "
1039 "multipart channel not releasing listeners."));
1040 StoreOnStopRequestCalled(false);
1041 StoreOnStartRequestCalled(false);
1042 return;
1043 }
1044
1045 // notify "http-on-stop-connect" observers
1046 gHttpHandler->OnStopRequest(this);
1047
1048 ReleaseListeners();
1049
1050 // If a preferred alt-data type was set, the parent would hold a reference to
1051 // the cache entry in case the child calls openAlternativeOutputStream().
1052 // (see nsHttpChannel::OnStopRequest)
1053 if (!mPreferredCachedAltDataTypes.IsEmpty()) {
1054 mAltDataCacheEntryAvailable = mCacheEntryAvailable;
1055 }
1056 mCacheEntryAvailable = false;
1057
1058 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
1059 }
1060
1061 void HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
1062 const int64_t& aProgressMax) {
1063 MOZ_ASSERT(OnSocketThread());
1064 LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
1065 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1066 this,
1067 [self = UnsafePtr<HttpChannelChild>(this), aProgress, aProgressMax]() {
1068 AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1069 self->DoOnProgress(self, aProgress, aProgressMax);
1070 }));
1071 }
1072
1073 void HttpChannelChild::ProcessOnStatus(const nsresult& aStatus) {
1074 MOZ_ASSERT(OnSocketThread());
1075 LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
1076 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1077 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1078 AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1079 self->DoOnStatus(self, aStatus);
1080 }));
1081 }
1082
1083 mozilla::ipc::IPCResult HttpChannelChild::RecvFailedAsyncOpen(
1084 const nsresult& aStatus) {
1085 LOG(("HttpChannelChild::RecvFailedAsyncOpen [this=%p]\n", this));
1086 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1087 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1088 self->FailedAsyncOpen(aStatus);
1089 }));
1090 return IPC_OK();
1091 }
1092
1093 // We need to have an implementation of this function just so that we can keep
1094 // all references to mCallOnResume of type HttpChannelChild: it's not OK in C++
1095 // to set a member function ptr to a base class function.
1096 void HttpChannelChild::HandleAsyncAbort() {
1097 HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
1098
1099 // Ignore all the messages from background channel after channel aborted.
1100 CleanupBackgroundChannel();
1101 }
1102
1103 void HttpChannelChild::FailedAsyncOpen(const nsresult& status) {
1104 LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n", this,
1105 static_cast<uint32_t>(status)));
1106 MOZ_ASSERT(NS_IsMainThread());
1107
1108 // Might be called twice in race condition in theory.
1109 // (one by RecvFailedAsyncOpen, another by
1110 // HttpBackgroundChannelChild::ActorFailed)
1111 if (LoadOnStartRequestCalled()) {
1112 return;
1113 }
1114
1115 if (NS_SUCCEEDED(mStatus)) {
1116 mStatus = status;
1117 }
1118
1119 // We're already being called from IPDL, therefore already "async"
1120 HandleAsyncAbort();
1121
1122 if (CanSend()) {
1123 TrySendDeletingChannel();
1124 }
1125 }
1126
1127 void HttpChannelChild::CleanupBackgroundChannel() {
1128 MutexAutoLock lock(mBgChildMutex);
1129
1130 AUTO_PROFILER_LABEL("HttpChannelChild::CleanupBackgroundChannel", NETWORK);
1131 LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
1132 this, mBgChild.get()));
1133
1134 mBgInitFailCallback = nullptr;
1135
1136 if (!mBgChild) {
1137 return;
1138 }
1139
1140 RefPtr<HttpBackgroundChannelChild> bgChild = std::move(mBgChild);
1141
1142 MOZ_RELEASE_ASSERT(gSocketTransportService);
1143 if (!OnSocketThread()) {
1144 gSocketTransportService->Dispatch(
1145 NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
1146 bgChild,
1147 &HttpBackgroundChannelChild::OnChannelClosed),
1148 NS_DISPATCH_NORMAL);
1149 } else {
1150 bgChild->OnChannelClosed();
1151 }
1152 }
1153
1154 void HttpChannelChild::DoNotifyListenerCleanup() {
1155 LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
1156 }
1157
1158 void HttpChannelChild::DoAsyncAbort(nsresult aStatus) {
1159 Unused << AsyncAbort(aStatus);
1160 }
1161
1162 mozilla::ipc::IPCResult HttpChannelChild::RecvDeleteSelf() {
1163 LOG(("HttpChannelChild::RecvDeleteSelf [this=%p]\n", this));
1164 MOZ_ASSERT(NS_IsMainThread());
1165
1166 // The redirection is vetoed. No need to suspend the event queue.
1167 if (mSuspendForWaitCompleteRedirectSetup) {
1168 mSuspendForWaitCompleteRedirectSetup = false;
1169 mEventQ->Resume();
1170 }
1171
1172 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1173 this,
1174 [self = UnsafePtr<HttpChannelChild>(this)]() { self->DeleteSelf(); }));
1175 return IPC_OK();
1176 }
1177
1178 void HttpChannelChild::DeleteSelf() { Send__delete__(this); }
1179
1180 void HttpChannelChild::NotifyOrReleaseListeners(nsresult rv) {
1181 MOZ_ASSERT(NS_IsMainThread());
1182
1183 if (NS_SUCCEEDED(rv) ||
1184 (LoadOnStartRequestCalled() && LoadOnStopRequestCalled())) {
1185 ReleaseListeners();
1186 return;
1187 }
1188
1189 if (NS_SUCCEEDED(mStatus)) {
1190 mStatus = rv;
1191 }
1192
1193 // This is enough what we need. Undelivered notifications will be pushed.
1194 // DoNotifyListener ensures the call to ReleaseListeners when done.
1195 DoNotifyListener();
1196 }
1197
1198 void HttpChannelChild::DoNotifyListener() {
1199 LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
1200 MOZ_ASSERT(NS_IsMainThread());
1201
1202 // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
1203 // LOAD_ONLY_IF_MODIFIED) we want to set LoadAfterOnStartRequestBegun() to
1204 // true before notifying listener.
1205 if (!LoadAfterOnStartRequestBegun()) {
1206 StoreAfterOnStartRequestBegun(true);
1207 }
1208
1209 if (mListener && !LoadOnStartRequestCalled()) {
1210 nsCOMPtr<nsIStreamListener> listener = mListener;
1211 StoreOnStartRequestCalled(
1212 true); // avoid reentrancy bugs by setting this now
1213 listener->OnStartRequest(this);
1214 }
1215 StoreOnStartRequestCalled(true);
1216
1217 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1218 this, [self = UnsafePtr<HttpChannelChild>(this)] {
1219 self->ContinueDoNotifyListener();
1220 }));
1221 }
1222
1223 void HttpChannelChild::ContinueDoNotifyListener() {
1224 LOG(("HttpChannelChild::ContinueDoNotifyListener this=%p", this));
1225 MOZ_ASSERT(NS_IsMainThread());
1226
1227 // Make sure IsPending is set to false. At this moment we are done from
1228 // the point of view of our consumer and we have to report our self
1229 // as not-pending.
1230 StoreIsPending(false);
1231
1232 if (mListener && !LoadOnStopRequestCalled()) {
1233 nsCOMPtr<nsIStreamListener> listener = mListener;
1234 StoreOnStopRequestCalled(true);
1235 listener->OnStopRequest(this, mStatus);
1236 }
1237 StoreOnStopRequestCalled(true);
1238
1239 // notify "http-on-stop-request" observers
1240 gHttpHandler->OnStopRequest(this);
1241
1242 // This channel has finished its job, potentially release any tail-blocked
1243 // requests with this.
1244 RemoveAsNonTailRequest();
1245
1246 // We have to make sure to drop the references to listeners and callbacks
1247 // no longer needed.
1248 ReleaseListeners();
1249
1250 DoNotifyListenerCleanup();
1251
1252 // If this is a navigation, then we must let the docshell flush the reports
1253 // to the console later. The LoadDocument() is pointing at the detached
1254 // document that started the navigation. We want to show the reports on the
1255 // new document. Otherwise the console is wiped and the user never sees
1256 // the information.
1257 if (!IsNavigation()) {
1258 if (mLoadGroup) {
1259 FlushConsoleReports(mLoadGroup);
1260 } else {
1261 RefPtr<dom::Document> doc;
1262 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1263 FlushConsoleReports(doc);
1264 }
1265 }
1266 }
1267
1268 mozilla::ipc::IPCResult HttpChannelChild::RecvReportSecurityMessage(
1269 const nsString& messageTag, const nsString& messageCategory) {
1270 DebugOnly<nsresult> rv = AddSecurityMessage(messageTag, messageCategory);
1271 MOZ_ASSERT(NS_SUCCEEDED(rv));
1272 return IPC_OK();
1273 }
1274
1275 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect1Begin(
1276 const uint32_t& aRegistrarId, const URIParams& aNewUri,
1277 const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags,
1278 const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
1279 const nsHttpResponseHead& aResponseHead,
1280 const nsCString& aSecurityInfoSerialization, const uint64_t& aChannelId,
1281 const NetAddr& aOldPeerAddr, const ResourceTimingStructArgs& aTiming) {
1282 // TODO: handle security info
1283 LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
1284 // We set peer address of child to the old peer,
1285 // Then it will be updated to new peer in OnStartRequest
1286 mPeerAddr = aOldPeerAddr;
1287
1288 // Cookies headers should not be visible to the child process
1289 MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
1290
1291 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1292 this, [self = UnsafePtr<HttpChannelChild>(this), aRegistrarId, aNewUri,
1293 aNewLoadFlags, aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1294 aSecurityInfoSerialization, aChannelId, aTiming]() {
1295 self->Redirect1Begin(aRegistrarId, aNewUri, aNewLoadFlags,
1296 aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1297 aSecurityInfoSerialization, aChannelId, aTiming);
1298 }));
1299 return IPC_OK();
1300 }
1301
1302 nsresult HttpChannelChild::SetupRedirect(nsIURI* uri,
1303 const nsHttpResponseHead* responseHead,
1304 const uint32_t& redirectFlags,
1305 nsIChannel** outChannel) {
1306 LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
1307
1308 if (mCanceled) {
1309 return NS_ERROR_ABORT;
1310 }
1311
1312 nsresult rv;
1313 nsCOMPtr<nsIIOService> ioService;
1314 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
1315 NS_ENSURE_SUCCESS(rv, rv);
1316
1317 nsCOMPtr<nsIChannel> newChannel;
1318 nsCOMPtr<nsILoadInfo> redirectLoadInfo =
1319 CloneLoadInfoForRedirect(uri, redirectFlags);
1320 rv = NS_NewChannelInternal(getter_AddRefs(newChannel), uri, redirectLoadInfo,
1321 nullptr, // PerformanceStorage
1322 nullptr, // aLoadGroup
1323 nullptr, // aCallbacks
1324 nsIRequest::LOAD_NORMAL, ioService);
1325 NS_ENSURE_SUCCESS(rv, rv);
1326
1327 // We won't get OnStartRequest, set cookies here.
1328 mResponseHead = MakeUnique<nsHttpResponseHead>(*responseHead);
1329
1330 bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
1331 mResponseHead->Status(), mRequestHead.ParsedMethod());
1332
1333 rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, redirectFlags);
1334 NS_ENSURE_SUCCESS(rv, rv);
1335
1336 mRedirectChannelChild = do_QueryInterface(newChannel);
1337 newChannel.forget(outChannel);
1338
1339 return NS_OK;
1340 }
1341
1342 void HttpChannelChild::Redirect1Begin(
1343 const uint32_t& registrarId, const URIParams& newOriginalURI,
1344 const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
1345 const ParentLoadInfoForwarderArgs& loadInfoForwarder,
1346 const nsHttpResponseHead& responseHead,
1347 const nsACString& securityInfoSerialization, const uint64_t& channelId,
1348 const ResourceTimingStructArgs& timing) {
1349 nsresult rv;
1350
1351 LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
1352
1353 ipc::MergeParentLoadInfoForwarder(loadInfoForwarder, mLoadInfo);
1354
1355 nsCOMPtr<nsIURI> uri = DeserializeURI(newOriginalURI);
1356
1357 ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings);
1358
1359 #ifdef MOZ_GECKO_PROFILER
1360 if (profiler_can_accept_markers()) {
1361 nsAutoCString requestMethod;
1362 GetRequestMethod(requestMethod);
1363 nsAutoCString contentType;
1364 responseHead.ContentType(contentType);
1365
1366 profiler_add_network_marker(
1367 mURI, requestMethod, mPriority, mChannelId,
1368 NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(),
1369 0, kCacheUnknown, mLoadInfo->GetInnerWindowID(), &mTransactionTimings,
1370 std::move(mSource), Some(nsDependentCString(contentType.get())), uri,
1371 redirectFlags, channelId);
1372 }
1373 #endif
1374
1375 if (!securityInfoSerialization.IsEmpty()) {
1376 rv = NS_DeserializeObject(securityInfoSerialization,
1377 getter_AddRefs(mSecurityInfo));
1378 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
1379 "Deserializing security info should not fail");
1380 }
1381
1382 nsCOMPtr<nsIChannel> newChannel;
1383 rv = SetupRedirect(uri, &responseHead, redirectFlags,
1384 getter_AddRefs(newChannel));
1385
1386 if (NS_SUCCEEDED(rv)) {
1387 MOZ_ALWAYS_SUCCEEDS(newChannel->SetLoadFlags(newLoadFlags));
1388
1389 if (mRedirectChannelChild) {
1390 // Set the channelId allocated in parent to the child instance
1391 nsCOMPtr<nsIHttpChannel> httpChannel =
1392 do_QueryInterface(mRedirectChannelChild);
1393 if (httpChannel) {
1394 rv = httpChannel->SetChannelId(channelId);
1395 MOZ_ASSERT(NS_SUCCEEDED(rv));
1396 }
1397 mRedirectChannelChild->ConnectParent(registrarId);
1398 }
1399
1400 nsCOMPtr<nsISerialEventTarget> target = GetNeckoTarget();
1401 MOZ_ASSERT(target);
1402
1403 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags,
1404 target);
1405 }
1406
1407 if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1408 }
1409
1410 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect3Complete() {
1411 LOG(("HttpChannelChild::RecvRedirect3Complete [this=%p]\n", this));
1412 nsCOMPtr<nsIChannel> redirectChannel =
1413 do_QueryInterface(mRedirectChannelChild);
1414 MOZ_ASSERT(redirectChannel);
1415 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1416 this, [self = UnsafePtr<HttpChannelChild>(this), redirectChannel]() {
1417 nsresult rv = NS_OK;
1418 Unused << self->GetStatus(&rv);
1419 if (NS_FAILED(rv)) {
1420 // Pre-redirect channel was canceled. Call |HandleAsyncAbort|, so
1421 // mListener's OnStart/StopRequest can be called. Nothing else will
1422 // trigger these notification after this point.
1423 // We do this before |CompleteRedirectSetup|, so post-redirect channel
1424 // stays unopened and we also make sure that OnStart/StopRequest won't
1425 // be called twice.
1426 self->HandleAsyncAbort();
1427
1428 nsCOMPtr<nsIHttpChannelChild> chan =
1429 do_QueryInterface(redirectChannel);
1430 RefPtr<HttpChannelChild> httpChannelChild =
1431 static_cast<HttpChannelChild*>(chan.get());
1432 if (httpChannelChild) {
1433 // For sending an IPC message to parent channel so that the loading
1434 // can be cancelled.
1435 Unused << httpChannelChild->Cancel(rv);
1436
1437 // The post-redirect channel could still get OnStart/StopRequest IPC
1438 // messages from parent, but the mListener is still null. So, we
1439 // call |DoNotifyListener| to pretend that OnStart/StopRequest are
1440 // already called.
1441 httpChannelChild->DoNotifyListener();
1442 }
1443 return;
1444 }
1445
1446 self->Redirect3Complete();
1447 }));
1448 return IPC_OK();
1449 }
1450
1451 void HttpChannelChild::ProcessNotifyClassificationFlags(
1452 uint32_t aClassificationFlags, bool aIsThirdParty) {
1453 LOG(
1454 ("HttpChannelChild::ProcessNotifyClassificationFlags thirdparty=%d "
1455 "flags=%" PRIu32 " [this=%p]\n",
1456 static_cast<int>(aIsThirdParty), aClassificationFlags, this));
1457 MOZ_ASSERT(OnSocketThread());
1458
1459 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1460 this, [self = UnsafePtr<HttpChannelChild>(this), aClassificationFlags,
1461 aIsThirdParty]() {
1462 self->AddClassificationFlags(aClassificationFlags, aIsThirdParty);
1463 }));
1464 }
1465
1466 void HttpChannelChild::ProcessNotifyFlashPluginStateChanged(
1467 nsIHttpChannel::FlashPluginState aState) {
1468 LOG(("HttpChannelChild::ProcessNotifyFlashPluginStateChanged [this=%p]\n",
1469 this));
1470 MOZ_ASSERT(OnSocketThread());
1471
1472 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1473 this, [self = UnsafePtr<HttpChannelChild>(this), aState]() {
1474 self->SetFlashPluginState(aState);
1475 }));
1476 }
1477
1478 void HttpChannelChild::ProcessSetClassifierMatchedInfo(
1479 const nsCString& aList, const nsCString& aProvider,
1480 const nsCString& aFullHash) {
1481 LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
1482 MOZ_ASSERT(OnSocketThread());
1483
1484 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1485 this,
1486 [self = UnsafePtr<HttpChannelChild>(this), aList, aProvider,
1487 aFullHash]() { self->SetMatchedInfo(aList, aProvider, aFullHash); }));
1488 }
1489
1490 void HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo(
1491 const nsCString& aLists, const nsCString& aFullHashes) {
1492 LOG(("HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo [this=%p]\n",
1493 this));
1494 MOZ_ASSERT(OnSocketThread());
1495
1496 nsTArray<nsCString> lists, fullhashes;
1497 for (const nsACString& token : aLists.Split(',')) {
1498 lists.AppendElement(token);
1499 }
1500 for (const nsACString& token : aFullHashes.Split(',')) {
1501 fullhashes.AppendElement(token);
1502 }
1503
1504 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1505 this, [self = UnsafePtr<HttpChannelChild>(this),
1506 lists = CopyableTArray{std::move(lists)},
1507 fullhashes = CopyableTArray{std::move(fullhashes)}]() {
1508 self->SetMatchedTrackingInfo(lists, fullhashes);
1509 }));
1510 }
1511
1512 // Completes the redirect and cleans up the old channel.
1513 void HttpChannelChild::Redirect3Complete() {
1514 LOG(("HttpChannelChild::Redirect3Complete [this=%p]\n", this));
1515 MOZ_ASSERT(NS_IsMainThread());
1516
1517 // Using an error as the default so that when we fail to forward this redirect
1518 // to the target channel, we make sure to notify the current listener from
1519 // CleanupRedirectingChannel.
1520 nsresult rv = NS_BINDING_ABORTED;
1521
1522 nsCOMPtr<nsIRedirectResultListener> vetoHook;
1523 GetCallback(vetoHook);
1524 if (vetoHook) {
1525 vetoHook->OnRedirectResult(true);
1526 }
1527
1528 // Chrome channel has been AsyncOpen'd. Reflect this in child.
1529 if (mRedirectChannelChild) {
1530 rv = mRedirectChannelChild->CompleteRedirectSetup(mListener);
1531 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1532 mSuccesfullyRedirected = NS_SUCCEEDED(rv);
1533 #endif
1534 }
1535
1536 CleanupRedirectingChannel(rv);
1537 }
1538
1539 void HttpChannelChild::CleanupRedirectingChannel(nsresult rv) {
1540 // Redirecting to new channel: shut this down and init new channel
1541 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED);
1542
1543 if (NS_SUCCEEDED(rv)) {
1544 nsCString remoteAddress;
1545 Unused << GetRemoteAddress(remoteAddress);
1546 nsCOMPtr<nsIURI> referrer;
1547 if (mReferrerInfo) {
1548 referrer = mReferrerInfo->GetComputedReferrer();
1549 }
1550
1551 nsCOMPtr<nsIRedirectHistoryEntry> entry =
1552 new nsRedirectHistoryEntry(GetURIPrincipal(), referrer, remoteAddress);
1553
1554 mLoadInfo->AppendRedirectHistoryEntry(entry, false);
1555 } else {
1556 NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?");
1557 }
1558
1559 // Release ref to new channel.
1560 mRedirectChannelChild = nullptr;
1561
1562 NotifyOrReleaseListeners(rv);
1563 CleanupBackgroundChannel();
1564 }
1565
1566 //-----------------------------------------------------------------------------
1567 // HttpChannelChild::nsIChildChannel
1568 //-----------------------------------------------------------------------------
1569
1570 NS_IMETHODIMP
1571 HttpChannelChild::ConnectParent(uint32_t registrarId) {
1572 LOG(("HttpChannelChild::ConnectParent [this=%p, id=%" PRIu32 "]\n", this,
1573 registrarId));
1574 MOZ_ASSERT(NS_IsMainThread());
1575 mozilla::dom::BrowserChild* browserChild = nullptr;
1576 nsCOMPtr<nsIBrowserChild> iBrowserChild;
1577 GetCallback(iBrowserChild);
1578 if (iBrowserChild) {
1579 browserChild =
1580 static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
1581 }
1582
1583 if (browserChild && !browserChild->IPCOpen()) {
1584 return NS_ERROR_FAILURE;
1585 }
1586
1587 ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
1588 if (cc->IsShuttingDown()) {
1589 return NS_ERROR_FAILURE;
1590 }
1591
1592 HttpBaseChannel::SetDocshellUserAgentOverride();
1593
1594 // This must happen before the constructor message is sent. Otherwise messages
1595 // from the parent could arrive quickly and be delivered to the wrong event
1596 // target.
1597 SetEventTarget();
1598
1599 if (browserChild) {
1600 MOZ_ASSERT(browserChild->WebNavigation());
1601 if (BrowsingContext* bc = browserChild->GetBrowsingContext()) {
1602 mTopBrowsingContextId = bc->Top()->Id();
1603 }
1604 }
1605
1606 HttpChannelConnectArgs connectArgs(registrarId);
1607 if (!gNeckoChild->SendPHttpChannelConstructor(
1608 this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
1609 return NS_ERROR_FAILURE;
1610 }
1611
1612 {
1613 MutexAutoLock lock(mBgChildMutex);
1614
1615 MOZ_ASSERT(!mBgChild);
1616 MOZ_ASSERT(!mBgInitFailCallback);
1617
1618 mBgInitFailCallback = NewRunnableMethod<nsresult>(
1619 "HttpChannelChild::OnRedirectVerifyCallback", this,
1620 &HttpChannelChild::OnRedirectVerifyCallback, NS_ERROR_FAILURE);
1621
1622 RefPtr<HttpBackgroundChannelChild> bgChild =
1623 new HttpBackgroundChannelChild();
1624
1625 MOZ_RELEASE_ASSERT(gSocketTransportService);
1626
1627 RefPtr<HttpChannelChild> self = this;
1628 nsresult rv = gSocketTransportService->Dispatch(
1629 NewRunnableMethod<RefPtr<HttpChannelChild>>(
1630 "HttpBackgroundChannelChild::Init", bgChild,
1631 &HttpBackgroundChannelChild::Init, std::move(self)),
1632 NS_DISPATCH_NORMAL);
1633
1634 if (NS_WARN_IF(NS_FAILED(rv))) {
1635 return rv;
1636 }
1637
1638 mBgChild = std::move(bgChild);
1639 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1640 mEverHadBgChildAtConnectParent = true;
1641 #endif
1642 }
1643
1644 // Should wait for CompleteRedirectSetup to set the listener.
1645 mEventQ->Suspend();
1646 MOZ_ASSERT(!mSuspendForWaitCompleteRedirectSetup);
1647 mSuspendForWaitCompleteRedirectSetup = true;
1648
1649 // Connect to socket process after mEventQ is suspended.
1650 MaybeConnectToSocketProcess();
1651
1652 return NS_OK;
1653 }
1654
1655 NS_IMETHODIMP
1656 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
1657 LOG(("HttpChannelChild::CompleteRedirectSetup [this=%p]\n", this));
1658 MOZ_ASSERT(NS_IsMainThread());
1659
1660 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
1661 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
1662
1663 // Resume the suspension in ConnectParent.
1664 auto eventQueueResumeGuard = MakeScopeExit([&] {
1665 MOZ_ASSERT(mSuspendForWaitCompleteRedirectSetup);
1666 mEventQ->Resume();
1667 mSuspendForWaitCompleteRedirectSetup = false;
1668 });
1669
1670 /*
1671 * No need to check for cancel: we don't get here if nsHttpChannel canceled
1672 * before AsyncOpen(); if it's canceled after that, OnStart/Stop will just
1673 * get called with error code as usual. So just setup mListener and make the
1674 * channel reflect AsyncOpen'ed state.
1675 */
1676
1677 mLastStatusReported = TimeStamp::Now();
1678 #ifdef MOZ_GECKO_PROFILER
1679 if (profiler_can_accept_markers()) {
1680 nsAutoCString requestMethod;
1681 GetRequestMethod(requestMethod);
1682
1683 profiler_add_network_marker(
1684 mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
1685 mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
1686 mLoadInfo->GetInnerWindowID());
1687 }
1688 #endif
1689 StoreIsPending(true);
1690 StoreWasOpened(true);
1691 mListener = aListener;
1692
1693 // add ourselves to the load group.
1694 if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
1695
1696 // We already have an open IPDL connection to the parent. If on-modify-request
1697 // listeners or load group observers canceled us, let the parent handle it
1698 // and send it back to us naturally.
1699 return NS_OK;
1700 }
1701
1702 //-----------------------------------------------------------------------------
1703 // HttpChannelChild::nsIAsyncVerifyRedirectCallback
1704 //-----------------------------------------------------------------------------
1705
1706 NS_IMETHODIMP
1707 HttpChannelChild::OnRedirectVerifyCallback(nsresult aResult) {
1708 LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
1709 MOZ_ASSERT(NS_IsMainThread());
1710 Maybe<URIParams> redirectURI;
1711 nsresult rv;
1712
1713 nsCOMPtr<nsIHttpChannel> newHttpChannel =
1714 do_QueryInterface(mRedirectChannelChild);
1715
1716 if (NS_SUCCEEDED(aResult) && !mRedirectChannelChild) {
1717 // mRedirectChannelChild doesn't exist means we're redirecting to a protocol
1718 // that doesn't implement nsIChildChannel. The redirect result should be set
1719 // as failed by veto listeners and shouldn't enter this condition. As the
1720 // last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
1721 // to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
1722 // another protocol and throw an error.
1723 LOG((" redirecting to a protocol that doesn't implement nsIChildChannel"));
1724 aResult = NS_ERROR_DOM_BAD_URI;
1725 }
1726
1727 nsCOMPtr<nsIReferrerInfo> referrerInfo;
1728 if (newHttpChannel) {
1729 // Must not be called until after redirect observers called.
1730 newHttpChannel->SetOriginalURI(mOriginalURI);
1731 referrerInfo = newHttpChannel->GetReferrerInfo();
1732 }
1733
1734 RequestHeaderTuples emptyHeaders;
1735 RequestHeaderTuples* headerTuples = &emptyHeaders;
1736 nsLoadFlags loadFlags = 0;
1737 Maybe<CorsPreflightArgs> corsPreflightArgs;
1738
1739 nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild =
1740 do_QueryInterface(mRedirectChannelChild);
1741 if (newHttpChannelChild && NS_SUCCEEDED(aResult)) {
1742 rv = newHttpChannelChild->AddCookiesToRequest();
1743 MOZ_ASSERT(NS_SUCCEEDED(rv));
1744 rv = newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples);
1745 MOZ_ASSERT(NS_SUCCEEDED(rv));
1746 newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs);
1747 }
1748
1749 /* If the redirect was canceled, bypass OMR and send an empty API
1750 * redirect URI */
1751 SerializeURI(nullptr, redirectURI);
1752
1753 if (NS_SUCCEEDED(aResult)) {
1754 // Note: this is where we would notify "http-on-modify-response" observers.
1755 // We have deliberately disabled this for child processes (see bug 806753)
1756 //
1757 // After we verify redirect, nsHttpChannel may hit the network: must give
1758 // "http-on-modify-request" observers the chance to cancel before that.
1759 // base->CallOnModifyRequestObservers();
1760
1761 nsCOMPtr<nsIHttpChannelInternal> newHttpChannelInternal =
1762 do_QueryInterface(mRedirectChannelChild);
1763 if (newHttpChannelInternal) {
1764 nsCOMPtr<nsIURI> apiRedirectURI;
1765 rv = newHttpChannelInternal->GetApiRedirectToURI(
1766 getter_AddRefs(apiRedirectURI));
1767 if (NS_SUCCEEDED(rv) && apiRedirectURI) {
1768 /* If there was an API redirect of this channel, we need to send it
1769 * up here, since it can't be sent via SendAsyncOpen. */
1770 SerializeURI(apiRedirectURI, redirectURI);
1771 }
1772 }
1773
1774 nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
1775 if (request) {
1776 request->GetLoadFlags(&loadFlags);
1777 }
1778 }
1779
1780 uint32_t sourceRequestBlockingReason = 0;
1781 mLoadInfo->GetRequestBlockingReason(&sourceRequestBlockingReason);
1782
1783 Maybe<ChildLoadInfoForwarderArgs> targetLoadInfoForwarder;
1784 nsCOMPtr<nsIChannel> newChannel = do_QueryInterface(mRedirectChannelChild);
1785 if (newChannel) {
1786 ChildLoadInfoForwarderArgs args;
1787 nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo();
1788 LoadInfoToChildLoadInfoForwarder(loadInfo, &args);
1789 targetLoadInfoForwarder.emplace(args);
1790 }
1791
1792 if (CanSend()) {
1793 SendRedirect2Verify(aResult, *headerTuples, sourceRequestBlockingReason,
1794 targetLoadInfoForwarder, loadFlags, referrerInfo,
1795 redirectURI, corsPreflightArgs);
1796 }
1797
1798 return NS_OK;
1799 }
1800
1801 //-----------------------------------------------------------------------------
1802 // HttpChannelChild::nsIRequest
1803 //-----------------------------------------------------------------------------
1804
1805 NS_IMETHODIMP
1806 HttpChannelChild::Cancel(nsresult aStatus) {
1807 LOG(("HttpChannelChild::Cancel [this=%p, status=%" PRIx32 "]\n", this,
1808 static_cast<uint32_t>(aStatus)));
1809 LogCallingScriptLocation(this);
1810
1811 MOZ_ASSERT(NS_IsMainThread());
1812
1813 if (!mCanceled) {
1814 // If this cancel occurs before nsHttpChannel has been set up, AsyncOpen
1815 // is responsible for cleaning up.
1816 mCanceled = true;
1817 mStatus = aStatus;
1818
1819 bool remoteChannelExists = RemoteChannelExists();
1820 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1821 mCanSendAtCancel = CanSend();
1822 mRemoteChannelExistedAtCancel = remoteChannelExists;
1823 #endif
1824
1825 if (remoteChannelExists) {
1826 SendCancel(aStatus, mLoadInfo->GetRequestBlockingReason());
1827 } else if (MOZ_UNLIKELY(!LoadOnStartRequestCalled() ||
1828 !LoadOnStopRequestCalled())) {
1829 Unused << AsyncAbort(mStatus);
1830 }
1831 }
1832 return NS_OK;
1833 }
1834
1835 NS_IMETHODIMP
1836 HttpChannelChild::Suspend() {
1837 LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 "\n", this,
1838 mSuspendCount + 1));
1839 MOZ_ASSERT(NS_IsMainThread());
1840
1841 LogCallingScriptLocation(this);
1842
1843 // SendSuspend only once, when suspend goes from 0 to 1.
1844 // Don't SendSuspend at all if we're diverting callbacks to the parent;
1845 // suspend will be called at the correct time in the parent itself.
1846 if (!mSuspendCount++) {
1847 if (RemoteChannelExists()) {
1848 SendSuspend();
1849 mSuspendSent = true;
1850 }
1851 }
1852 mEventQ->Suspend();
1853
1854 return NS_OK;
1855 }
1856
1857 NS_IMETHODIMP
1858 HttpChannelChild::Resume() {
1859 LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 "\n", this,
1860 mSuspendCount - 1));
1861 MOZ_ASSERT(NS_IsMainThread());
1862 NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
1863
1864 LogCallingScriptLocation(this);
1865
1866 nsresult rv = NS_OK;
1867
1868 // SendResume only once, when suspend count drops to 0.
1869 // Don't SendResume at all if we're diverting callbacks to the parent (unless
1870 // suspend was sent earlier); otherwise, resume will be called at the correct
1871 // time in the parent itself.
1872 if (!--mSuspendCount) {
1873 if (RemoteChannelExists() && mSuspendSent) {
1874 SendResume();
1875 }
1876 if (mCallOnResume) {
1877 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
1878 MOZ_ASSERT(neckoTarget);
1879
1880 RefPtr<HttpChannelChild> self = this;
1881 std::function<nsresult(HttpChannelChild*)> callOnResume = nullptr;
1882 std::swap(callOnResume, mCallOnResume);
1883 rv = neckoTarget->Dispatch(
1884 NS_NewRunnableFunction(
1885 "net::HttpChannelChild::mCallOnResume",
1886 [callOnResume, self{std::move(self)}]() { callOnResume(self); }),
1887 NS_DISPATCH_NORMAL);
1888 }
1889 }
1890 mEventQ->Resume();
1891
1892 return rv;
1893 }
1894
1895 //-----------------------------------------------------------------------------
1896 // HttpChannelChild::nsIChannel
1897 //-----------------------------------------------------------------------------
1898
1899 NS_IMETHODIMP
1900 HttpChannelChild::GetSecurityInfo(nsISupports** aSecurityInfo) {
1901 NS_ENSURE_ARG_POINTER(aSecurityInfo);
1902 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
1903 return NS_OK;
1904 }
1905
1906 NS_IMETHODIMP
1907 HttpChannelChild::AsyncOpen(nsIStreamListener* aListener) {
1908 LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
1909
1910 nsresult rv = AsyncOpenInternal(aListener);
1911 if (NS_FAILED(rv)) {
1912 uint32_t blockingReason = 0;
1913 mLoadInfo->GetRequestBlockingReason(&blockingReason);
1914 LOG(
1915 ("HttpChannelChild::AsyncOpen failed [this=%p rv=0x%08x "
1916 "blocking-reason=%u]\n",
1917 this, static_cast<uint32_t>(rv), blockingReason));
1918
1919 gHttpHandler->OnFailedOpeningRequest(this);
1920 }
1921
1922 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1923 mAsyncOpenSucceeded = NS_SUCCEEDED(rv);
1924 #endif
1925 return rv;
1926 }
1927
1928 nsresult HttpChannelChild::AsyncOpenInternal(nsIStreamListener* aListener) {
1929 nsresult rv;
1930
1931 nsCOMPtr<nsIStreamListener> listener = aListener;
1932 rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
1933 if (NS_WARN_IF(NS_FAILED(rv))) {
1934 ReleaseListeners();
1935 return rv;
1936 }
1937
1938 MOZ_ASSERT(
1939 mLoadInfo->GetSecurityMode() == 0 ||
1940 mLoadInfo->GetInitialSecurityCheckDone() ||
1941 (mLoadInfo->GetSecurityMode() ==
1942 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
1943 mLoadInfo->GetLoadingPrincipal() &&
1944 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
1945 "security flags in loadInfo but doContentSecurityCheck() not called");
1946
1947 LogCallingScriptLocation(this);
1948
1949 if (!mLoadGroup && !mCallbacks) {
1950 // If no one called SetLoadGroup or SetNotificationCallbacks, the private
1951 // state has not been updated on PrivateBrowsingChannel (which we derive
1952 // from) Hence, we have to call UpdatePrivateBrowsing() here
1953 UpdatePrivateBrowsing();
1954 }
1955
1956 #ifdef DEBUG
1957 AssertPrivateBrowsingId();
1958 #endif
1959
1960 if (mCanceled) {
1961 ReleaseListeners();
1962 return mStatus;
1963 }
1964
1965 NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
1966 NS_ENSURE_ARG_POINTER(listener);
1967 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
1968 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
1969
1970 if (MaybeWaitForUploadStreamLength(listener, nullptr)) {
1971 return NS_OK;
1972 }
1973
1974 if (!LoadAsyncOpenTimeOverriden()) {
1975 mAsyncOpenTime = TimeStamp::Now();
1976 }
1977
1978 // Port checked in parent, but duplicate here so we can return with error
1979 // immediately
1980 rv = NS_CheckPortSafety(mURI);
1981 if (NS_FAILED(rv)) {
1982 ReleaseListeners();
1983 return rv;
1984 }
1985
1986 nsAutoCString cookie;
1987 if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookie))) {
1988 mUserSetCookieHeader = cookie;
1989 }
1990
1991 DebugOnly<nsresult> check = AddCookiesToRequest();
1992 MOZ_ASSERT(NS_SUCCEEDED(check));
1993
1994 //
1995 // NOTE: From now on we must return NS_OK; all errors must be handled via
1996 // OnStart/OnStopRequest
1997 //
1998
1999 // We notify "http-on-opening-request" observers in the child
2000 // process so that devtools can capture a stack trace at the
2001 // appropriate spot. See bug 806753 for some information about why
2002 // other http-* notifications are disabled in child processes.
2003 gHttpHandler->OnOpeningRequest(this);
2004
2005 mLastStatusReported = TimeStamp::Now();
2006 #ifdef MOZ_GECKO_PROFILER
2007 if (profiler_can_accept_markers()) {
2008 nsAutoCString requestMethod;
2009 GetRequestMethod(requestMethod);
2010
2011 profiler_add_network_marker(
2012 mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
2013 mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
2014 mLoadInfo->GetInnerWindowID());
2015 }
2016 #endif
2017 StoreIsPending(true);
2018 StoreWasOpened(true);
2019 mListener = listener;
2020
2021 // add ourselves to the load group.
2022 if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
2023
2024 if (mCanceled) {
2025 // We may have been canceled already, either by on-modify-request
2026 // listeners or by load group observers; in that case, don't create IPDL
2027 // connection. See nsHttpChannel::AsyncOpen().
2028 ReleaseListeners();
2029 return mStatus;
2030 }
2031
2032 // Set user agent override from docshell
2033 HttpBaseChannel::SetDocshellUserAgentOverride();
2034
2035 rv = ContinueAsyncOpen();
2036 if (NS_FAILED(rv)) {
2037 ReleaseListeners();
2038 }
2039 return rv;
2040 }
2041
2042 // Assigns an nsISerialEventTarget to our IPDL actor so that IPC messages are
2043 // sent to the correct DocGroup/TabGroup.
2044 void HttpChannelChild::SetEventTarget() {
2045 nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
2046
2047 nsCOMPtr<nsISerialEventTarget> target =
2048 nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Network);
2049
2050 if (!target) {
2051 return;
2052 }
2053
2054 gNeckoChild->SetEventTargetForActor(this, target);
2055
2056 {
2057 MutexAutoLock lock(mEventTargetMutex);
2058 mNeckoTarget = target;
2059 }
2060 }
2061
2062 already_AddRefed<nsISerialEventTarget> HttpChannelChild::GetNeckoTarget() {
2063 nsCOMPtr<nsISerialEventTarget> target;
2064 {
2065 MutexAutoLock lock(mEventTargetMutex);
2066 target = mNeckoTarget;
2067 }
2068
2069 if (!target) {
2070 target = GetMainThreadSerialEventTarget();
2071 }
2072 return target.forget();
2073 }
2074
2075 already_AddRefed<nsIEventTarget> HttpChannelChild::GetODATarget() {
2076 nsCOMPtr<nsIEventTarget> target;
2077 {
2078 MutexAutoLock lock(mEventTargetMutex);
2079 if (mODATarget) {
2080 target = mODATarget;
2081 } else {
2082 target = mNeckoTarget;
2083 }
2084 }
2085
2086 if (!target) {
2087 target = GetMainThreadEventTarget();
2088 }
2089 return target.forget();
2090 }
2091
2092 nsresult HttpChannelChild::ContinueAsyncOpen() {
2093 nsresult rv;
2094 //
2095 // Send request to the chrome process...
2096 //
2097
2098 mozilla::dom::BrowserChild* browserChild = nullptr;
2099 nsCOMPtr<nsIBrowserChild> iBrowserChild;
2100 GetCallback(iBrowserChild);
2101 if (iBrowserChild) {
2102 browserChild =
2103 static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
2104 }
2105
2106 // This id identifies the inner window's top-level document,
2107 // which changes on every new load or navigation.
2108 uint64_t contentWindowId = 0;
2109 TimeStamp navigationStartTimeStamp;
2110 if (browserChild) {
2111 MOZ_ASSERT(browserChild->WebNavigation());
2112 if (RefPtr<Document> document = browserChild->GetTopLevelDocument()) {
2113 contentWindowId = document->InnerWindowID();
2114 nsDOMNavigationTiming* navigationTiming = document->GetNavigationTiming();
2115 if (navigationTiming) {
2116 navigationStartTimeStamp =
2117 navigationTiming->GetNavigationStartTimeStamp();
2118 }
2119 }
2120 if (BrowsingContext* bc = browserChild->GetBrowsingContext()) {
2121 mTopBrowsingContextId = bc->Top()->Id();
2122 }
2123 }
2124 SetTopLevelContentWindowId(contentWindowId);
2125
2126 HttpChannelOpenArgs openArgs;
2127 // No access to HttpChannelOpenArgs members, but they each have a
2128 // function with the struct name that returns a ref.
2129 SerializeURI(mURI, openArgs.uri());
2130 SerializeURI(mOriginalURI, openArgs.original());
2131 SerializeURI(mDocumentURI, openArgs.doc());
2132 SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
2133 openArgs.loadFlags() = mLoadFlags;
2134 openArgs.requestHeaders() = mClientSetRequestHeaders;
2135 mRequestHead.Method(openArgs.requestMethod());
2136 openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes.Clone();
2137 openArgs.referrerInfo() = mReferrerInfo;
2138
2139 AutoIPCStream autoStream(openArgs.uploadStream());
2140 if (mUploadStream) {
2141 autoStream.Serialize(mUploadStream, ContentChild::GetSingleton());
2142 autoStream.TakeOptionalValue();
2143 }
2144
2145 Maybe<CorsPreflightArgs> optionalCorsPreflightArgs;
2146 GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs);
2147
2148 // NB: This call forces us to cache mTopWindowURI if we haven't already.
2149 nsCOMPtr<nsIURI> uri;
2150 GetTopWindowURI(mURI, getter_AddRefs(uri));
2151
2152 SerializeURI(mTopWindowURI, openArgs.topWindowURI());
2153
2154 openArgs.preflightArgs() = optionalCorsPreflightArgs;
2155
2156 openArgs.uploadStreamHasHeaders() = LoadUploadStreamHasHeaders();
2157 openArgs.priority() = mPriority;
2158 openArgs.classOfService() = mClassOfService;
2159 openArgs.redirectionLimit() = mRedirectionLimit;
2160 openArgs.allowSTS() = LoadAllowSTS();
2161 openArgs.thirdPartyFlags() = LoadThirdPartyFlags();
2162 openArgs.resumeAt() = mSendResumeAt;
2163 openArgs.startPos() = mStartPos;
2164 openArgs.entityID() = mEntityID;
2165 openArgs.allowSpdy() = LoadAllowSpdy();
2166 openArgs.allowHttp3() = LoadAllowHttp3();
2167 openArgs.allowAltSvc() = LoadAllowAltSvc();
2168 openArgs.beConservative() = LoadBeConservative();
2169 openArgs.tlsFlags() = mTlsFlags;
2170 openArgs.initialRwin() = mInitialRwin;
2171
2172 openArgs.cacheKey() = mCacheKey;
2173
2174 openArgs.blockAuthPrompt() = LoadBlockAuthPrompt();
2175
2176 openArgs.allowStaleCacheContent() = LoadAllowStaleCacheContent();
2177 openArgs.preferCacheLoadOverBypass() = LoadPreferCacheLoadOverBypass();
2178
2179 openArgs.contentTypeHint() = mContentTypeHint;
2180
2181 rv = mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &openArgs.loadInfo());
2182 NS_ENSURE_SUCCESS(rv, rv);
2183
2184 EnsureRequestContextID();
2185 openArgs.requestContextID() = mRequestContextID;
2186
2187 openArgs.corsMode() = mCorsMode;
2188 openArgs.redirectMode() = mRedirectMode;
2189
2190 openArgs.channelId() = mChannelId;
2191
2192 openArgs.integrityMetadata() = mIntegrityMetadata;
2193
2194 openArgs.contentWindowId() = contentWindowId;
2195 openArgs.topBrowsingContextId() = mTopBrowsingContextId;
2196
2197 LOG(("HttpChannelChild::ContinueAsyncOpen this=%p gid=%" PRIu64
2198 " top bid=%" PRIx64,
2199 this, mChannelId, mTopBrowsingContextId));
2200
2201 if (browserChild && !browserChild->IPCOpen()) {
2202 return NS_ERROR_FAILURE;
2203 }
2204
2205 ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
2206 if (cc->IsShuttingDown()) {
2207 return NS_ERROR_FAILURE;
2208 }
2209
2210 openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
2211 openArgs.launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
2212 openArgs.dispatchFetchEventStart() = mDispatchFetchEventStart;
2213 openArgs.dispatchFetchEventEnd() = mDispatchFetchEventEnd;
2214 openArgs.handleFetchEventStart() = mHandleFetchEventStart;
2215 openArgs.handleFetchEventEnd() = mHandleFetchEventEnd;
2216
2217 openArgs.forceMainDocumentChannel() = LoadForceMainDocumentChannel();
2218
2219 openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
2220
2221 // This must happen before the constructor message is sent. Otherwise messages
2222 // from the parent could arrive quickly and be delivered to the wrong event
2223 // target.
2224 SetEventTarget();
2225
2226 if (!gNeckoChild->SendPHttpChannelConstructor(
2227 this, browserChild, IPC::SerializedLoadContext(this), openArgs)) {
2228 return NS_ERROR_FAILURE;
2229 }
2230
2231 {
2232 MutexAutoLock lock(mBgChildMutex);
2233
2234 MOZ_RELEASE_ASSERT(gSocketTransportService);
2235
2236 // Service worker might use the same HttpChannelChild to do async open
2237 // twice. Need to disconnect with previous background channel before
2238 // creating the new one, to prevent receiving further notification
2239 // from it.
2240 if (mBgChild) {
2241 RefPtr<HttpBackgroundChannelChild> prevBgChild = std::move(mBgChild);
2242 gSocketTransportService->Dispatch(
2243 NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
2244 prevBgChild,
2245 &HttpBackgroundChannelChild::OnChannelClosed),
2246 NS_DISPATCH_NORMAL);
2247 }
2248
2249 MOZ_ASSERT(!mBgInitFailCallback);
2250
2251 mBgInitFailCallback = NewRunnableMethod<nsresult>(
2252 "HttpChannelChild::FailedAsyncOpen", this,
2253 &HttpChannelChild::FailedAsyncOpen, NS_ERROR_FAILURE);
2254
2255 RefPtr<HttpBackgroundChannelChild> bgChild =
2256 new HttpBackgroundChannelChild();
2257
2258 RefPtr<HttpChannelChild> self = this;
2259 nsresult rv = gSocketTransportService->Dispatch(
2260 NewRunnableMethod<RefPtr<HttpChannelChild>>(
2261 "HttpBackgroundChannelChild::Init", bgChild,
2262 &HttpBackgroundChannelChild::Init, self),
2263 NS_DISPATCH_NORMAL);
2264
2265 if (NS_WARN_IF(NS_FAILED(rv))) {
2266 return rv;
2267 }
2268
2269 mBgChild = std::move(bgChild);
2270 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2271 mEverHadBgChildAtAsyncOpen = true;
2272 #endif
2273 }
2274
2275 MaybeConnectToSocketProcess();
2276
2277 return NS_OK;
2278 }
2279
2280 //-----------------------------------------------------------------------------
2281 // HttpChannelChild::nsIHttpChannel
2282 //-----------------------------------------------------------------------------
2283
2284 NS_IMETHODIMP
2285 HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
2286 const nsACString& aValue, bool aMerge) {
2287 LOG(("HttpChannelChild::SetRequestHeader [this=%p]\n", this));
2288 nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge);
2289 if (NS_FAILED(rv)) return rv;
2290
2291 RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2292 if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2293
2294 tuple->mHeader = aHeader;
2295 tuple->mValue = aValue;
2296 tuple->mMerge = aMerge;
2297 tuple->mEmpty = false;
2298 return NS_OK;
2299 }
2300
2301 NS_IMETHODIMP
2302 HttpChannelChild::SetEmptyRequestHeader(const nsACString& aHeader) {
2303 LOG(("HttpChannelChild::SetEmptyRequestHeader [this=%p]\n", this));
2304 nsresult rv = HttpBaseChannel::SetEmptyRequestHeader(aHeader);
2305 if (NS_FAILED(rv)) return rv;
2306
2307 RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2308 if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2309
2310 tuple->mHeader = aHeader;
2311 tuple->mMerge = false;
2312 tuple->mEmpty = true;
2313 return NS_OK;
2314 }
2315
2316 NS_IMETHODIMP
2317 HttpChannelChild::RedirectTo(nsIURI* newURI) {
2318 // disabled until/unless addons run in child or something else needs this
2319 return NS_ERROR_NOT_IMPLEMENTED;
2320 }
2321
2322 NS_IMETHODIMP
2323 HttpChannelChild::UpgradeToSecure() {
2324 // disabled until/unless addons run in child or something else needs this
2325 return NS_ERROR_NOT_IMPLEMENTED;
2326 }
2327
2328 NS_IMETHODIMP
2329 HttpChannelChild::GetProtocolVersion(nsACString& aProtocolVersion) {
2330 aProtocolVersion = mProtocolVersion;
2331 return NS_OK;
2332 }
2333
2334 //-----------------------------------------------------------------------------
2335 // HttpChannelChild::nsIHttpChannelInternal
2336 //-----------------------------------------------------------------------------
2337
2338 NS_IMETHODIMP
2339 HttpChannelChild::GetIsAuthChannel(bool* aIsAuthChannel) { DROP_DEAD(); }
2340
2341 //-----------------------------------------------------------------------------
2342 // HttpChannelChild::nsICacheInfoChannel
2343 //-----------------------------------------------------------------------------
2344
2345 NS_IMETHODIMP
2346 HttpChannelChild::GetCacheTokenFetchCount(int32_t* _retval) {
2347 NS_ENSURE_ARG_POINTER(_retval);
2348 MOZ_ASSERT(NS_IsMainThread());
2349
2350 if (!mCacheEntryAvailable && !mAltDataCacheEntryAvailable) {
2351 return NS_ERROR_NOT_AVAILABLE;
2352 }
2353
2354 *_retval = mCacheFetchCount;
2355 return NS_OK;
2356 }
2357
2358 NS_IMETHODIMP
2359 HttpChannelChild::GetCacheTokenExpirationTime(uint32_t* _retval) {
2360 NS_ENSURE_ARG_POINTER(_retval);
2361 MOZ_ASSERT(NS_IsMainThread());
2362
2363 if (!mCacheEntryAvailable) return NS_ERROR_NOT_AVAILABLE;
2364
2365 *_retval = mCacheExpirationTime;
2366 return NS_OK;
2367 }
2368
2369 NS_IMETHODIMP
2370 HttpChannelChild::IsFromCache(bool* value) {
2371 if (!LoadIsPending()) return NS_ERROR_NOT_AVAILABLE;
2372
2373 *value = mIsFromCache;
2374 return NS_OK;
2375 }
2376
2377 NS_IMETHODIMP
2378 HttpChannelChild::GetCacheEntryId(uint64_t* aCacheEntryId) {
2379 bool fromCache = false;
2380 if (NS_FAILED(IsFromCache(&fromCache)) || !fromCache ||
2381 !mCacheEntryAvailable) {
2382 return NS_ERROR_NOT_AVAILABLE;
2383 }
2384
2385 *aCacheEntryId = mCacheEntryId;
2386 return NS_OK;
2387 }
2388
2389 NS_IMETHODIMP
2390 HttpChannelChild::IsRacing(bool* aIsRacing) {
2391 if (!LoadAfterOnStartRequestBegun()) {
2392 return NS_ERROR_NOT_AVAILABLE;
2393 }
2394 *aIsRacing = mIsRacing;
2395 return NS_OK;
2396 }
2397
2398 NS_IMETHODIMP
2399 HttpChannelChild::GetCacheKey(uint32_t* cacheKey) {
2400 MOZ_ASSERT(NS_IsMainThread());
2401
2402 *cacheKey = mCacheKey;
2403 return NS_OK;
2404 }
2405 NS_IMETHODIMP
2406 HttpChannelChild::SetCacheKey(uint32_t cacheKey) {
2407 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
2408
2409 mCacheKey = cacheKey;
2410 return NS_OK;
2411 }
2412
2413 NS_IMETHODIMP
2414 HttpChannelChild::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
2415 StoreAllowStaleCacheContent(aAllowStaleCacheContent);
2416 return NS_OK;
2417 }
2418 NS_IMETHODIMP
2419 HttpChannelChild::GetAllowStaleCacheContent(bool* aAllowStaleCacheContent) {
2420 NS_ENSURE_ARG(aAllowStaleCacheContent);
2421 *aAllowStaleCacheContent = LoadAllowStaleCacheContent();
2422 return NS_OK;
2423 }
2424
2425 NS_IMETHODIMP
2426 HttpChannelChild::SetPreferCacheLoadOverBypass(
2427 bool aPreferCacheLoadOverBypass) {
2428 StorePreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
2429 return NS_OK;
2430 }
2431 NS_IMETHODIMP
2432 HttpChannelChild::GetPreferCacheLoadOverBypass(
2433 bool* aPreferCacheLoadOverBypass) {
2434 NS_ENSURE_ARG(aPreferCacheLoadOverBypass);
2435 *aPreferCacheLoadOverBypass = LoadPreferCacheLoadOverBypass();
2436 return NS_OK;
2437 }
2438
2439 NS_IMETHODIMP
2440 HttpChannelChild::PreferAlternativeDataType(const nsACString& aType,
2441 const nsACString& aContentType,
2442 bool aDeliverAltData) {
2443 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
2444
2445 mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
2446 nsCString(aType), nsCString(aContentType), aDeliverAltData));
2447 return NS_OK;
2448 }
2449
2450 const nsTArray<PreferredAlternativeDataTypeParams>&
2451 HttpChannelChild::PreferredAlternativeDataTypes() {
2452 return mPreferredCachedAltDataTypes;
2453 }
2454
2455 NS_IMETHODIMP
2456 HttpChannelChild::GetAlternativeDataType(nsACString& aType) {
2457 // Must be called during or after OnStartRequest
2458 if (!LoadAfterOnStartRequestBegun()) {
2459 return NS_ERROR_NOT_AVAILABLE;
2460 }
2461
2462 aType = mAvailableCachedAltDataType;
2463 return NS_OK;
2464 }
2465
2466 NS_IMETHODIMP
2467 HttpChannelChild::OpenAlternativeOutputStream(const nsACString& aType,
2468 int64_t aPredictedSize,
2469 nsIAsyncOutputStream** _retval) {
2470 MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
2471
2472 if (!CanSend()) {
2473 return NS_ERROR_NOT_AVAILABLE;
2474 }
2475 if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
2476 return NS_ERROR_NOT_AVAILABLE;
2477 }
2478
2479 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2480 MOZ_ASSERT(neckoTarget);
2481
2482 RefPtr<AltDataOutputStreamChild> stream = new AltDataOutputStreamChild();
2483 stream->AddIPDLReference();
2484
2485 gNeckoChild->SetEventTargetForActor(stream, neckoTarget);
2486
2487 if (!gNeckoChild->SendPAltDataOutputStreamConstructor(
2488 stream, nsCString(aType), aPredictedSize, this)) {
2489 return NS_ERROR_FAILURE;
2490 }
2491
2492 stream.forget(_retval);
2493 return NS_OK;
2494 }
2495
2496 NS_IMETHODIMP
2497 HttpChannelChild::GetOriginalInputStream(nsIInputStreamReceiver* aReceiver) {
2498 if (aReceiver == nullptr) {
2499 return NS_ERROR_INVALID_ARG;
2500 }
2501
2502 if (!CanSend()) {
2503 return NS_ERROR_NOT_AVAILABLE;
2504 }
2505
2506 mOriginalInputStreamReceiver = aReceiver;
2507 Unused << SendOpenOriginalCacheInputStream();
2508
2509 return NS_OK;
2510 }
2511
2512 NS_IMETHODIMP
2513 HttpChannelChild::GetAltDataInputStream(const nsACString& aType,
2514 nsIInputStreamReceiver* aReceiver) {
2515 if (aReceiver == nullptr) {
2516 return NS_ERROR_INVALID_ARG;
2517 }
2518
2519 if (!CanSend()) {
2520 return NS_ERROR_NOT_AVAILABLE;
2521 }
2522
2523 mAltDataInputStreamReceiver = aReceiver;
2524 Unused << SendOpenAltDataCacheInputStream(nsCString(aType));
2525
2526 return NS_OK;
2527 }
2528
2529 mozilla::ipc::IPCResult HttpChannelChild::RecvOriginalCacheInputStreamAvailable(
2530 const Maybe<IPCStream>& aStream) {
2531 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
2532 nsCOMPtr<nsIInputStreamReceiver> receiver;
2533 receiver.swap(mOriginalInputStreamReceiver);
2534 if (receiver) {
2535 receiver->OnInputStreamReady(stream);
2536 }
2537
2538 return IPC_OK();
2539 }
2540
2541 mozilla::ipc::IPCResult HttpChannelChild::RecvAltDataCacheInputStreamAvailable(
2542 const Maybe<IPCStream>& aStream) {
2543 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
2544 nsCOMPtr<nsIInputStreamReceiver> receiver;
2545 receiver.swap(mAltDataInputStreamReceiver);
2546 if (receiver) {
2547 receiver->OnInputStreamReady(stream);
2548 }
2549
2550 return IPC_OK();
2551 }
2552
2553 //-----------------------------------------------------------------------------
2554 // HttpChannelChild::nsIResumableChannel
2555 //-----------------------------------------------------------------------------
2556
2557 NS_IMETHODIMP
2558 HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID) {
2559 LOG(("HttpChannelChild::ResumeAt [this=%p]\n", this));
2560 ENSURE_CALLED_BEFORE_CONNECT();
2561 mStartPos = startPos;
2562 mEntityID = entityID;
2563 mSendResumeAt = true;
2564 return NS_OK;
2565 }
2566
2567 // GetEntityID is shared in HttpBaseChannel
2568
2569 //-----------------------------------------------------------------------------
2570 // HttpChannelChild::nsISupportsPriority
2571 //-----------------------------------------------------------------------------
2572
2573 NS_IMETHODIMP
2574 HttpChannelChild::SetPriority(int32_t aPriority) {
2575 LOG(("HttpChannelChild::SetPriority %p p=%d", this, aPriority));
2576
2577 int16_t newValue = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX);
2578 if (mPriority == newValue) return NS_OK;
2579 mPriority = newValue;
2580 if (RemoteChannelExists()) SendSetPriority(mPriority);
2581 return NS_OK;
2582 }
2583
2584 //-----------------------------------------------------------------------------
2585 // HttpChannelChild::nsIClassOfService
2586 //-----------------------------------------------------------------------------
2587 NS_IMETHODIMP
2588 HttpChannelChild::SetClassFlags(uint32_t inFlags) {
2589 if (mClassOfService == inFlags) {
2590 return NS_OK;
2591 }
2592
2593 mClassOfService = inFlags;
2594
2595 LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
2596
2597 if (RemoteChannelExists()) {
2598 SendSetClassOfService(mClassOfService);
2599 }
2600 return NS_OK;
2601 }
2602
2603 NS_IMETHODIMP
2604 HttpChannelChild::AddClassFlags(uint32_t inFlags) {
2605 mClassOfService |= inFlags;
2606
2607 LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
2608
2609 if (RemoteChannelExists()) {
2610 SendSetClassOfService(mClassOfService);
2611 }
2612 return NS_OK;
2613 }
2614
2615 NS_IMETHODIMP
2616 HttpChannelChild::ClearClassFlags(uint32_t inFlags) {
2617 mClassOfService &= ~inFlags;
2618
2619 LOG(("HttpChannelChild %p ClassOfService=%u", this, mClassOfService));
2620
2621 if (RemoteChannelExists()) {
2622 SendSetClassOfService(mClassOfService);
2623 }
2624 return NS_OK;
2625 }
2626
2627 //-----------------------------------------------------------------------------
2628 // HttpChannelChild::nsIProxiedChannel
2629 //-----------------------------------------------------------------------------
2630
2631 NS_IMETHODIMP
2632 HttpChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) { DROP_DEAD(); }
2633
2634 NS_IMETHODIMP HttpChannelChild::GetHttpProxyConnectResponseCode(
2635 int32_t* aResponseCode) {
2636 DROP_DEAD();
2637 }
2638
2639 //-----------------------------------------------------------------------------
2640 // HttpChannelChild::nsIHttpChannelChild
2641 //-----------------------------------------------------------------------------
2642
2643 NS_IMETHODIMP HttpChannelChild::AddCookiesToRequest() {
2644 HttpBaseChannel::AddCookiesToRequest();
2645 return NS_OK;
2646 }
2647
2648 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(
2649 RequestHeaderTuples** aRequestHeaders) {
2650 *aRequestHeaders = &mClientSetRequestHeaders;
2651 return NS_OK;
2652 }
2653
2654 void HttpChannelChild::GetClientSetCorsPreflightParameters(
2655 Maybe<CorsPreflightArgs>& aArgs) {
2656 if (LoadRequireCORSPreflight()) {
2657 CorsPreflightArgs args;
2658 args.unsafeHeaders() = mUnsafeHeaders.Clone();
2659 aArgs.emplace(args);
2660 } else {
2661 aArgs = Nothing();
2662 }
2663 }
2664
2665 NS_IMETHODIMP
2666 HttpChannelChild::RemoveCorsPreflightCacheEntry(
2667 nsIURI* aURI, nsIPrincipal* aPrincipal,
2668 const OriginAttributes& aOriginAttributes) {
2669 URIParams uri;
2670 SerializeURI(aURI, uri);
2671 PrincipalInfo principalInfo;
2672 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
2673 if (NS_WARN_IF(NS_FAILED(rv))) {
2674 return rv;
2675 }
2676 bool result = false;
2677 // Be careful to not attempt to send a message to the parent after the
2678 // actor has been destroyed.
2679 if (CanSend()) {
2680 result = SendRemoveCorsPreflightCacheEntry(uri, principalInfo,
2681 aOriginAttributes);
2682 }
2683 return result ? NS_OK : NS_ERROR_FAILURE;
2684 }
2685
2686 //-----------------------------------------------------------------------------
2687 // HttpChannelChild::nsIMuliPartChannel
2688 //-----------------------------------------------------------------------------
2689
2690 NS_IMETHODIMP
2691 HttpChannelChild::GetBaseChannel(nsIChannel** aBaseChannel) {
2692 if (!mMultiPartID) {
2693 MOZ_ASSERT(false, "Not a multipart channel");
2694 return NS_ERROR_NOT_AVAILABLE;
2695 }
2696 nsCOMPtr<nsIChannel> channel = this;
2697 channel.forget(aBaseChannel);
2698 return NS_OK;
2699 }
2700
2701 NS_IMETHODIMP
2702 HttpChannelChild::GetPartID(uint32_t* aPartID) {
2703 if (!mMultiPartID) {
2704 MOZ_ASSERT(false, "Not a multipart channel");
2705 return NS_ERROR_NOT_AVAILABLE;
2706 }
2707 *aPartID = *mMultiPartID;
2708 return NS_OK;
2709 }
2710
2711 NS_IMETHODIMP
2712 HttpChannelChild::GetIsLastPart(bool* aIsLastPart) {
2713 if (!mMultiPartID) {
2714 return NS_ERROR_NOT_AVAILABLE;
2715 }
2716 *aIsLastPart = mIsLastPartOfMultiPart;
2717 return NS_OK;
2718 }
2719
2720 //-----------------------------------------------------------------------------
2721 // HttpChannelChild::nsIThreadRetargetableRequest
2722 //-----------------------------------------------------------------------------
2723
2724 NS_IMETHODIMP
2725 HttpChannelChild::RetargetDeliveryTo(nsIEventTarget* aNewTarget) {
2726 LOG(("HttpChannelChild::RetargetDeliveryTo [this=%p, aNewTarget=%p]", this,
2727 aNewTarget));
2728
2729 MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
2730 MOZ_ASSERT(!mODATarget);
2731 MOZ_ASSERT(aNewTarget);
2732
2733 NS_ENSURE_ARG(aNewTarget);
2734 if (aNewTarget->IsOnCurrentThread()) {
2735 NS_WARNING("Retargeting delivery to same thread");
2736 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::successMainThread;
2737 return NS_OK;
2738 }
2739
2740 if (mMultiPartID) {
2741 // TODO: Maybe add a new label for this? Maybe it doesn't
2742 // matter though, since we also blocked QI, so we shouldn't
2743 // ever get here.
2744 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
2745 return NS_ERROR_NO_INTERFACE;
2746 }
2747
2748 // Ensure that |mListener| and any subsequent listeners can be retargeted
2749 // to another thread.
2750 nsresult rv = NS_OK;
2751 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
2752 do_QueryInterface(mListener, &rv);
2753 if (!retargetableListener || NS_FAILED(rv)) {
2754 NS_WARNING("Listener is not retargetable");
2755 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
2756 return NS_ERROR_NO_INTERFACE;
2757 }
2758
2759 rv = retargetableListener->CheckListenerChain();
2760 if (NS_FAILED(rv)) {
2761 NS_WARNING("Subsequent listeners are not retargetable");
2762 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListenerChain;
2763 return rv;
2764 }
2765
2766 {
2767 MutexAutoLock lock(mEventTargetMutex);
2768 mODATarget = aNewTarget;
2769 }
2770
2771 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::success;
2772 return NS_OK;
2773 }
2774
2775 NS_IMETHODIMP
2776 HttpChannelChild::GetDeliveryTarget(nsIEventTarget** aEventTarget) {
2777 MutexAutoLock lock(mEventTargetMutex);
2778
2779 nsCOMPtr<nsIEventTarget> target = mODATarget;
2780 if (!mODATarget) {
2781 target = GetCurrentEventTarget();
2782 }
2783 target.forget(aEventTarget);
2784 return NS_OK;
2785 }
2786
2787 void HttpChannelChild::TrySendDeletingChannel() {
2788 AUTO_PROFILER_LABEL("HttpChannelChild::TrySendDeletingChannel", NETWORK);
2789 if (!mDeletingChannelSent.compareExchange(false, true)) {
2790 // SendDeletingChannel is already sent.
2791 return;
2792 }
2793
2794 if (NS_IsMainThread()) {
2795 if (NS_WARN_IF(!CanSend())) {
2796 // IPC actor is destroyed already, do not send more messages.
2797 return;
2798 }
2799
2800 Unused << PHttpChannelChild::SendDeletingChannel();
2801 return;
2802 }
2803
2804 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2805 MOZ_ASSERT(neckoTarget);
2806
2807 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
2808 NewNonOwningRunnableMethod(
2809 "net::HttpChannelChild::TrySendDeletingChannel", this,
2810 &HttpChannelChild::TrySendDeletingChannel),
2811 NS_DISPATCH_NORMAL);
2812 MOZ_ASSERT(NS_SUCCEEDED(rv));
2813 }
2814
2815 void HttpChannelChild::OnCopyComplete(nsresult aStatus) {
2816 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<nsresult>(
2817 "net::HttpBaseChannel::EnsureUploadStreamIsCloneableComplete", this,
2818 &HttpChannelChild::EnsureUploadStreamIsCloneableComplete, aStatus);
2819 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2820 MOZ_ASSERT(neckoTarget);
2821
2822 Unused << neckoTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
2823 }
2824
2825 nsresult HttpChannelChild::AsyncCallImpl(
2826 void (HttpChannelChild::*funcPtr)(),
2827 nsRunnableMethod<HttpChannelChild>** retval) {
2828 nsresult rv;
2829
2830 RefPtr<nsRunnableMethod<HttpChannelChild>> event =
2831 NewRunnableMethod("net::HttpChannelChild::AsyncCall", this, funcPtr);
2832 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2833 MOZ_ASSERT(neckoTarget);
2834
2835 rv = neckoTarget->Dispatch(event, NS_DISPATCH_NORMAL);
2836
2837 if (NS_SUCCEEDED(rv) && retval) {
2838 *retval = event;
2839 }
2840
2841 return rv;
2842 }
2843
2844 nsresult HttpChannelChild::SetReferrerHeader(const nsACString& aReferrer,
2845 bool aRespectBeforeConnect) {
2846 // Normally this would be ENSURE_CALLED_BEFORE_CONNECT, but since the
2847 // "connect" is done in the main process, and LoadRequestObserversCalled() is
2848 // never set in the ChannelChild, before connect basically means before
2849 // asyncOpen.
2850 if (aRespectBeforeConnect) {
2851 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
2852 }
2853
2854 // remove old referrer if any
2855 mClientSetRequestHeaders.RemoveElementsBy(
2856 [](const auto& header) { return "Referer"_ns.Equals(header.mHeader); });
2857
2858 return HttpBaseChannel::SetReferrerHeader(aReferrer, aRespectBeforeConnect);
2859 }
2860
2861 void HttpChannelChild::CancelOnMainThread(nsresult aRv) {
2862 LOG(("HttpChannelChild::CancelOnMainThread [this=%p]", this));
2863
2864 if (NS_IsMainThread()) {
2865 Cancel(aRv);
2866 return;
2867 }
2868
2869 mEventQ->Suspend();
2870 // Cancel is expected to preempt any other channel events, thus we put this
2871 // event in the front of mEventQ to make sure nsIStreamListener not receiving
2872 // any ODA/OnStopRequest callbacks.
2873 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
2874 this, [self = UnsafePtr<HttpChannelChild>(this), aRv]() {
2875 self->Cancel(aRv);
2876 }));
2877 mEventQ->Resume();
2878 }
2879
2880 mozilla::ipc::IPCResult HttpChannelChild::RecvIssueDeprecationWarning(
2881 const uint32_t& warning, const bool& asError) {
2882 nsCOMPtr<nsIDeprecationWarner> warner;
2883 GetCallback(warner);
2884 if (warner) {
2885 warner->IssueWarning(warning, asError);
2886 }
2887 return IPC_OK();
2888 }
2889
2890 mozilla::ipc::IPCResult HttpChannelChild::RecvSetPriority(
2891 const int16_t& aPriority) {
2892 mPriority = aPriority;
2893 return IPC_OK();
2894 }
2895
2896 // We don't have a copyable Endpoint and NeckoTargetChannelFunctionEvent takes
2897 // std::function<void()>. It's not possible to avoid the copy from the type of
2898 // lambda to std::function, so does the capture list. Hence, we're forced to
2899 // use the old-fashioned channel event inheritance.
2900 class AttachStreamFilterEvent : public ChannelEvent {
2901 public:
2902 AttachStreamFilterEvent(HttpChannelChild* aChild,
2903 already_AddRefed<nsIEventTarget> aTarget,
2904 Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
2905 : mChild(aChild), mTarget(aTarget), mEndpoint(std::move(aEndpoint)) {}
2906
2907 already_AddRefed<nsIEventTarget> GetEventTarget() override {
2908 nsCOMPtr<nsIEventTarget> target = mTarget;
2909 return target.forget();
2910 }
2911
2912 void Run() override {
2913 extensions::StreamFilterParent::Attach(mChild, std::move(mEndpoint));
2914 }
2915
2916 private:
2917 HttpChannelChild* mChild;
2918 nsCOMPtr<nsIEventTarget> mTarget;
2919 Endpoint<extensions::PStreamFilterParent> mEndpoint;
2920 };
2921
2922 void HttpChannelChild::ProcessAttachStreamFilter(
2923 Endpoint<extensions::PStreamFilterParent>&& aEndpoint) {
2924 LOG(("HttpChannelChild::ProcessAttachStreamFilter [this=%p]\n", this));
2925 MOZ_ASSERT(OnSocketThread());
2926
2927 mEventQ->RunOrEnqueue(new AttachStreamFilterEvent(this, GetNeckoTarget(),
2928 std::move(aEndpoint)));
2929 }
2930
2931 void HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
2932 MOZ_ASSERT(NS_IsMainThread());
2933
2934 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2935 mActorDestroyReason.emplace(aWhy);
2936 #endif
2937
2938 // OnStartRequest might be dropped if IPDL is destroyed abnormally
2939 // and BackgroundChild might have pending IPC messages.
2940 // Clean up BackgroundChild at this time to prevent memleak.
2941 if (aWhy != Deletion) {
2942 // Make sure all the messages are processed.
2943 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
2944
2945 mStatus = NS_ERROR_DOCSHELL_DYING;
2946 HandleAsyncAbort();
2947
2948 // Cleanup the background channel before we resume the eventQ so we don't
2949 // get any other events.
2950 CleanupBackgroundChannel();
2951
2952 mIPCActorDeleted = true;
2953 mCanceled = true;
2954 }
2955 }
2956
2957 mozilla::ipc::IPCResult HttpChannelChild::RecvLogBlockedCORSRequest(
2958 const nsString& aMessage, const nsCString& aCategory) {
2959 Unused << LogBlockedCORSRequest(aMessage, aCategory);
2960 return IPC_OK();
2961 }
2962
2963 NS_IMETHODIMP
2964 HttpChannelChild::LogBlockedCORSRequest(const nsAString& aMessage,
2965 const nsACString& aCategory) {
2966 uint64_t innerWindowID = mLoadInfo->GetInnerWindowID();
2967 bool privateBrowsing = !!mLoadInfo->GetOriginAttributes().mPrivateBrowsingId;
2968 bool fromChromeContext =
2969 mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal();
2970 nsCORSListenerProxy::LogBlockedCORSRequest(
2971 innerWindowID, privateBrowsing, fromChromeContext, aMessage, aCategory);
2972 return NS_OK;
2973 }
2974
2975 mozilla::ipc::IPCResult HttpChannelChild::RecvLogMimeTypeMismatch(
2976 const nsCString& aMessageName, const bool& aWarning, const nsString& aURL,
2977 const nsString& aContentType) {
2978 Unused << LogMimeTypeMismatch(aMessageName, aWarning, aURL, aContentType);
2979 return IPC_OK();
2980 }
2981
2982 NS_IMETHODIMP
2983 HttpChannelChild::LogMimeTypeMismatch(const nsACString& aMessageName,
2984 bool aWarning, const nsAString& aURL,
2985 const nsAString& aContentType) {
2986 RefPtr<Document> doc;
2987 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
2988
2989 AutoTArray<nsString, 2> params;
2990 params.AppendElement(aURL);
2991 params.AppendElement(aContentType);
2992 nsContentUtils::ReportToConsole(
2993 aWarning ? nsIScriptError::warningFlag : nsIScriptError::errorFlag,
2994 "MIMEMISMATCH"_ns, doc, nsContentUtils::eSECURITY_PROPERTIES,
2995 nsCString(aMessageName).get(), params);
2996 return NS_OK;
2997 }
2998
2999 nsresult HttpChannelChild::CrossProcessRedirectFinished(nsresult aStatus) {
3000 if (!CanSend()) {
3001 return NS_BINDING_FAILED;
3002 }
3003
3004 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
3005 mStatus = aStatus;
3006 }
3007
3008 return mStatus;
3009 }
3010
3011 void HttpChannelChild::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
3012 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3013 mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy = true;
3014 #endif
3015 }
3016
3017 void HttpChannelChild::MaybeConnectToSocketProcess() {
3018 if (!nsIOService::UseSocketProcess()) {
3019 return;
3020 }
3021
3022 if (!StaticPrefs::network_send_ODA_to_content_directly()) {
3023 return;
3024 }
3025
3026 RefPtr<HttpBackgroundChannelChild> bgChild = mBgChild;
3027 SocketProcessBridgeChild::GetSocketProcessBridge()->Then(
3028 GetCurrentSerialEventTarget(), __func__,
3029 [bgChild]() {
3030 gSocketTransportService->Dispatch(
3031 NewRunnableMethod("HttpBackgroundChannelChild::CreateDataBridge",
3032 bgChild,
3033 &HttpBackgroundChannelChild::CreateDataBridge),
3034 NS_DISPATCH_NORMAL);
3035 },
3036 []() { NS_WARNING("Failed to create SocketProcessBridgeChild"); });
3037 }
3038
3039 } // namespace net
3040 } // namespace mozilla
3041