Backed out 2 changesets (bug 1849864) for causing multiple failures. CLOSED TREE
[gecko.git] / netwerk / protocol / http / HttpChannelChild.cpp
blob01b3112f2c7cb9c40521a019a0c6f1cbc6dc1f65
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
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/. */
8 // HttpLog.h should generally be included first
9 #include "HttpLog.h"
11 #include "mozilla/net/PBackgroundDataBridge.h"
12 #include "nsHttp.h"
13 #include "nsICacheEntry.h"
14 #include "mozilla/BasePrincipal.h"
15 #include "mozilla/PerfStats.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/dom/ContentChild.h"
18 #include "mozilla/dom/DocGroup.h"
19 #include "mozilla/dom/ServiceWorkerUtils.h"
20 #include "mozilla/dom/BrowserChild.h"
21 #include "mozilla/dom/LinkStyle.h"
22 #include "mozilla/extensions/StreamFilterParent.h"
23 #include "mozilla/ipc/IPCStreamUtils.h"
24 #include "mozilla/net/NeckoChild.h"
25 #include "mozilla/net/HttpChannelChild.h"
26 #include "mozilla/net/UrlClassifierCommon.h"
27 #include "mozilla/net/UrlClassifierFeatureFactory.h"
29 #include "AltDataOutputStreamChild.h"
30 #include "CookieServiceChild.h"
31 #include "HttpBackgroundChannelChild.h"
32 #include "NetworkMarker.h"
33 #include "nsCOMPtr.h"
34 #include "nsContentPolicyUtils.h"
35 #include "nsDOMNavigationTiming.h"
36 #include "nsIThreadRetargetableStreamListener.h"
37 #include "nsStringStream.h"
38 #include "nsHttpChannel.h"
39 #include "nsHttpHandler.h"
40 #include "nsQueryObject.h"
41 #include "nsNetUtil.h"
42 #include "nsSerializationHelper.h"
43 #include "mozilla/Attributes.h"
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/dom/PerformanceStorage.h"
46 #include "mozilla/ipc/InputStreamUtils.h"
47 #include "mozilla/ipc/URIUtils.h"
48 #include "mozilla/ipc/BackgroundUtils.h"
49 #include "mozilla/net/DNS.h"
50 #include "mozilla/net/SocketProcessBridgeChild.h"
51 #include "mozilla/ScopeExit.h"
52 #include "mozilla/StaticPrefs_network.h"
53 #include "mozilla/StoragePrincipalHelper.h"
54 #include "SerializedLoadContext.h"
55 #include "nsInputStreamPump.h"
56 #include "nsContentSecurityManager.h"
57 #include "nsICompressConvStats.h"
58 #include "mozilla/dom/Document.h"
59 #include "nsIScriptError.h"
60 #include "nsISerialEventTarget.h"
61 #include "nsRedirectHistoryEntry.h"
62 #include "nsSocketTransportService2.h"
63 #include "nsStreamUtils.h"
64 #include "nsThreadUtils.h"
65 #include "nsCORSListenerProxy.h"
66 #include "nsIOService.h"
68 #include <functional>
70 using namespace mozilla::dom;
71 using namespace mozilla::ipc;
73 namespace mozilla::net {
75 //-----------------------------------------------------------------------------
76 // HttpChannelChild
77 //-----------------------------------------------------------------------------
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 mIsFirstPartOfMultiPart(false),
89 mIsLastPartOfMultiPart(false),
90 mSuspendForWaitCompleteRedirectSetup(false),
91 mRecvOnStartRequestSentCalled(false),
92 mSuspendedByWaitingForPermissionCookie(false) {
93 LOG(("Creating HttpChannelChild @%p\n", this));
95 mChannelCreationTime = PR_Now();
96 mChannelCreationTimestamp = TimeStamp::Now();
97 mLastStatusReported =
98 mChannelCreationTimestamp; // in case we enable the profiler after Init()
99 mAsyncOpenTime = TimeStamp::Now();
100 mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
102 // Ensure that the cookie service is initialized before the first
103 // IPC HTTP channel is created.
104 // We require that the parent cookie service actor exists while
105 // processing HTTP responses.
106 RefPtr<CookieServiceChild> cookieService = CookieServiceChild::GetSingleton();
109 HttpChannelChild::~HttpChannelChild() {
110 LOG(("Destroying HttpChannelChild @%p\n", this));
112 // See HttpChannelChild::Release, HttpChannelChild should be always destroyed
113 // on the main thread.
114 MOZ_RELEASE_ASSERT(NS_IsMainThread());
116 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
117 if (mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy && mAsyncOpenSucceeded &&
118 !mSuccesfullyRedirected && !LoadOnStopRequestCalled()) {
119 bool emptyBgChildQueue, nullBgChild;
121 MutexAutoLock lock(mBgChildMutex);
122 nullBgChild = !mBgChild;
123 emptyBgChildQueue = !nullBgChild && mBgChild->IsQueueEmpty();
126 uint32_t flags =
127 (mRedirectChannelChild ? 1 << 0 : 0) |
128 (mEventQ->IsEmpty() ? 1 << 1 : 0) | (nullBgChild ? 1 << 2 : 0) |
129 (emptyBgChildQueue ? 1 << 3 : 0) |
130 (LoadOnStartRequestCalled() ? 1 << 4 : 0) |
131 (mBackgroundChildQueueFinalState == BCKCHILD_EMPTY ? 1 << 5 : 0) |
132 (mBackgroundChildQueueFinalState == BCKCHILD_NON_EMPTY ? 1 << 6 : 0) |
133 (mRemoteChannelExistedAtCancel ? 1 << 7 : 0) |
134 (mEverHadBgChildAtAsyncOpen ? 1 << 8 : 0) |
135 (mEverHadBgChildAtConnectParent ? 1 << 9 : 0) |
136 (mCreateBackgroundChannelFailed ? 1 << 10 : 0) |
137 (mBgInitFailCallbackTriggered ? 1 << 11 : 0) |
138 (mCanSendAtCancel ? 1 << 12 : 0) | (!!mSuspendCount ? 1 << 13 : 0) |
139 (!!mCallOnResume ? 1 << 14 : 0);
140 MOZ_CRASH_UNSAFE_PRINTF(
141 "~HttpChannelChild, LoadOnStopRequestCalled()=false, mStatus=0x%08x, "
142 "mActorDestroyReason=%d, 20200717 flags=%u",
143 static_cast<uint32_t>(nsresult(mStatus)),
144 static_cast<int32_t>(mActorDestroyReason ? *mActorDestroyReason : -1),
145 flags);
147 #endif
149 mEventQ->NotifyReleasingOwner();
151 ReleaseMainThreadOnlyReferences();
154 void HttpChannelChild::ReleaseMainThreadOnlyReferences() {
155 if (NS_IsMainThread()) {
156 // Already on main thread, let dtor to
157 // take care of releasing references
158 return;
161 NS_ReleaseOnMainThread("HttpChannelChild::mRedirectChannelChild",
162 mRedirectChannelChild.forget());
164 //-----------------------------------------------------------------------------
165 // HttpChannelChild::nsISupports
166 //-----------------------------------------------------------------------------
168 NS_IMPL_ADDREF(HttpChannelChild)
170 NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
171 if (!NS_IsMainThread()) {
172 nsrefcnt count = mRefCnt;
173 nsresult rv = NS_DispatchToMainThread(NewNonOwningRunnableMethod(
174 "HttpChannelChild::Release", this, &HttpChannelChild::Release));
176 // Continue Release procedure if failed to dispatch to main thread.
177 if (!NS_WARN_IF(NS_FAILED(rv))) {
178 return count - 1;
182 nsrefcnt count = --mRefCnt;
183 MOZ_ASSERT(int32_t(count) >= 0, "dup release");
185 // Normally we Send_delete in OnStopRequest, but when we need to retain the
186 // remote channel for security info IPDL itself holds 1 reference, so we
187 // Send_delete when refCnt==1. But if !CanSend(), then there's nobody to send
188 // to, so we fall through.
189 if (mKeptAlive && count == 1 && CanSend()) {
190 NS_LOG_RELEASE(this, 1, "HttpChannelChild");
191 mKeptAlive = false;
192 // We send a message to the parent, which calls SendDelete, and then the
193 // child calling Send__delete__() to finally drop the refcount to 0.
194 TrySendDeletingChannel();
195 return 1;
198 if (count == 0) {
199 mRefCnt = 1; /* stabilize */
201 // We don't have a listener when AsyncOpen has failed or when this channel
202 // has been sucessfully redirected.
203 if (MOZ_LIKELY(LoadOnStartRequestCalled() && LoadOnStopRequestCalled()) ||
204 !mListener) {
205 NS_LOG_RELEASE(this, 0, "HttpChannelChild");
206 delete this;
207 return 0;
210 // This makes sure we fulfill the stream listener contract all the time.
211 if (NS_SUCCEEDED(mStatus)) {
212 mStatus = NS_ERROR_ABORT;
215 // Turn the stabilization refcount into a regular strong reference.
217 // 1) We tell refcount logging about the "stabilization" AddRef, which
218 // will become the reference for |channel|. We do this first so that we
219 // don't tell refcount logging that the refcount has dropped to zero, which
220 // it will interpret as destroying the object.
221 NS_LOG_ADDREF(this, 2, "HttpChannelChild", sizeof(*this));
223 // 2) We tell refcount logging about the original call to Release().
224 NS_LOG_RELEASE(this, 1, "HttpChannelChild");
226 // 3) Finally, we turn the reference into a regular smart pointer.
227 RefPtr<HttpChannelChild> channel = dont_AddRef(this);
229 // This runnable will create a strong reference to |this|.
230 NS_DispatchToMainThread(
231 NewRunnableMethod("~HttpChannelChild>DoNotifyListener", channel,
232 &HttpChannelChild::DoNotifyListener));
234 // If NS_DispatchToMainThread failed then we're going to leak the runnable,
235 // and thus the channel, so there's no need to do anything else.
237 // We should have already done any special handling for the refcount = 1
238 // case when the refcount first went from 2 to 1. We don't want it to happen
239 // when |channel| is destroyed.
240 MOZ_ASSERT(!mKeptAlive || !CanSend());
242 // XXX If std::move(channel) is allowed, then we don't have to have extra
243 // checks for the refcount going from 2 to 1. See bug 1680217.
245 // This will release the stabilization refcount, which is necessary to avoid
246 // a leak.
247 channel = nullptr;
249 return mRefCnt;
252 NS_LOG_RELEASE(this, count, "HttpChannelChild");
253 return count;
256 NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
257 NS_INTERFACE_MAP_ENTRY(nsIRequest)
258 NS_INTERFACE_MAP_ENTRY(nsIChannel)
259 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
260 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
261 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICacheInfoChannel,
262 !mMultiPartID.isSome())
263 NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
264 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
265 NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
266 NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
267 NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
268 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
269 NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
270 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
271 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiPartChannel, mMultiPartID.isSome())
272 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIThreadRetargetableRequest,
273 !mMultiPartID.isSome())
274 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelChild)
275 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
277 //-----------------------------------------------------------------------------
278 // HttpChannelChild::PHttpChannelChild
279 //-----------------------------------------------------------------------------
281 void HttpChannelChild::OnBackgroundChildReady(
282 HttpBackgroundChannelChild* aBgChild) {
283 LOG(("HttpChannelChild::OnBackgroundChildReady [this=%p, bgChild=%p]\n", this,
284 aBgChild));
285 MOZ_ASSERT(OnSocketThread());
288 MutexAutoLock lock(mBgChildMutex);
290 // mBgChild might be removed or replaced while the original background
291 // channel is inited on STS thread.
292 if (mBgChild != aBgChild) {
293 return;
296 MOZ_ASSERT(mBgInitFailCallback);
297 mBgInitFailCallback = nullptr;
301 void HttpChannelChild::OnBackgroundChildDestroyed(
302 HttpBackgroundChannelChild* aBgChild) {
303 LOG(("HttpChannelChild::OnBackgroundChildDestroyed [this=%p]\n", this));
304 // This function might be called during shutdown phase, so OnSocketThread()
305 // might return false even on STS thread. Use IsOnCurrentThreadInfallible()
306 // to get correct information.
307 MOZ_ASSERT(gSocketTransportService);
308 MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
310 nsCOMPtr<nsIRunnable> callback;
312 MutexAutoLock lock(mBgChildMutex);
314 // mBgChild might be removed or replaced while the original background
315 // channel is destroyed on STS thread.
316 if (aBgChild != mBgChild) {
317 return;
320 mBgChild = nullptr;
321 callback = std::move(mBgInitFailCallback);
324 if (callback) {
325 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
326 mBgInitFailCallbackTriggered = true;
327 #endif
328 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
329 neckoTarget->Dispatch(callback, NS_DISPATCH_NORMAL);
333 mozilla::ipc::IPCResult HttpChannelChild::RecvOnStartRequestSent() {
334 LOG(("HttpChannelChild::RecvOnStartRequestSent [this=%p]\n", this));
335 MOZ_ASSERT(NS_IsMainThread());
336 MOZ_ASSERT(!mRecvOnStartRequestSentCalled);
338 mRecvOnStartRequestSentCalled = true;
340 if (mSuspendedByWaitingForPermissionCookie) {
341 mSuspendedByWaitingForPermissionCookie = false;
342 mEventQ->Resume();
344 return IPC_OK();
347 void HttpChannelChild::ProcessOnStartRequest(
348 const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
349 const nsHttpHeaderArray& aRequestHeaders,
350 const HttpChannelOnStartRequestArgs& aArgs,
351 const HttpChannelAltDataStream& aAltData,
352 const TimeStamp& aOnStartRequestStartTime) {
353 LOG(("HttpChannelChild::ProcessOnStartRequest [this=%p]\n", this));
354 MOZ_ASSERT(OnSocketThread());
356 TimeStamp start = TimeStamp::Now();
358 mAltDataInputStream = DeserializeIPCStream(aAltData.altDataInputStream());
360 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
361 this, [self = UnsafePtr<HttpChannelChild>(this), aResponseHead,
362 aUseResponseHead, aRequestHeaders, aArgs, start]() {
363 TimeDuration delay = TimeStamp::Now() - start;
364 glean::networking::http_content_onstart_delay.AccumulateRawDuration(
365 delay);
367 self->OnStartRequest(aResponseHead, aUseResponseHead, aRequestHeaders,
368 aArgs);
369 }));
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 aTimings.transactionPending = aArgs.transactionPending();
386 void HttpChannelChild::OnStartRequest(
387 const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
388 const nsHttpHeaderArray& aRequestHeaders,
389 const HttpChannelOnStartRequestArgs& aArgs) {
390 LOG(("HttpChannelChild::OnStartRequest [this=%p]\n", this));
392 // If this channel was aborted by ActorDestroy, then there may be other
393 // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
394 // be handled. In that case we just ignore them to avoid calling the listener
395 // twice.
396 if (LoadOnStartRequestCalled() && mIPCActorDeleted) {
397 return;
400 // Copy arguments only. It's possible to handle other IPC between
401 // OnStartRequest and DoOnStartRequest.
402 mComputedCrossOriginOpenerPolicy = aArgs.openerPolicy();
404 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
405 mStatus = aArgs.channelStatus();
408 // Cookies headers should not be visible to the child process
409 MOZ_ASSERT(!aRequestHeaders.HasHeader(nsHttp::Cookie));
410 MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
412 if (aUseResponseHead && !mCanceled) {
413 mResponseHead = MakeUnique<nsHttpResponseHead>(aResponseHead);
416 mSecurityInfo = aArgs.securityInfo();
418 ipc::MergeParentLoadInfoForwarder(aArgs.loadInfoForwarder(), mLoadInfo);
420 mIsFromCache = aArgs.isFromCache();
421 mIsRacing = aArgs.isRacing();
422 mCacheEntryAvailable = aArgs.cacheEntryAvailable();
423 mCacheEntryId = aArgs.cacheEntryId();
424 mCacheFetchCount = aArgs.cacheFetchCount();
425 mProtocolVersion = aArgs.protocolVersion();
426 mCacheExpirationTime = aArgs.cacheExpirationTime();
427 mSelfAddr = aArgs.selfAddr();
428 mPeerAddr = aArgs.peerAddr();
430 mRedirectCount = aArgs.redirectCount();
431 mAvailableCachedAltDataType = aArgs.altDataType();
432 StoreDeliveringAltData(aArgs.deliveringAltData());
433 mAltDataLength = aArgs.altDataLength();
434 StoreResolvedByTRR(aArgs.isResolvedByTRR());
435 mEffectiveTRRMode = aArgs.effectiveTRRMode();
436 mTRRSkipReason = aArgs.trrSkipReason();
438 SetApplyConversion(aArgs.applyConversion());
440 StoreAfterOnStartRequestBegun(true);
441 StoreHasHTTPSRR(aArgs.hasHTTPSRR());
443 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
445 mCacheKey = aArgs.cacheKey();
447 StoreIsProxyUsed(aArgs.isProxyUsed());
449 // replace our request headers with what actually got sent in the parent
450 mRequestHead.SetHeaders(aRequestHeaders);
452 // Note: this is where we would notify "http-on-examine-response" observers.
453 // We have deliberately disabled this for child processes (see bug 806753)
455 // gHttpHandler->OnExamineResponse(this);
457 ResourceTimingStructArgsToTimingsStruct(aArgs.timing(), mTransactionTimings);
459 nsAutoCString cosString;
460 ClassOfService::ToString(mClassOfService, cosString);
461 if (!mAsyncOpenTime.IsNull() &&
462 !aArgs.timing().transactionPending().IsNull()) {
463 Telemetry::AccumulateTimeDelta(
464 Telemetry::NETWORK_ASYNC_OPEN_CHILD_TO_TRANSACTION_PENDING_EXP_MS,
465 cosString, mAsyncOpenTime, aArgs.timing().transactionPending());
466 PerfStats::RecordMeasurement(
467 PerfStats::Metric::HttpChannelAsyncOpenToTransactionPending,
468 aArgs.timing().transactionPending() - mAsyncOpenTime);
471 const TimeStamp now = TimeStamp::Now();
472 if (!aArgs.timing().responseStart().IsNull()) {
473 Telemetry::AccumulateTimeDelta(
474 Telemetry::NETWORK_RESPONSE_START_PARENT_TO_CONTENT_EXP_MS, cosString,
475 aArgs.timing().responseStart(), now);
476 PerfStats::RecordMeasurement(
477 PerfStats::Metric::HttpChannelResponseStartParentToContent,
478 now - aArgs.timing().responseStart());
480 if (!mOnStartRequestStartTime.IsNull()) {
481 PerfStats::RecordMeasurement(PerfStats::Metric::OnStartRequestToContent,
482 now - mOnStartRequestStartTime);
485 StoreAllRedirectsSameOrigin(aArgs.allRedirectsSameOrigin());
487 mMultiPartID = aArgs.multiPartID();
488 mIsFirstPartOfMultiPart = aArgs.isFirstPartOfMultiPart();
489 mIsLastPartOfMultiPart = aArgs.isLastPartOfMultiPart();
491 if (aArgs.overrideReferrerInfo()) {
492 // The arguments passed to SetReferrerInfoInternal here should mirror the
493 // arguments passed in
494 // nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown(), except for
495 // aRespectBeforeConnect which we pass false here since we're intentionally
496 // overriding the referrer after BeginConnect().
497 Unused << SetReferrerInfoInternal(aArgs.overrideReferrerInfo(), false, true,
498 false);
501 if (!aArgs.cookie().IsEmpty()) {
502 SetCookie(aArgs.cookie());
505 if (aArgs.shouldWaitForOnStartRequestSent() &&
506 !mRecvOnStartRequestSentCalled) {
507 LOG((" > pending DoOnStartRequest until RecvOnStartRequestSent\n"));
508 MOZ_ASSERT(NS_IsMainThread());
510 mEventQ->Suspend();
511 mSuspendedByWaitingForPermissionCookie = true;
512 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
513 this, [self = UnsafePtr<HttpChannelChild>(this)]() {
514 self->DoOnStartRequest(self);
515 }));
516 return;
519 // Remember whether HTTP3 is supported
520 if (mResponseHead) {
521 mSupportsHTTP3 =
522 nsHttpHandler::IsHttp3SupportedByServer(mResponseHead.get());
525 DoOnStartRequest(this);
528 void HttpChannelChild::ProcessOnAfterLastPart(const nsresult& aStatus) {
529 LOG(("HttpChannelChild::ProcessOnAfterLastPart [this=%p]\n", this));
530 MOZ_ASSERT(OnSocketThread());
531 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
532 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
533 self->OnAfterLastPart(aStatus);
534 }));
537 void HttpChannelChild::OnAfterLastPart(const nsresult& aStatus) {
538 if (LoadOnStopRequestCalled()) {
539 return;
541 StoreOnStopRequestCalled(true);
543 // notify "http-on-stop-connect" observers
544 gHttpHandler->OnStopRequest(this);
546 ReleaseListeners();
548 // If a preferred alt-data type was set, the parent would hold a reference to
549 // the cache entry in case the child calls openAlternativeOutputStream().
550 // (see nsHttpChannel::OnStopRequest)
551 if (!mPreferredCachedAltDataTypes.IsEmpty()) {
552 mAltDataCacheEntryAvailable = mCacheEntryAvailable;
554 mCacheEntryAvailable = false;
556 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
557 CleanupBackgroundChannel();
559 if (mLoadFlags & LOAD_DOCUMENT_URI) {
560 // Keep IPDL channel open, but only for updating security info.
561 // If IPDL is already closed, then do nothing.
562 if (CanSend()) {
563 mKeptAlive = true;
564 SendDocumentChannelCleanup(true);
566 } else {
567 // The parent process will respond by sending a DeleteSelf message and
568 // making sure not to send any more messages after that.
569 TrySendDeletingChannel();
573 void HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest) {
574 nsresult rv;
576 LOG(("HttpChannelChild::DoOnStartRequest [this=%p]\n", this));
578 // We handle all the listener chaining before OnStartRequest at this moment.
579 // Prevent additional listeners being added to the chain after the request
580 // as started.
581 StoreTracingEnabled(false);
583 // mListener could be null if the redirect setup is not completed.
584 MOZ_ASSERT(mListener || LoadOnStartRequestCalled());
585 if (!mListener) {
586 Cancel(NS_ERROR_FAILURE);
587 return;
590 if (mListener) {
591 nsCOMPtr<nsIStreamListener> listener(mListener);
592 StoreOnStartRequestCalled(true);
593 rv = listener->OnStartRequest(aRequest);
594 } else {
595 rv = NS_ERROR_UNEXPECTED;
597 StoreOnStartRequestCalled(true);
599 if (NS_FAILED(rv)) {
600 CancelWithReason(rv, "HttpChannelChild listener->OnStartRequest failed"_ns);
601 return;
604 nsCOMPtr<nsIStreamListener> listener;
605 rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
606 if (NS_FAILED(rv)) {
607 CancelWithReason(rv,
608 "HttpChannelChild DoApplyContentConversions failed"_ns);
609 } else if (listener) {
610 mListener = listener;
611 mCompressListener = listener;
615 void HttpChannelChild::ProcessOnTransportAndData(
616 const nsresult& aChannelStatus, const nsresult& aTransportStatus,
617 const uint64_t& aOffset, const uint32_t& aCount, const nsACString& aData,
618 const TimeStamp& aOnDataAvailableStartTime) {
619 LOG(("HttpChannelChild::ProcessOnTransportAndData [this=%p]\n", this));
620 MOZ_ASSERT(OnSocketThread());
621 mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
622 [self = UnsafePtr<HttpChannelChild>(this)]() {
623 return self->GetODATarget();
625 [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus,
626 aTransportStatus, aOffset, aCount, aData = nsCString(aData),
627 aOnDataAvailableStartTime]() {
628 self->mOnDataAvailableStartTime = aOnDataAvailableStartTime;
629 self->OnTransportAndData(aChannelStatus, aTransportStatus, aOffset,
630 aCount, aData);
631 }));
634 void HttpChannelChild::OnTransportAndData(const nsresult& aChannelStatus,
635 const nsresult& aTransportStatus,
636 const uint64_t& aOffset,
637 const uint32_t& aCount,
638 const nsACString& aData) {
639 LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
641 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
642 mStatus = aChannelStatus;
645 if (mCanceled || NS_FAILED(mStatus)) {
646 return;
649 if (!mOnDataAvailableStartTime.IsNull()) {
650 PerfStats::RecordMeasurement(PerfStats::Metric::OnDataAvailableToContent,
651 TimeStamp::Now() - mOnDataAvailableStartTime);
654 // Hold queue lock throughout all three calls, else we might process a later
655 // necko msg in between them.
656 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
658 int64_t progressMax;
659 if (NS_FAILED(GetContentLength(&progressMax))) {
660 progressMax = -1;
663 const int64_t progress = aOffset + aCount;
665 // OnTransportAndData will be run on retargeted thread if applicable, however
666 // OnStatus/OnProgress event can only be fired on main thread. We need to
667 // dispatch the status/progress event handling back to main thread with the
668 // appropriate event target for networking.
669 if (NS_IsMainThread()) {
670 DoOnStatus(this, aTransportStatus);
671 DoOnProgress(this, progress, progressMax);
672 } else {
673 RefPtr<HttpChannelChild> self = this;
674 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
675 MOZ_ASSERT(neckoTarget);
677 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
678 NS_NewRunnableFunction(
679 "net::HttpChannelChild::OnTransportAndData",
680 [self, aTransportStatus, progress, progressMax]() {
681 self->DoOnStatus(self, aTransportStatus);
682 self->DoOnProgress(self, progress, progressMax);
684 NS_DISPATCH_NORMAL);
685 MOZ_ASSERT(NS_SUCCEEDED(rv));
688 // OnDataAvailable
690 // NOTE: the OnDataAvailable contract requires the client to read all the data
691 // in the inputstream. This code relies on that ('data' will go away after
692 // this function). Apparently the previous, non-e10s behavior was to actually
693 // support only reading part of the data, allowing later calls to read the
694 // rest.
695 nsCOMPtr<nsIInputStream> stringStream;
696 nsresult rv =
697 NS_NewByteInputStream(getter_AddRefs(stringStream),
698 Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
699 if (NS_FAILED(rv)) {
700 CancelWithReason(rv, "HttpChannelChild NS_NewByteInputStream failed"_ns);
701 return;
704 DoOnDataAvailable(this, stringStream, aOffset, aCount);
705 stringStream->Close();
707 // TODO: Bug 1523916 backpressure needs to take into account if the data is
708 // coming from the main process or from the socket process via PBackground.
709 if (NeedToReportBytesRead()) {
710 mUnreportBytesRead += aCount;
711 if (mUnreportBytesRead >= gHttpHandler->SendWindowSize() >> 2) {
712 if (NS_IsMainThread()) {
713 Unused << SendBytesRead(mUnreportBytesRead);
714 } else {
715 // PHttpChannel connects to the main thread
716 RefPtr<HttpChannelChild> self = this;
717 int32_t bytesRead = mUnreportBytesRead;
718 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
719 MOZ_ASSERT(neckoTarget);
721 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
722 NS_NewRunnableFunction("net::HttpChannelChild::SendBytesRead",
723 [self, bytesRead]() {
724 Unused << self->SendBytesRead(bytesRead);
726 NS_DISPATCH_NORMAL);
727 MOZ_ASSERT(NS_SUCCEEDED(rv));
729 mUnreportBytesRead = 0;
734 bool HttpChannelChild::NeedToReportBytesRead() {
735 if (mCacheNeedToReportBytesReadInitialized) {
736 return mNeedToReportBytesRead;
739 // Might notify parent for partial cache, and the IPC message is ignored by
740 // parent.
741 int64_t contentLength = -1;
742 if (gHttpHandler->SendWindowSize() == 0 || mIsFromCache ||
743 NS_FAILED(GetContentLength(&contentLength)) ||
744 contentLength < gHttpHandler->SendWindowSize()) {
745 mNeedToReportBytesRead = false;
748 mCacheNeedToReportBytesReadInitialized = true;
749 return mNeedToReportBytesRead;
752 void HttpChannelChild::DoOnStatus(nsIRequest* aRequest, nsresult status) {
753 LOG(("HttpChannelChild::DoOnStatus [this=%p]\n", this));
754 MOZ_ASSERT(NS_IsMainThread());
756 if (mCanceled) return;
758 // cache the progress sink so we don't have to query for it each time.
759 if (!mProgressSink) GetCallback(mProgressSink);
761 // block status/progress after Cancel or OnStopRequest has been called,
762 // or if channel has LOAD_BACKGROUND set.
763 if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending() &&
764 !(mLoadFlags & LOAD_BACKGROUND)) {
765 nsAutoCString host;
766 mURI->GetHost(host);
767 mProgressSink->OnStatus(aRequest, status,
768 NS_ConvertUTF8toUTF16(host).get());
772 void HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress,
773 int64_t progressMax) {
774 LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
775 MOZ_ASSERT(NS_IsMainThread());
777 if (mCanceled) return;
779 // cache the progress sink so we don't have to query for it each time.
780 if (!mProgressSink) GetCallback(mProgressSink);
782 // block status/progress after Cancel or OnStopRequest has been called,
783 // or if channel has LOAD_BACKGROUND set.
784 if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending()) {
785 // OnProgress
787 if (progress > 0) {
788 mProgressSink->OnProgress(aRequest, progress, progressMax);
792 // mOnProgressEventSent indicates we have flushed all the
793 // progress events on the main thread. It is needed if
794 // we do not want to dispatch OnDataFinished before sending
795 // all of the progress updates.
796 if (progress == progressMax) {
797 mOnProgressEventSent = true;
801 void HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest,
802 nsIInputStream* aStream,
803 uint64_t aOffset, uint32_t aCount) {
804 AUTO_PROFILER_LABEL("HttpChannelChild::DoOnDataAvailable", NETWORK);
805 LOG(("HttpChannelChild::DoOnDataAvailable [this=%p]\n", this));
806 if (mCanceled) return;
808 if (mListener) {
809 nsCOMPtr<nsIStreamListener> listener(mListener);
810 nsresult rv = listener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
811 if (NS_FAILED(rv)) {
812 CancelOnMainThread(rv, "HttpChannelChild OnDataAvailable failed"_ns);
817 void HttpChannelChild::SendOnDataFinished(const nsresult& aChannelStatus) {
818 LOG(("HttpChannelChild::SendOnDataFinished [this=%p]\n", this));
819 if (MOZ_UNLIKELY(NS_IsMainThread())) {
820 MOZ_ASSERT(false, "SendOnDataFinished should not be called on main thread");
821 return;
824 if (mCanceled) return;
826 // we need to ensure we OnDataFinished only after all the progress
827 // updates are dispatched on the main thread
828 if (StaticPrefs::network_send_OnDataFinished_after_progress_updates() &&
829 !mOnProgressEventSent) {
830 return;
833 if (mListener) {
834 nsCOMPtr<nsIThreadRetargetableStreamListener> omtEventListener =
835 do_QueryInterface(mListener);
836 if (omtEventListener) {
837 LOG(
838 ("HttpChannelChild::SendOnDataFinished sending data end "
839 "notification[this=%p]\n",
840 this));
841 // We want to calculate the delta time between this call and
842 // ProcessOnStopRequest. Complicating things is that OnStopRequest
843 // could come first, and that it will run on a different thread, so
844 // we need to synchronize and lock data.
845 omtEventListener->OnDataFinished(aChannelStatus);
846 } else {
847 LOG(
848 ("HttpChannelChild::SendOnDataFinished missing "
849 "nsIThreadRetargetableStreamListener "
850 "implementation [this=%p]\n",
851 this));
856 class RecordStopRequestDelta final {
857 public:
858 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecordStopRequestDelta);
860 TimeStamp mOnStopRequestTime;
861 TimeStamp mOnDataFinishedTime;
863 private:
864 ~RecordStopRequestDelta() {
865 if (mOnDataFinishedTime.IsNull() || mOnStopRequestTime.IsNull()) {
866 return;
869 TimeDuration delta = (mOnStopRequestTime - mOnDataFinishedTime);
870 if (delta.ToMilliseconds() < 0) {
871 // Because Telemetry can't handle negatives
872 delta = -delta;
873 glean::networking::http_content_ondatafinished_to_onstop_delay_negative
874 .AccumulateRawDuration(delta);
875 } else {
876 glean::networking::http_content_ondatafinished_to_onstop_delay
877 .AccumulateRawDuration(delta);
882 void HttpChannelChild::ProcessOnStopRequest(
883 const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
884 const nsHttpHeaderArray& aResponseTrailers,
885 nsTArray<ConsoleReportCollected>&& aConsoleReports, bool aFromSocketProcess,
886 const TimeStamp& aOnStopRequestStartTime) {
887 LOG(
888 ("HttpChannelChild::ProcessOnStopRequest [this=%p, "
889 "aFromSocketProcess=%d]\n",
890 this, aFromSocketProcess));
891 MOZ_ASSERT(OnSocketThread());
892 { // assign some of the members that would be accessed by the listeners
893 // upon getting OnDataFinished notications
894 MutexAutoLock lock(mOnDataFinishedMutex);
895 mTransferSize = aTiming.transferSize();
896 mEncodedBodySize = aTiming.encodedBodySize();
899 RefPtr<RecordStopRequestDelta> timing;
900 TimeStamp start = TimeStamp::Now();
901 if (StaticPrefs::network_send_OnDataFinished() &&
902 mOMTResult == LABELS_HTTP_CHILD_OMT_STATS::success) {
903 timing = new RecordStopRequestDelta;
904 mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
905 [self = UnsafePtr<HttpChannelChild>(this)]() {
906 return self->GetODATarget();
908 [self = UnsafePtr<HttpChannelChild>(this), status = aChannelStatus,
909 start, timing]() {
910 TimeStamp now = TimeStamp::Now();
911 TimeDuration delay = now - start;
912 glean::networking::http_content_ondatafinished_delay
913 .AccumulateRawDuration(delay);
914 timing->mOnDataFinishedTime = now;
915 self->SendOnDataFinished(status);
916 }));
918 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
919 this, [self = UnsafePtr<HttpChannelChild>(this), aChannelStatus, aTiming,
920 aResponseTrailers,
921 consoleReports = CopyableTArray{aConsoleReports.Clone()},
922 aFromSocketProcess, start, timing]() mutable {
923 TimeStamp now = TimeStamp::Now();
924 TimeDuration delay = now - start;
925 glean::networking::http_content_onstop_delay.AccumulateRawDuration(
926 delay);
927 if (timing) {
928 timing->mOnStopRequestTime = now;
930 self->OnStopRequest(aChannelStatus, aTiming, aResponseTrailers);
931 if (!aFromSocketProcess) {
932 self->DoOnConsoleReport(std::move(consoleReports));
933 self->ContinueOnStopRequest();
935 }));
938 void HttpChannelChild::ProcessOnConsoleReport(
939 nsTArray<ConsoleReportCollected>&& aConsoleReports) {
940 LOG(("HttpChannelChild::ProcessOnConsoleReport [this=%p]\n", this));
941 MOZ_ASSERT(OnSocketThread());
943 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
944 this,
945 [self = UnsafePtr<HttpChannelChild>(this),
946 consoleReports = CopyableTArray{aConsoleReports.Clone()}]() mutable {
947 self->DoOnConsoleReport(std::move(consoleReports));
948 self->ContinueOnStopRequest();
949 }));
952 void HttpChannelChild::DoOnConsoleReport(
953 nsTArray<ConsoleReportCollected>&& aConsoleReports) {
954 if (aConsoleReports.IsEmpty()) {
955 return;
958 for (ConsoleReportCollected& report : aConsoleReports) {
959 if (report.propertiesFile() <
960 nsContentUtils::PropertiesFile::PropertiesFile_COUNT) {
961 AddConsoleReport(report.errorFlags(), report.category(),
962 nsContentUtils::PropertiesFile(report.propertiesFile()),
963 report.sourceFileURI(), report.lineNumber(),
964 report.columnNumber(), report.messageName(),
965 report.stringParams());
968 MaybeFlushConsoleReports();
971 void HttpChannelChild::RecordChannelCompletionDurationForEarlyHint() {
972 if (!mLoadGroup) {
973 return;
976 uint32_t earlyHintType = 0;
977 nsCOMPtr<nsIRequest> req;
978 Unused << mLoadGroup->GetDefaultLoadRequest(getter_AddRefs(req));
979 if (nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(req)) {
980 Unused << httpChannel->GetEarlyHintLinkType(&earlyHintType);
983 if (!earlyHintType) {
984 return;
987 nsAutoCString earlyHintKey;
988 if (mIsFromCache) {
989 earlyHintKey.Append("cache_"_ns);
990 } else {
991 earlyHintKey.Append("net_"_ns);
993 if (earlyHintType & LinkStyle::ePRECONNECT) {
994 earlyHintKey.Append("preconnect_"_ns);
996 if (earlyHintType & LinkStyle::ePRELOAD) {
997 earlyHintKey.Append("preload_"_ns);
998 earlyHintKey.Append(mEarlyHintPreloaderId ? "1"_ns : "0"_ns);
1001 Telemetry::AccumulateTimeDelta(Telemetry::EH_PERF_CHANNEL_COMPLETION_TIME,
1002 earlyHintKey, mAsyncOpenTime,
1003 TimeStamp::Now());
1006 void HttpChannelChild::OnStopRequest(
1007 const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
1008 const nsHttpHeaderArray& aResponseTrailers) {
1009 LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
1010 static_cast<uint32_t>(aChannelStatus)));
1011 MOZ_ASSERT(NS_IsMainThread());
1013 // If this channel was aborted by ActorDestroy, then there may be other
1014 // OnStartRequest/OnStopRequest/OnDataAvailable IPC messages that need to
1015 // be handled. In that case we just ignore them to avoid calling the listener
1016 // twice.
1017 if (LoadOnStopRequestCalled() && mIPCActorDeleted) {
1018 return;
1021 nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
1022 if (conv) {
1023 conv->GetDecodedDataLength(&mDecodedBodySize);
1026 ResourceTimingStructArgsToTimingsStruct(aTiming, mTransactionTimings);
1028 // Do not overwrite or adjust the original mAsyncOpenTime by timing.fetchStart
1029 // We must use the original child process time in order to account for child
1030 // side work and IPC transit overhead.
1031 // XXX: This depends on TimeStamp being equivalent across processes.
1032 // This is true for modern hardware but for older platforms it is not always
1033 // true.
1035 mRedirectStartTimeStamp = aTiming.redirectStart();
1036 mRedirectEndTimeStamp = aTiming.redirectEnd();
1037 // mTransferSize and mEncodedBodySize are set in ProcessOnStopRequest
1038 // TODO: check if we need to move assignments of other members to
1039 // ProcessOnStopRequest
1041 mCacheReadStart = aTiming.cacheReadStart();
1042 mCacheReadEnd = aTiming.cacheReadEnd();
1044 const TimeStamp now = TimeStamp::Now();
1046 if (profiler_thread_is_being_profiled_for_markers()) {
1047 nsAutoCString requestMethod;
1048 GetRequestMethod(requestMethod);
1049 nsAutoCString contentType;
1050 if (mResponseHead) {
1051 mResponseHead->ContentType(contentType);
1053 int32_t priority = PRIORITY_NORMAL;
1054 GetPriority(&priority);
1055 profiler_add_network_marker(
1056 mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP,
1057 mLastStatusReported, now, mTransferSize, kCacheUnknown,
1058 mLoadInfo->GetInnerWindowID(),
1059 mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0,
1060 &mTransactionTimings, std::move(mSource),
1061 Some(nsDependentCString(contentType.get())));
1064 RecordChannelCompletionDurationForEarlyHint();
1066 TimeDuration channelCompletionDuration = now - mAsyncOpenTime;
1067 if (mIsFromCache) {
1068 PerfStats::RecordMeasurement(PerfStats::Metric::HttpChannelCompletion_Cache,
1069 channelCompletionDuration);
1070 } else {
1071 PerfStats::RecordMeasurement(
1072 PerfStats::Metric::HttpChannelCompletion_Network,
1073 channelCompletionDuration);
1075 PerfStats::RecordMeasurement(PerfStats::Metric::HttpChannelCompletion,
1076 channelCompletionDuration);
1078 if (!aTiming.responseEnd().IsNull()) {
1079 nsAutoCString cosString;
1080 ClassOfService::ToString(mClassOfService, cosString);
1081 Telemetry::AccumulateTimeDelta(
1082 Telemetry::NETWORK_RESPONSE_END_PARENT_TO_CONTENT_MS, cosString,
1083 aTiming.responseEnd(), now);
1084 PerfStats::RecordMeasurement(
1085 PerfStats::Metric::HttpChannelResponseEndParentToContent,
1086 now - aTiming.responseEnd());
1089 if (!mOnStopRequestStartTime.IsNull()) {
1090 PerfStats::RecordMeasurement(PerfStats::Metric::OnStopRequestToContent,
1091 now - mOnStopRequestStartTime);
1094 mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers);
1096 DoPreOnStopRequest(aChannelStatus);
1098 { // We must flush the queue before we Send__delete__
1099 // (although we really shouldn't receive any msgs after OnStop),
1100 // so make sure this goes out of scope before then.
1101 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
1103 DoOnStopRequest(this, aChannelStatus);
1104 // DoOnStopRequest() calls ReleaseListeners()
1108 void HttpChannelChild::ContinueOnStopRequest() {
1109 // If we're a multi-part stream, then don't cleanup yet, and we'll do so
1110 // in OnAfterLastPart.
1111 if (mMultiPartID) {
1112 LOG(
1113 ("HttpChannelChild::OnStopRequest - Expecting future parts on a "
1114 "multipart channel postpone cleaning up."));
1115 return;
1118 CollectMixedContentTelemetry();
1120 CleanupBackgroundChannel();
1122 // If there is a possibility we might want to write alt data to the cache
1123 // entry, we keep the channel alive. We still send the DocumentChannelCleanup
1124 // message but request the cache entry to be kept by the parent.
1125 // If the channel has failed, the cache entry is in a non-writtable state and
1126 // we want to release it to not block following consumers.
1127 if (NS_SUCCEEDED(mStatus) && !mPreferredCachedAltDataTypes.IsEmpty()) {
1128 mKeptAlive = true;
1129 SendDocumentChannelCleanup(false); // don't clear cache entry
1130 return;
1133 if (mLoadFlags & LOAD_DOCUMENT_URI) {
1134 // Keep IPDL channel open, but only for updating security info.
1135 // If IPDL is already closed, then do nothing.
1136 if (CanSend()) {
1137 mKeptAlive = true;
1138 SendDocumentChannelCleanup(true);
1140 } else {
1141 // The parent process will respond by sending a DeleteSelf message and
1142 // making sure not to send any more messages after that.
1143 TrySendDeletingChannel();
1147 void HttpChannelChild::DoPreOnStopRequest(nsresult aStatus) {
1148 AUTO_PROFILER_LABEL("HttpChannelChild::DoPreOnStopRequest", NETWORK);
1149 LOG(("HttpChannelChild::DoPreOnStopRequest [this=%p status=%" PRIx32 "]\n",
1150 this, static_cast<uint32_t>(aStatus)));
1151 StoreIsPending(false);
1153 MaybeReportTimingData();
1155 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
1156 mStatus = aStatus;
1159 CollectOMTTelemetry();
1162 void HttpChannelChild::CollectOMTTelemetry() {
1163 MOZ_ASSERT(NS_IsMainThread());
1165 // Only collect telemetry for HTTP channel that is loaded successfully and
1166 // completely.
1167 if (mCanceled || NS_FAILED(mStatus)) {
1168 return;
1171 // Use content policy type to accumulate data by usage.
1172 nsAutoCString key(
1173 NS_CP_ContentTypeName(mLoadInfo->InternalContentPolicyType()));
1175 Telemetry::AccumulateCategoricalKeyed(
1176 key, static_cast<LABELS_HTTP_CHILD_OMT_STATS>(mOMTResult));
1179 void HttpChannelChild::CollectMixedContentTelemetry() {
1180 MOZ_ASSERT(NS_IsMainThread());
1182 nsContentPolicyType internalLoadType;
1183 mLoadInfo->GetInternalContentPolicyType(&internalLoadType);
1184 bool statusIsSuccess = NS_SUCCEEDED(mStatus);
1185 RefPtr<Document> doc;
1186 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1187 if (!doc) {
1188 return;
1190 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE ||
1191 internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD) {
1192 if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
1193 doc->SetUseCounter(
1194 statusIsSuccess
1195 ? eUseCounter_custom_MixedContentUpgradedImageSuccess
1196 : eUseCounter_custom_MixedContentUpgradedImageFailure);
1197 } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
1198 doc->SetUseCounter(
1199 statusIsSuccess
1200 ? eUseCounter_custom_MixedContentNotUpgradedImageSuccess
1201 : eUseCounter_custom_MixedContentNotUpgradedImageFailure);
1203 return;
1205 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_VIDEO) {
1206 if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
1207 doc->SetUseCounter(
1208 statusIsSuccess
1209 ? eUseCounter_custom_MixedContentUpgradedVideoSuccess
1210 : eUseCounter_custom_MixedContentUpgradedVideoFailure);
1211 } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
1212 doc->SetUseCounter(
1213 statusIsSuccess
1214 ? eUseCounter_custom_MixedContentNotUpgradedVideoSuccess
1215 : eUseCounter_custom_MixedContentNotUpgradedVideoFailure);
1217 return;
1219 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_AUDIO) {
1220 if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
1221 doc->SetUseCounter(
1222 statusIsSuccess
1223 ? eUseCounter_custom_MixedContentUpgradedAudioSuccess
1224 : eUseCounter_custom_MixedContentUpgradedAudioFailure);
1225 } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
1226 doc->SetUseCounter(
1227 statusIsSuccess
1228 ? eUseCounter_custom_MixedContentNotUpgradedAudioSuccess
1229 : eUseCounter_custom_MixedContentNotUpgradedAudioFailure);
1234 void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
1235 nsresult aChannelStatus) {
1236 AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
1237 LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
1238 MOZ_ASSERT(NS_IsMainThread());
1239 MOZ_ASSERT(!LoadIsPending());
1241 auto checkForBlockedContent = [&]() {
1242 // NB: We use aChannelStatus here instead of mStatus because if there was an
1243 // nsCORSListenerProxy on this request, it will override the tracking
1244 // protection's return value.
1245 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
1246 aChannelStatus) ||
1247 aChannelStatus == NS_ERROR_MALWARE_URI ||
1248 aChannelStatus == NS_ERROR_UNWANTED_URI ||
1249 aChannelStatus == NS_ERROR_BLOCKED_URI ||
1250 aChannelStatus == NS_ERROR_HARMFUL_URI ||
1251 aChannelStatus == NS_ERROR_PHISHING_URI) {
1252 nsCString list, provider, fullhash;
1254 nsresult rv = GetMatchedList(list);
1255 NS_ENSURE_SUCCESS_VOID(rv);
1257 rv = GetMatchedProvider(provider);
1258 NS_ENSURE_SUCCESS_VOID(rv);
1260 rv = GetMatchedFullHash(fullhash);
1261 NS_ENSURE_SUCCESS_VOID(rv);
1263 UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
1264 provider, fullhash);
1267 checkForBlockedContent();
1269 MaybeLogCOEPError(aChannelStatus);
1271 // See bug 1587686. If the redirect setup is not completed, the post-redirect
1272 // channel will be not opened and mListener will be null.
1273 MOZ_ASSERT(mListener || !LoadWasOpened());
1274 if (!mListener) {
1275 return;
1278 MOZ_ASSERT(!LoadOnStopRequestCalled(),
1279 "We should not call OnStopRequest twice");
1281 if (mListener) {
1282 nsCOMPtr<nsIStreamListener> listener(mListener);
1283 StoreOnStopRequestCalled(true);
1284 listener->OnStopRequest(aRequest, mStatus);
1286 StoreOnStopRequestCalled(true);
1288 // If we're a multi-part stream, then don't cleanup yet, and we'll do so
1289 // in OnAfterLastPart.
1290 if (mMultiPartID) {
1291 LOG(
1292 ("HttpChannelChild::DoOnStopRequest - Expecting future parts on a "
1293 "multipart channel not releasing listeners."));
1294 StoreOnStopRequestCalled(false);
1295 StoreOnStartRequestCalled(false);
1296 return;
1299 // notify "http-on-stop-connect" observers
1300 gHttpHandler->OnStopRequest(this);
1302 ReleaseListeners();
1304 // If a preferred alt-data type was set, the parent would hold a reference to
1305 // the cache entry in case the child calls openAlternativeOutputStream().
1306 // (see nsHttpChannel::OnStopRequest)
1307 if (!mPreferredCachedAltDataTypes.IsEmpty()) {
1308 mAltDataCacheEntryAvailable = mCacheEntryAvailable;
1310 mCacheEntryAvailable = false;
1312 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
1315 void HttpChannelChild::ProcessOnProgress(const int64_t& aProgress,
1316 const int64_t& aProgressMax) {
1317 MOZ_ASSERT(OnSocketThread());
1318 LOG(("HttpChannelChild::ProcessOnProgress [this=%p]\n", this));
1319 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1320 this,
1321 [self = UnsafePtr<HttpChannelChild>(this), aProgress, aProgressMax]() {
1322 AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1323 self->DoOnProgress(self, aProgress, aProgressMax);
1324 }));
1327 void HttpChannelChild::ProcessOnStatus(const nsresult& aStatus) {
1328 MOZ_ASSERT(OnSocketThread());
1329 LOG(("HttpChannelChild::ProcessOnStatus [this=%p]\n", this));
1330 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1331 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1332 AutoEventEnqueuer ensureSerialDispatch(self->mEventQ);
1333 self->DoOnStatus(self, aStatus);
1334 }));
1337 mozilla::ipc::IPCResult HttpChannelChild::RecvFailedAsyncOpen(
1338 const nsresult& aStatus) {
1339 LOG(("HttpChannelChild::RecvFailedAsyncOpen [this=%p]\n", this));
1340 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1341 this, [self = UnsafePtr<HttpChannelChild>(this), aStatus]() {
1342 self->FailedAsyncOpen(aStatus);
1343 }));
1344 return IPC_OK();
1347 // We need to have an implementation of this function just so that we can keep
1348 // all references to mCallOnResume of type HttpChannelChild: it's not OK in C++
1349 // to set a member function ptr to a base class function.
1350 void HttpChannelChild::HandleAsyncAbort() {
1351 HttpAsyncAborter<HttpChannelChild>::HandleAsyncAbort();
1353 // Ignore all the messages from background channel after channel aborted.
1354 CleanupBackgroundChannel();
1357 void HttpChannelChild::FailedAsyncOpen(const nsresult& status) {
1358 LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%" PRIx32 "]\n", this,
1359 static_cast<uint32_t>(status)));
1360 MOZ_ASSERT(NS_IsMainThread());
1362 // Might be called twice in race condition in theory.
1363 // (one by RecvFailedAsyncOpen, another by
1364 // HttpBackgroundChannelChild::ActorFailed)
1365 if (LoadOnStartRequestCalled()) {
1366 return;
1369 if (NS_SUCCEEDED(mStatus)) {
1370 mStatus = status;
1373 // We're already being called from IPDL, therefore already "async"
1374 HandleAsyncAbort();
1376 if (CanSend()) {
1377 TrySendDeletingChannel();
1381 void HttpChannelChild::CleanupBackgroundChannel() {
1382 MutexAutoLock lock(mBgChildMutex);
1384 AUTO_PROFILER_LABEL("HttpChannelChild::CleanupBackgroundChannel", NETWORK);
1385 LOG(("HttpChannelChild::CleanupBackgroundChannel [this=%p bgChild=%p]\n",
1386 this, mBgChild.get()));
1388 mBgInitFailCallback = nullptr;
1390 if (!mBgChild) {
1391 return;
1394 RefPtr<HttpBackgroundChannelChild> bgChild = std::move(mBgChild);
1396 MOZ_RELEASE_ASSERT(gSocketTransportService);
1397 if (!OnSocketThread()) {
1398 gSocketTransportService->Dispatch(
1399 NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
1400 bgChild,
1401 &HttpBackgroundChannelChild::OnChannelClosed),
1402 NS_DISPATCH_NORMAL);
1403 } else {
1404 bgChild->OnChannelClosed();
1408 void HttpChannelChild::DoNotifyListenerCleanup() {
1409 LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
1412 void HttpChannelChild::DoAsyncAbort(nsresult aStatus) {
1413 Unused << AsyncAbort(aStatus);
1416 mozilla::ipc::IPCResult HttpChannelChild::RecvDeleteSelf() {
1417 LOG(("HttpChannelChild::RecvDeleteSelf [this=%p]\n", this));
1418 MOZ_ASSERT(NS_IsMainThread());
1420 // The redirection is vetoed. No need to suspend the event queue.
1421 if (mSuspendForWaitCompleteRedirectSetup) {
1422 mSuspendForWaitCompleteRedirectSetup = false;
1423 mEventQ->Resume();
1426 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1427 this,
1428 [self = UnsafePtr<HttpChannelChild>(this)]() { self->DeleteSelf(); }));
1429 return IPC_OK();
1432 void HttpChannelChild::DeleteSelf() { Send__delete__(this); }
1434 void HttpChannelChild::NotifyOrReleaseListeners(nsresult rv) {
1435 MOZ_ASSERT(NS_IsMainThread());
1437 if (NS_SUCCEEDED(rv) ||
1438 (LoadOnStartRequestCalled() && LoadOnStopRequestCalled())) {
1439 ReleaseListeners();
1440 return;
1443 if (NS_SUCCEEDED(mStatus)) {
1444 mStatus = rv;
1447 // This is enough what we need. Undelivered notifications will be pushed.
1448 // DoNotifyListener ensures the call to ReleaseListeners when done.
1449 DoNotifyListener();
1452 void HttpChannelChild::DoNotifyListener() {
1453 LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
1454 MOZ_ASSERT(NS_IsMainThread());
1456 // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag
1457 // LOAD_ONLY_IF_MODIFIED) we want to set LoadAfterOnStartRequestBegun() to
1458 // true before notifying listener.
1459 if (!LoadAfterOnStartRequestBegun()) {
1460 StoreAfterOnStartRequestBegun(true);
1463 if (mListener && !LoadOnStartRequestCalled()) {
1464 nsCOMPtr<nsIStreamListener> listener = mListener;
1465 StoreOnStartRequestCalled(
1466 true); // avoid reentrancy bugs by setting this now
1467 listener->OnStartRequest(this);
1469 StoreOnStartRequestCalled(true);
1471 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1472 this, [self = UnsafePtr<HttpChannelChild>(this)] {
1473 self->ContinueDoNotifyListener();
1474 }));
1477 void HttpChannelChild::ContinueDoNotifyListener() {
1478 LOG(("HttpChannelChild::ContinueDoNotifyListener this=%p", this));
1479 MOZ_ASSERT(NS_IsMainThread());
1481 // Make sure IsPending is set to false. At this moment we are done from
1482 // the point of view of our consumer and we have to report our self
1483 // as not-pending.
1484 StoreIsPending(false);
1486 if (mListener && !LoadOnStopRequestCalled()) {
1487 nsCOMPtr<nsIStreamListener> listener = mListener;
1488 StoreOnStopRequestCalled(true);
1489 listener->OnStopRequest(this, mStatus);
1491 StoreOnStopRequestCalled(true);
1493 // notify "http-on-stop-request" observers
1494 gHttpHandler->OnStopRequest(this);
1496 // This channel has finished its job, potentially release any tail-blocked
1497 // requests with this.
1498 RemoveAsNonTailRequest();
1500 // We have to make sure to drop the references to listeners and callbacks
1501 // no longer needed.
1502 ReleaseListeners();
1504 DoNotifyListenerCleanup();
1506 // If this is a navigation, then we must let the docshell flush the reports
1507 // to the console later. The LoadDocument() is pointing at the detached
1508 // document that started the navigation. We want to show the reports on the
1509 // new document. Otherwise the console is wiped and the user never sees
1510 // the information.
1511 if (!IsNavigation()) {
1512 if (mLoadGroup) {
1513 FlushConsoleReports(mLoadGroup);
1514 } else {
1515 RefPtr<dom::Document> doc;
1516 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
1517 FlushConsoleReports(doc);
1522 mozilla::ipc::IPCResult HttpChannelChild::RecvReportSecurityMessage(
1523 const nsAString& messageTag, const nsAString& messageCategory) {
1524 DebugOnly<nsresult> rv = AddSecurityMessage(messageTag, messageCategory);
1525 MOZ_ASSERT(NS_SUCCEEDED(rv));
1526 return IPC_OK();
1529 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect1Begin(
1530 const uint32_t& aRegistrarId, nsIURI* aNewUri,
1531 const uint32_t& aNewLoadFlags, const uint32_t& aRedirectFlags,
1532 const ParentLoadInfoForwarderArgs& aLoadInfoForwarder,
1533 const nsHttpResponseHead& aResponseHead,
1534 nsITransportSecurityInfo* aSecurityInfo, const uint64_t& aChannelId,
1535 const NetAddr& aOldPeerAddr, const ResourceTimingStructArgs& aTiming) {
1536 // TODO: handle security info
1537 LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this));
1538 // We set peer address of child to the old peer,
1539 // Then it will be updated to new peer in OnStartRequest
1540 mPeerAddr = aOldPeerAddr;
1542 // Cookies headers should not be visible to the child process
1543 MOZ_ASSERT(!nsHttpResponseHead(aResponseHead).HasHeader(nsHttp::Set_Cookie));
1545 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1546 this, [self = UnsafePtr<HttpChannelChild>(this), aRegistrarId,
1547 newUri = RefPtr{aNewUri}, aNewLoadFlags, aRedirectFlags,
1548 aLoadInfoForwarder, aResponseHead,
1549 aSecurityInfo = nsCOMPtr{aSecurityInfo}, aChannelId, aTiming]() {
1550 self->Redirect1Begin(aRegistrarId, newUri, aNewLoadFlags,
1551 aRedirectFlags, aLoadInfoForwarder, aResponseHead,
1552 aSecurityInfo, aChannelId, aTiming);
1553 }));
1554 return IPC_OK();
1557 nsresult HttpChannelChild::SetupRedirect(nsIURI* uri,
1558 const nsHttpResponseHead* responseHead,
1559 const uint32_t& redirectFlags,
1560 nsIChannel** outChannel) {
1561 LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
1563 if (mCanceled) {
1564 return NS_ERROR_ABORT;
1567 nsresult rv;
1568 nsCOMPtr<nsIIOService> ioService;
1569 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
1570 NS_ENSURE_SUCCESS(rv, rv);
1572 nsCOMPtr<nsIChannel> newChannel;
1573 nsCOMPtr<nsILoadInfo> redirectLoadInfo =
1574 CloneLoadInfoForRedirect(uri, redirectFlags);
1575 rv = NS_NewChannelInternal(getter_AddRefs(newChannel), uri, redirectLoadInfo,
1576 nullptr, // PerformanceStorage
1577 nullptr, // aLoadGroup
1578 nullptr, // aCallbacks
1579 nsIRequest::LOAD_NORMAL, ioService);
1580 NS_ENSURE_SUCCESS(rv, rv);
1582 // We won't get OnStartRequest, set cookies here.
1583 mResponseHead = MakeUnique<nsHttpResponseHead>(*responseHead);
1585 bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(
1586 mResponseHead->Status(), mRequestHead.ParsedMethod());
1588 rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, redirectFlags);
1589 NS_ENSURE_SUCCESS(rv, rv);
1591 mRedirectChannelChild = do_QueryInterface(newChannel);
1592 newChannel.forget(outChannel);
1594 return NS_OK;
1597 void HttpChannelChild::Redirect1Begin(
1598 const uint32_t& registrarId, nsIURI* newOriginalURI,
1599 const uint32_t& newLoadFlags, const uint32_t& redirectFlags,
1600 const ParentLoadInfoForwarderArgs& loadInfoForwarder,
1601 const nsHttpResponseHead& responseHead,
1602 nsITransportSecurityInfo* securityInfo, const uint64_t& channelId,
1603 const ResourceTimingStructArgs& timing) {
1604 nsresult rv;
1606 LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
1608 MOZ_ASSERT(newOriginalURI, "newOriginalURI should not be null");
1610 ipc::MergeParentLoadInfoForwarder(loadInfoForwarder, mLoadInfo);
1611 ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings);
1613 if (profiler_thread_is_being_profiled_for_markers()) {
1614 nsAutoCString requestMethod;
1615 GetRequestMethod(requestMethod);
1616 nsAutoCString contentType;
1617 responseHead.ContentType(contentType);
1619 profiler_add_network_marker(
1620 mURI, requestMethod, mPriority, mChannelId,
1621 NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(),
1622 0, kCacheUnknown, mLoadInfo->GetInnerWindowID(),
1623 mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0,
1624 &mTransactionTimings, std::move(mSource),
1625 Some(nsDependentCString(contentType.get())), newOriginalURI,
1626 redirectFlags, channelId);
1629 mSecurityInfo = securityInfo;
1631 nsCOMPtr<nsIChannel> newChannel;
1632 rv = SetupRedirect(newOriginalURI, &responseHead, redirectFlags,
1633 getter_AddRefs(newChannel));
1635 if (NS_SUCCEEDED(rv)) {
1636 MOZ_ALWAYS_SUCCEEDS(newChannel->SetLoadFlags(newLoadFlags));
1638 if (mRedirectChannelChild) {
1639 // Set the channelId allocated in parent to the child instance
1640 nsCOMPtr<nsIHttpChannel> httpChannel =
1641 do_QueryInterface(mRedirectChannelChild);
1642 if (httpChannel) {
1643 rv = httpChannel->SetChannelId(channelId);
1644 MOZ_ASSERT(NS_SUCCEEDED(rv));
1646 mRedirectChannelChild->ConnectParent(registrarId);
1649 nsCOMPtr<nsISerialEventTarget> target = GetNeckoTarget();
1650 MOZ_ASSERT(target);
1652 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags,
1653 target);
1656 if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv);
1659 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirect3Complete() {
1660 LOG(("HttpChannelChild::RecvRedirect3Complete [this=%p]\n", this));
1661 nsCOMPtr<nsIChannel> redirectChannel =
1662 do_QueryInterface(mRedirectChannelChild);
1663 MOZ_ASSERT(redirectChannel);
1664 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1665 this, [self = UnsafePtr<HttpChannelChild>(this), redirectChannel]() {
1666 nsresult rv = NS_OK;
1667 Unused << self->GetStatus(&rv);
1668 if (NS_FAILED(rv)) {
1669 // Pre-redirect channel was canceled. Call |HandleAsyncAbort|, so
1670 // mListener's OnStart/StopRequest can be called. Nothing else will
1671 // trigger these notification after this point.
1672 // We do this before |CompleteRedirectSetup|, so post-redirect channel
1673 // stays unopened and we also make sure that OnStart/StopRequest won't
1674 // be called twice.
1675 self->HandleAsyncAbort();
1677 nsCOMPtr<nsIHttpChannelChild> chan =
1678 do_QueryInterface(redirectChannel);
1679 RefPtr<HttpChannelChild> httpChannelChild =
1680 static_cast<HttpChannelChild*>(chan.get());
1681 if (httpChannelChild) {
1682 // For sending an IPC message to parent channel so that the loading
1683 // can be cancelled.
1684 Unused << httpChannelChild->CancelWithReason(
1685 rv, "HttpChannelChild Redirect3 failed"_ns);
1687 // The post-redirect channel could still get OnStart/StopRequest IPC
1688 // messages from parent, but the mListener is still null. So, we
1689 // call |DoNotifyListener| to pretend that OnStart/StopRequest are
1690 // already called.
1691 httpChannelChild->DoNotifyListener();
1693 return;
1696 self->Redirect3Complete();
1697 }));
1698 return IPC_OK();
1701 mozilla::ipc::IPCResult HttpChannelChild::RecvRedirectFailed(
1702 const nsresult& status) {
1703 LOG(("HttpChannelChild::RecvRedirectFailed this=%p status=%X\n", this,
1704 static_cast<uint32_t>(status)));
1705 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1706 this, [self = UnsafePtr<HttpChannelChild>(this), status]() {
1707 nsCOMPtr<nsIRedirectResultListener> vetoHook;
1708 self->GetCallback(vetoHook);
1709 if (vetoHook) {
1710 vetoHook->OnRedirectResult(status);
1713 if (RefPtr<HttpChannelChild> httpChannelChild =
1714 do_QueryObject(self->mRedirectChannelChild)) {
1715 // For sending an IPC message to parent channel so that the loading
1716 // can be cancelled.
1717 Unused << httpChannelChild->CancelWithReason(
1718 status, "HttpChannelChild RecvRedirectFailed"_ns);
1720 // The post-redirect channel could still get OnStart/StopRequest IPC
1721 // messages from parent, but the mListener is still null. So, we
1722 // call |DoNotifyListener| to pretend that OnStart/StopRequest are
1723 // already called.
1724 httpChannelChild->DoNotifyListener();
1726 }));
1728 return IPC_OK();
1731 void HttpChannelChild::ProcessNotifyClassificationFlags(
1732 uint32_t aClassificationFlags, bool aIsThirdParty) {
1733 LOG(
1734 ("HttpChannelChild::ProcessNotifyClassificationFlags thirdparty=%d "
1735 "flags=%" PRIu32 " [this=%p]\n",
1736 static_cast<int>(aIsThirdParty), aClassificationFlags, this));
1737 MOZ_ASSERT(OnSocketThread());
1739 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1740 this, [self = UnsafePtr<HttpChannelChild>(this), aClassificationFlags,
1741 aIsThirdParty]() {
1742 self->AddClassificationFlags(aClassificationFlags, aIsThirdParty);
1743 }));
1746 void HttpChannelChild::ProcessSetClassifierMatchedInfo(
1747 const nsACString& aList, const nsACString& aProvider,
1748 const nsACString& aFullHash) {
1749 LOG(("HttpChannelChild::ProcessSetClassifierMatchedInfo [this=%p]\n", this));
1750 MOZ_ASSERT(OnSocketThread());
1752 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1753 this,
1754 [self = UnsafePtr<HttpChannelChild>(this), aList = nsCString(aList),
1755 aProvider = nsCString(aProvider), aFullHash = nsCString(aFullHash)]() {
1756 self->SetMatchedInfo(aList, aProvider, aFullHash);
1757 }));
1760 void HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo(
1761 const nsACString& aLists, const nsACString& aFullHashes) {
1762 LOG(("HttpChannelChild::ProcessSetClassifierMatchedTrackingInfo [this=%p]\n",
1763 this));
1764 MOZ_ASSERT(OnSocketThread());
1766 nsTArray<nsCString> lists, fullhashes;
1767 for (const nsACString& token : aLists.Split(',')) {
1768 lists.AppendElement(token);
1770 for (const nsACString& token : aFullHashes.Split(',')) {
1771 fullhashes.AppendElement(token);
1774 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
1775 this, [self = UnsafePtr<HttpChannelChild>(this),
1776 lists = CopyableTArray{std::move(lists)},
1777 fullhashes = CopyableTArray{std::move(fullhashes)}]() {
1778 self->SetMatchedTrackingInfo(lists, fullhashes);
1779 }));
1782 // Completes the redirect and cleans up the old channel.
1783 void HttpChannelChild::Redirect3Complete() {
1784 LOG(("HttpChannelChild::Redirect3Complete [this=%p]\n", this));
1785 MOZ_ASSERT(NS_IsMainThread());
1787 // Using an error as the default so that when we fail to forward this redirect
1788 // to the target channel, we make sure to notify the current listener from
1789 // CleanupRedirectingChannel.
1790 nsresult rv = NS_BINDING_ABORTED;
1792 nsCOMPtr<nsIRedirectResultListener> vetoHook;
1793 GetCallback(vetoHook);
1794 if (vetoHook) {
1795 vetoHook->OnRedirectResult(NS_OK);
1798 // Chrome channel has been AsyncOpen'd. Reflect this in child.
1799 if (mRedirectChannelChild) {
1800 rv = mRedirectChannelChild->CompleteRedirectSetup(mListener);
1801 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1802 mSuccesfullyRedirected = NS_SUCCEEDED(rv);
1803 #endif
1806 CleanupRedirectingChannel(rv);
1809 void HttpChannelChild::CleanupRedirectingChannel(nsresult rv) {
1810 // Redirecting to new channel: shut this down and init new channel
1811 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_ABORTED);
1813 if (NS_SUCCEEDED(rv)) {
1814 mLoadInfo->AppendRedirectHistoryEntry(this, false);
1815 } else {
1816 NS_WARNING("CompleteRedirectSetup failed, HttpChannelChild already open?");
1819 // Release ref to new channel.
1820 mRedirectChannelChild = nullptr;
1822 NotifyOrReleaseListeners(rv);
1823 CleanupBackgroundChannel();
1826 //-----------------------------------------------------------------------------
1827 // HttpChannelChild::nsIChildChannel
1828 //-----------------------------------------------------------------------------
1830 NS_IMETHODIMP
1831 HttpChannelChild::ConnectParent(uint32_t registrarId) {
1832 LOG(("HttpChannelChild::ConnectParent [this=%p, id=%" PRIu32 "]\n", this,
1833 registrarId));
1834 MOZ_ASSERT(NS_IsMainThread());
1835 mozilla::dom::BrowserChild* browserChild = nullptr;
1836 nsCOMPtr<nsIBrowserChild> iBrowserChild;
1837 GetCallback(iBrowserChild);
1838 if (iBrowserChild) {
1839 browserChild =
1840 static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
1843 if (browserChild && !browserChild->IPCOpen()) {
1844 return NS_ERROR_FAILURE;
1847 ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
1848 if (cc->IsShuttingDown()) {
1849 return NS_ERROR_FAILURE;
1852 HttpBaseChannel::SetDocshellUserAgentOverride();
1854 // This must happen before the constructor message is sent. Otherwise messages
1855 // from the parent could arrive quickly and be delivered to the wrong event
1856 // target.
1857 SetEventTarget();
1859 if (browserChild) {
1860 MOZ_ASSERT(browserChild->WebNavigation());
1861 if (BrowsingContext* bc = browserChild->GetBrowsingContext()) {
1862 mBrowserId = bc->BrowserId();
1866 HttpChannelConnectArgs connectArgs(registrarId);
1867 if (!gNeckoChild->SendPHttpChannelConstructor(
1868 this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
1869 return NS_ERROR_FAILURE;
1873 MutexAutoLock lock(mBgChildMutex);
1875 MOZ_ASSERT(!mBgChild);
1876 MOZ_ASSERT(!mBgInitFailCallback);
1878 mBgInitFailCallback = NewRunnableMethod<nsresult>(
1879 "HttpChannelChild::OnRedirectVerifyCallback", this,
1880 &HttpChannelChild::OnRedirectVerifyCallback, NS_ERROR_FAILURE);
1882 RefPtr<HttpBackgroundChannelChild> bgChild =
1883 new HttpBackgroundChannelChild();
1885 MOZ_RELEASE_ASSERT(gSocketTransportService);
1887 RefPtr<HttpChannelChild> self = this;
1888 nsresult rv = gSocketTransportService->Dispatch(
1889 NewRunnableMethod<RefPtr<HttpChannelChild>>(
1890 "HttpBackgroundChannelChild::Init", bgChild,
1891 &HttpBackgroundChannelChild::Init, std::move(self)),
1892 NS_DISPATCH_NORMAL);
1894 if (NS_WARN_IF(NS_FAILED(rv))) {
1895 return rv;
1898 mBgChild = std::move(bgChild);
1899 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1900 mEverHadBgChildAtConnectParent = true;
1901 #endif
1904 // Should wait for CompleteRedirectSetup to set the listener.
1905 mEventQ->Suspend();
1906 MOZ_ASSERT(!mSuspendForWaitCompleteRedirectSetup);
1907 mSuspendForWaitCompleteRedirectSetup = true;
1909 // Connect to socket process after mEventQ is suspended.
1910 MaybeConnectToSocketProcess();
1912 return NS_OK;
1915 NS_IMETHODIMP
1916 HttpChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
1917 LOG(("HttpChannelChild::CompleteRedirectSetup [this=%p]\n", this));
1918 MOZ_ASSERT(NS_IsMainThread());
1920 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
1921 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
1923 // Resume the suspension in ConnectParent.
1924 auto eventQueueResumeGuard = MakeScopeExit([&] {
1925 MOZ_ASSERT(mSuspendForWaitCompleteRedirectSetup);
1926 mEventQ->Resume();
1927 mSuspendForWaitCompleteRedirectSetup = false;
1931 * No need to check for cancel: we don't get here if nsHttpChannel canceled
1932 * before AsyncOpen(); if it's canceled after that, OnStart/Stop will just
1933 * get called with error code as usual. So just setup mListener and make the
1934 * channel reflect AsyncOpen'ed state.
1937 mLastStatusReported = TimeStamp::Now();
1938 if (profiler_thread_is_being_profiled_for_markers()) {
1939 nsAutoCString requestMethod;
1940 GetRequestMethod(requestMethod);
1942 profiler_add_network_marker(
1943 mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
1944 mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
1945 mLoadInfo->GetInnerWindowID(),
1946 mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0);
1948 StoreIsPending(true);
1949 StoreWasOpened(true);
1950 mListener = aListener;
1952 // add ourselves to the load group.
1953 if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
1955 // We already have an open IPDL connection to the parent. If on-modify-request
1956 // listeners or load group observers canceled us, let the parent handle it
1957 // and send it back to us naturally.
1958 return NS_OK;
1961 //-----------------------------------------------------------------------------
1962 // HttpChannelChild::nsIAsyncVerifyRedirectCallback
1963 //-----------------------------------------------------------------------------
1965 NS_IMETHODIMP
1966 HttpChannelChild::OnRedirectVerifyCallback(nsresult aResult) {
1967 LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
1968 MOZ_ASSERT(NS_IsMainThread());
1969 nsCOMPtr<nsIURI> redirectURI;
1971 DebugOnly<nsresult> rv = NS_OK;
1973 nsCOMPtr<nsIHttpChannel> newHttpChannel =
1974 do_QueryInterface(mRedirectChannelChild);
1976 if (NS_SUCCEEDED(aResult) && !mRedirectChannelChild) {
1977 // mRedirectChannelChild doesn't exist means we're redirecting to a protocol
1978 // that doesn't implement nsIChildChannel. The redirect result should be set
1979 // as failed by veto listeners and shouldn't enter this condition. As the
1980 // last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
1981 // to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
1982 // another protocol and throw an error.
1983 LOG((" redirecting to a protocol that doesn't implement nsIChildChannel"));
1984 aResult = NS_ERROR_DOM_BAD_URI;
1987 nsCOMPtr<nsIReferrerInfo> referrerInfo;
1988 if (newHttpChannel) {
1989 // Must not be called until after redirect observers called.
1990 newHttpChannel->SetOriginalURI(mOriginalURI);
1991 referrerInfo = newHttpChannel->GetReferrerInfo();
1994 RequestHeaderTuples emptyHeaders;
1995 RequestHeaderTuples* headerTuples = &emptyHeaders;
1996 nsLoadFlags loadFlags = 0;
1997 Maybe<CorsPreflightArgs> corsPreflightArgs;
1999 nsCOMPtr<nsIHttpChannelChild> newHttpChannelChild =
2000 do_QueryInterface(mRedirectChannelChild);
2001 if (newHttpChannelChild && NS_SUCCEEDED(aResult)) {
2002 rv = newHttpChannelChild->AddCookiesToRequest();
2003 MOZ_ASSERT(NS_SUCCEEDED(rv));
2004 rv = newHttpChannelChild->GetClientSetRequestHeaders(&headerTuples);
2005 MOZ_ASSERT(NS_SUCCEEDED(rv));
2006 newHttpChannelChild->GetClientSetCorsPreflightParameters(corsPreflightArgs);
2009 if (NS_SUCCEEDED(aResult)) {
2010 // Note: this is where we would notify "http-on-modify-response" observers.
2011 // We have deliberately disabled this for child processes (see bug 806753)
2013 // After we verify redirect, nsHttpChannel may hit the network: must give
2014 // "http-on-modify-request" observers the chance to cancel before that.
2015 // base->CallOnModifyRequestObservers();
2017 nsCOMPtr<nsIHttpChannelInternal> newHttpChannelInternal =
2018 do_QueryInterface(mRedirectChannelChild);
2019 if (newHttpChannelInternal) {
2020 Unused << newHttpChannelInternal->GetApiRedirectToURI(
2021 getter_AddRefs(redirectURI));
2024 nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
2025 if (request) {
2026 request->GetLoadFlags(&loadFlags);
2030 uint32_t sourceRequestBlockingReason = 0;
2031 mLoadInfo->GetRequestBlockingReason(&sourceRequestBlockingReason);
2033 Maybe<ChildLoadInfoForwarderArgs> targetLoadInfoForwarder;
2034 nsCOMPtr<nsIChannel> newChannel = do_QueryInterface(mRedirectChannelChild);
2035 if (newChannel) {
2036 ChildLoadInfoForwarderArgs args;
2037 nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo();
2038 LoadInfoToChildLoadInfoForwarder(loadInfo, &args);
2039 targetLoadInfoForwarder.emplace(args);
2042 if (CanSend()) {
2043 SendRedirect2Verify(aResult, *headerTuples, sourceRequestBlockingReason,
2044 targetLoadInfoForwarder, loadFlags, referrerInfo,
2045 redirectURI, corsPreflightArgs);
2048 return NS_OK;
2051 //-----------------------------------------------------------------------------
2052 // HttpChannelChild::nsIRequest
2053 //-----------------------------------------------------------------------------
2055 NS_IMETHODIMP HttpChannelChild::SetCanceledReason(const nsACString& aReason) {
2056 return SetCanceledReasonImpl(aReason);
2059 NS_IMETHODIMP HttpChannelChild::GetCanceledReason(nsACString& aReason) {
2060 return GetCanceledReasonImpl(aReason);
2063 NS_IMETHODIMP
2064 HttpChannelChild::CancelWithReason(nsresult aStatus,
2065 const nsACString& aReason) {
2066 return CancelWithReasonImpl(aStatus, aReason);
2069 NS_IMETHODIMP
2070 HttpChannelChild::Cancel(nsresult aStatus) {
2071 LOG(("HttpChannelChild::Cancel [this=%p, status=%" PRIx32 "]\n", this,
2072 static_cast<uint32_t>(aStatus)));
2073 // only logging on parent is necessary
2074 Maybe<nsCString> logStack = CallingScriptLocationString();
2075 Maybe<nsCString> logOnParent;
2076 if (logStack.isSome()) {
2077 logOnParent = Some(""_ns);
2078 logOnParent->AppendPrintf(
2079 "[this=%p] cancelled call in child process from script: %s", this,
2080 logStack->get());
2083 MOZ_ASSERT(NS_IsMainThread());
2085 if (!mCanceled) {
2086 // If this cancel occurs before nsHttpChannel has been set up, AsyncOpen
2087 // is responsible for cleaning up.
2088 mCanceled = true;
2089 mStatus = aStatus;
2091 bool remoteChannelExists = RemoteChannelExists();
2092 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2093 mCanSendAtCancel = CanSend();
2094 mRemoteChannelExistedAtCancel = remoteChannelExists;
2095 #endif
2097 if (remoteChannelExists) {
2098 SendCancel(aStatus, mLoadInfo->GetRequestBlockingReason(),
2099 mCanceledReason, logOnParent);
2100 } else if (MOZ_UNLIKELY(!LoadOnStartRequestCalled() ||
2101 !LoadOnStopRequestCalled())) {
2102 Unused << AsyncAbort(mStatus);
2105 return NS_OK;
2108 NS_IMETHODIMP
2109 HttpChannelChild::Suspend() {
2110 LOG(("HttpChannelChild::Suspend [this=%p, mSuspendCount=%" PRIu32 "\n", this,
2111 mSuspendCount + 1));
2112 MOZ_ASSERT(NS_IsMainThread());
2114 LogCallingScriptLocation(this);
2116 // SendSuspend only once, when suspend goes from 0 to 1.
2117 // Don't SendSuspend at all if we're diverting callbacks to the parent;
2118 // suspend will be called at the correct time in the parent itself.
2119 if (!mSuspendCount++) {
2120 if (RemoteChannelExists()) {
2121 SendSuspend();
2122 mSuspendSent = true;
2125 mEventQ->Suspend();
2127 return NS_OK;
2130 NS_IMETHODIMP
2131 HttpChannelChild::Resume() {
2132 LOG(("HttpChannelChild::Resume [this=%p, mSuspendCount=%" PRIu32 "\n", this,
2133 mSuspendCount - 1));
2134 MOZ_ASSERT(NS_IsMainThread());
2135 NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
2137 LogCallingScriptLocation(this);
2139 nsresult rv = NS_OK;
2141 // SendResume only once, when suspend count drops to 0.
2142 // Don't SendResume at all if we're diverting callbacks to the parent (unless
2143 // suspend was sent earlier); otherwise, resume will be called at the correct
2144 // time in the parent itself.
2145 if (!--mSuspendCount) {
2146 if (RemoteChannelExists() && mSuspendSent) {
2147 SendResume();
2149 if (mCallOnResume) {
2150 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2151 MOZ_ASSERT(neckoTarget);
2153 RefPtr<HttpChannelChild> self = this;
2154 std::function<nsresult(HttpChannelChild*)> callOnResume = nullptr;
2155 std::swap(callOnResume, mCallOnResume);
2156 rv = neckoTarget->Dispatch(
2157 NS_NewRunnableFunction(
2158 "net::HttpChannelChild::mCallOnResume",
2159 [callOnResume, self{std::move(self)}]() { callOnResume(self); }),
2160 NS_DISPATCH_NORMAL);
2163 mEventQ->Resume();
2165 return rv;
2168 //-----------------------------------------------------------------------------
2169 // HttpChannelChild::nsIChannel
2170 //-----------------------------------------------------------------------------
2172 NS_IMETHODIMP
2173 HttpChannelChild::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
2174 NS_ENSURE_ARG_POINTER(aSecurityInfo);
2175 *aSecurityInfo = do_AddRef(mSecurityInfo).take();
2176 return NS_OK;
2179 NS_IMETHODIMP
2180 HttpChannelChild::AsyncOpen(nsIStreamListener* aListener) {
2181 LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
2183 nsresult rv = AsyncOpenInternal(aListener);
2184 if (NS_FAILED(rv)) {
2185 uint32_t blockingReason = 0;
2186 mLoadInfo->GetRequestBlockingReason(&blockingReason);
2187 LOG(
2188 ("HttpChannelChild::AsyncOpen failed [this=%p rv=0x%08x "
2189 "blocking-reason=%u]\n",
2190 this, static_cast<uint32_t>(rv), blockingReason));
2192 gHttpHandler->OnFailedOpeningRequest(this);
2195 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2196 mAsyncOpenSucceeded = NS_SUCCEEDED(rv);
2197 #endif
2198 return rv;
2201 nsresult HttpChannelChild::AsyncOpenInternal(nsIStreamListener* aListener) {
2202 nsresult rv;
2204 nsCOMPtr<nsIStreamListener> listener = aListener;
2205 rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
2206 if (NS_WARN_IF(NS_FAILED(rv))) {
2207 ReleaseListeners();
2208 return rv;
2211 MOZ_ASSERT(
2212 mLoadInfo->GetSecurityMode() == 0 ||
2213 mLoadInfo->GetInitialSecurityCheckDone() ||
2214 (mLoadInfo->GetSecurityMode() ==
2215 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
2216 mLoadInfo->GetLoadingPrincipal() &&
2217 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
2218 "security flags in loadInfo but doContentSecurityCheck() not called");
2220 LogCallingScriptLocation(this);
2222 if (!mLoadGroup && !mCallbacks) {
2223 // If no one called SetLoadGroup or SetNotificationCallbacks, the private
2224 // state has not been updated on PrivateBrowsingChannel (which we derive
2225 // from) Hence, we have to call UpdatePrivateBrowsing() here
2226 UpdatePrivateBrowsing();
2229 #ifdef DEBUG
2230 AssertPrivateBrowsingId();
2231 #endif
2233 if (mCanceled) {
2234 ReleaseListeners();
2235 return mStatus;
2238 NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
2239 NS_ENSURE_ARG_POINTER(listener);
2240 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
2241 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
2243 if (MaybeWaitForUploadStreamNormalization(listener, nullptr)) {
2244 return NS_OK;
2247 if (!LoadAsyncOpenTimeOverriden()) {
2248 mAsyncOpenTime = TimeStamp::Now();
2251 // Port checked in parent, but duplicate here so we can return with error
2252 // immediately
2253 rv = NS_CheckPortSafety(mURI);
2254 if (NS_FAILED(rv)) {
2255 ReleaseListeners();
2256 return rv;
2259 nsAutoCString cookie;
2260 if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookie))) {
2261 mUserSetCookieHeader = cookie;
2264 DebugOnly<nsresult> check = AddCookiesToRequest();
2265 MOZ_ASSERT(NS_SUCCEEDED(check));
2268 // NOTE: From now on we must return NS_OK; all errors must be handled via
2269 // OnStart/OnStopRequest
2272 // We notify "http-on-opening-request" observers in the child
2273 // process so that devtools can capture a stack trace at the
2274 // appropriate spot. See bug 806753 for some information about why
2275 // other http-* notifications are disabled in child processes.
2276 gHttpHandler->OnOpeningRequest(this);
2278 mLastStatusReported = TimeStamp::Now();
2279 if (profiler_thread_is_being_profiled_for_markers()) {
2280 nsAutoCString requestMethod;
2281 GetRequestMethod(requestMethod);
2283 profiler_add_network_marker(
2284 mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
2285 mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
2286 mLoadInfo->GetInnerWindowID(),
2287 mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0);
2289 StoreIsPending(true);
2290 StoreWasOpened(true);
2291 mListener = listener;
2293 if (mCanceled) {
2294 // We may have been canceled already, either by on-modify-request
2295 // listeners or by load group observers; in that case, don't create IPDL
2296 // connection. See nsHttpChannel::AsyncOpen().
2297 ReleaseListeners();
2298 return mStatus;
2301 // Set user agent override from docshell
2302 HttpBaseChannel::SetDocshellUserAgentOverride();
2304 rv = ContinueAsyncOpen();
2305 if (NS_FAILED(rv)) {
2306 ReleaseListeners();
2308 return rv;
2311 // Assigns an nsISerialEventTarget to our IPDL actor so that IPC messages are
2312 // sent to the correct DocGroup/TabGroup.
2313 void HttpChannelChild::SetEventTarget() {
2314 MutexAutoLock lock(mEventTargetMutex);
2315 mNeckoTarget = GetMainThreadSerialEventTarget();
2318 already_AddRefed<nsISerialEventTarget> HttpChannelChild::GetNeckoTarget() {
2319 nsCOMPtr<nsISerialEventTarget> target;
2321 MutexAutoLock lock(mEventTargetMutex);
2322 target = mNeckoTarget;
2325 if (!target) {
2326 target = GetMainThreadSerialEventTarget();
2328 return target.forget();
2331 already_AddRefed<nsIEventTarget> HttpChannelChild::GetODATarget() {
2332 nsCOMPtr<nsIEventTarget> target;
2334 MutexAutoLock lock(mEventTargetMutex);
2335 if (mODATarget) {
2336 target = mODATarget;
2337 } else {
2338 target = mNeckoTarget;
2342 if (!target) {
2343 target = GetMainThreadSerialEventTarget();
2345 return target.forget();
2348 nsresult HttpChannelChild::ContinueAsyncOpen() {
2349 nsresult rv;
2351 // Send request to the chrome process...
2354 mozilla::dom::BrowserChild* browserChild = nullptr;
2355 nsCOMPtr<nsIBrowserChild> iBrowserChild;
2356 GetCallback(iBrowserChild);
2357 if (iBrowserChild) {
2358 browserChild =
2359 static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
2362 // This id identifies the inner window's top-level document,
2363 // which changes on every new load or navigation.
2364 uint64_t contentWindowId = 0;
2365 TimeStamp navigationStartTimeStamp;
2366 if (browserChild) {
2367 MOZ_ASSERT(browserChild->WebNavigation());
2368 if (RefPtr<Document> document = browserChild->GetTopLevelDocument()) {
2369 contentWindowId = document->InnerWindowID();
2370 nsDOMNavigationTiming* navigationTiming = document->GetNavigationTiming();
2371 if (navigationTiming) {
2372 navigationStartTimeStamp =
2373 navigationTiming->GetNavigationStartTimeStamp();
2376 if (BrowsingContext* bc = browserChild->GetBrowsingContext()) {
2377 mBrowserId = bc->BrowserId();
2380 SetTopLevelContentWindowId(contentWindowId);
2382 if (browserChild && !browserChild->IPCOpen()) {
2383 return NS_ERROR_FAILURE;
2386 ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
2387 if (cc->IsShuttingDown()) {
2388 return NS_ERROR_FAILURE;
2391 // add ourselves to the load group.
2392 if (mLoadGroup) {
2393 mLoadGroup->AddRequest(this, nullptr);
2396 HttpChannelOpenArgs openArgs;
2397 // No access to HttpChannelOpenArgs members, but they each have a
2398 // function with the struct name that returns a ref.
2399 openArgs.uri() = mURI;
2400 openArgs.original() = mOriginalURI;
2401 openArgs.doc() = mDocumentURI;
2402 openArgs.apiRedirectTo() = mAPIRedirectToURI;
2403 openArgs.loadFlags() = mLoadFlags;
2404 openArgs.requestHeaders() = mClientSetRequestHeaders;
2405 mRequestHead.Method(openArgs.requestMethod());
2406 openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes.Clone();
2407 openArgs.referrerInfo() = mReferrerInfo;
2409 if (mUploadStream) {
2410 MOZ_ALWAYS_TRUE(SerializeIPCStream(do_AddRef(mUploadStream),
2411 openArgs.uploadStream(),
2412 /* aAllowLazy */ false));
2415 Maybe<CorsPreflightArgs> optionalCorsPreflightArgs;
2416 GetClientSetCorsPreflightParameters(optionalCorsPreflightArgs);
2418 // NB: This call forces us to cache mTopWindowURI if we haven't already.
2419 nsCOMPtr<nsIURI> uri;
2420 GetTopWindowURI(mURI, getter_AddRefs(uri));
2422 openArgs.topWindowURI() = mTopWindowURI;
2424 openArgs.preflightArgs() = optionalCorsPreflightArgs;
2426 openArgs.uploadStreamHasHeaders() = LoadUploadStreamHasHeaders();
2427 openArgs.priority() = mPriority;
2428 openArgs.classOfService() = mClassOfService;
2429 openArgs.redirectionLimit() = mRedirectionLimit;
2430 openArgs.allowSTS() = LoadAllowSTS();
2431 openArgs.thirdPartyFlags() = LoadThirdPartyFlags();
2432 openArgs.resumeAt() = mSendResumeAt;
2433 openArgs.startPos() = mStartPos;
2434 openArgs.entityID() = mEntityID;
2435 openArgs.allowSpdy() = LoadAllowSpdy();
2436 openArgs.allowHttp3() = LoadAllowHttp3();
2437 openArgs.allowAltSvc() = LoadAllowAltSvc();
2438 openArgs.beConservative() = LoadBeConservative();
2439 openArgs.bypassProxy() = BypassProxy();
2440 openArgs.tlsFlags() = mTlsFlags;
2441 openArgs.initialRwin() = mInitialRwin;
2443 openArgs.cacheKey() = mCacheKey;
2445 openArgs.blockAuthPrompt() = LoadBlockAuthPrompt();
2447 openArgs.allowStaleCacheContent() = LoadAllowStaleCacheContent();
2448 openArgs.preferCacheLoadOverBypass() = LoadPreferCacheLoadOverBypass();
2450 openArgs.contentTypeHint() = mContentTypeHint;
2452 rv = mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &openArgs.loadInfo());
2453 NS_ENSURE_SUCCESS(rv, rv);
2455 EnsureRequestContextID();
2456 openArgs.requestContextID() = mRequestContextID;
2458 openArgs.requestMode() = mRequestMode;
2459 openArgs.redirectMode() = mRedirectMode;
2461 openArgs.channelId() = mChannelId;
2463 openArgs.integrityMetadata() = mIntegrityMetadata;
2465 openArgs.contentWindowId() = contentWindowId;
2466 openArgs.browserId() = mBrowserId;
2468 LOG(("HttpChannelChild::ContinueAsyncOpen this=%p gid=%" PRIu64
2469 " browser id=%" PRIx64,
2470 this, mChannelId, mBrowserId));
2472 openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
2473 openArgs.launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
2474 openArgs.dispatchFetchEventStart() = mDispatchFetchEventStart;
2475 openArgs.dispatchFetchEventEnd() = mDispatchFetchEventEnd;
2476 openArgs.handleFetchEventStart() = mHandleFetchEventStart;
2477 openArgs.handleFetchEventEnd() = mHandleFetchEventEnd;
2479 openArgs.forceMainDocumentChannel() = LoadForceMainDocumentChannel();
2481 openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
2482 openArgs.earlyHintPreloaderId() = mEarlyHintPreloaderId;
2484 openArgs.classicScriptHintCharset() = mClassicScriptHintCharset;
2486 RefPtr<Document> doc;
2487 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
2489 if (doc) {
2490 nsAutoString documentCharacterSet;
2491 doc->GetCharacterSet(documentCharacterSet);
2492 openArgs.documentCharacterSet() = documentCharacterSet;
2495 // This must happen before the constructor message is sent. Otherwise messages
2496 // from the parent could arrive quickly and be delivered to the wrong event
2497 // target.
2498 SetEventTarget();
2500 if (!gNeckoChild->SendPHttpChannelConstructor(
2501 this, browserChild, IPC::SerializedLoadContext(this), openArgs)) {
2502 return NS_ERROR_FAILURE;
2506 MutexAutoLock lock(mBgChildMutex);
2508 MOZ_RELEASE_ASSERT(gSocketTransportService);
2510 // Service worker might use the same HttpChannelChild to do async open
2511 // twice. Need to disconnect with previous background channel before
2512 // creating the new one, to prevent receiving further notification
2513 // from it.
2514 if (mBgChild) {
2515 RefPtr<HttpBackgroundChannelChild> prevBgChild = std::move(mBgChild);
2516 gSocketTransportService->Dispatch(
2517 NewRunnableMethod("HttpBackgroundChannelChild::OnChannelClosed",
2518 prevBgChild,
2519 &HttpBackgroundChannelChild::OnChannelClosed),
2520 NS_DISPATCH_NORMAL);
2523 MOZ_ASSERT(!mBgInitFailCallback);
2525 mBgInitFailCallback = NewRunnableMethod<nsresult>(
2526 "HttpChannelChild::FailedAsyncOpen", this,
2527 &HttpChannelChild::FailedAsyncOpen, NS_ERROR_FAILURE);
2529 RefPtr<HttpBackgroundChannelChild> bgChild =
2530 new HttpBackgroundChannelChild();
2532 RefPtr<HttpChannelChild> self = this;
2533 nsresult rv = gSocketTransportService->Dispatch(
2534 NewRunnableMethod<RefPtr<HttpChannelChild>>(
2535 "HttpBackgroundChannelChild::Init", bgChild,
2536 &HttpBackgroundChannelChild::Init, self),
2537 NS_DISPATCH_NORMAL);
2539 if (NS_WARN_IF(NS_FAILED(rv))) {
2540 return rv;
2543 mBgChild = std::move(bgChild);
2544 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2545 mEverHadBgChildAtAsyncOpen = true;
2546 #endif
2549 MaybeConnectToSocketProcess();
2551 return NS_OK;
2554 //-----------------------------------------------------------------------------
2555 // HttpChannelChild::nsIHttpChannel
2556 //-----------------------------------------------------------------------------
2558 NS_IMETHODIMP
2559 HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
2560 const nsACString& aValue, bool aMerge) {
2561 LOG(("HttpChannelChild::SetRequestHeader [this=%p]\n", this));
2562 nsresult rv = HttpBaseChannel::SetRequestHeader(aHeader, aValue, aMerge);
2563 if (NS_FAILED(rv)) return rv;
2565 RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2566 if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2568 tuple->mHeader = aHeader;
2569 tuple->mValue = aValue;
2570 tuple->mMerge = aMerge;
2571 tuple->mEmpty = false;
2572 return NS_OK;
2575 NS_IMETHODIMP
2576 HttpChannelChild::SetEmptyRequestHeader(const nsACString& aHeader) {
2577 LOG(("HttpChannelChild::SetEmptyRequestHeader [this=%p]\n", this));
2578 nsresult rv = HttpBaseChannel::SetEmptyRequestHeader(aHeader);
2579 if (NS_FAILED(rv)) return rv;
2581 RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
2582 if (!tuple) return NS_ERROR_OUT_OF_MEMORY;
2584 tuple->mHeader = aHeader;
2585 tuple->mMerge = false;
2586 tuple->mEmpty = true;
2587 return NS_OK;
2590 NS_IMETHODIMP
2591 HttpChannelChild::RedirectTo(nsIURI* newURI) {
2592 // disabled until/unless addons run in child or something else needs this
2593 return NS_ERROR_NOT_IMPLEMENTED;
2596 NS_IMETHODIMP
2597 HttpChannelChild::UpgradeToSecure() {
2598 // disabled until/unless addons run in child or something else needs this
2599 return NS_ERROR_NOT_IMPLEMENTED;
2602 NS_IMETHODIMP
2603 HttpChannelChild::GetProtocolVersion(nsACString& aProtocolVersion) {
2604 aProtocolVersion = mProtocolVersion;
2605 return NS_OK;
2608 //-----------------------------------------------------------------------------
2609 // HttpChannelChild::nsIHttpChannelInternal
2610 //-----------------------------------------------------------------------------
2612 NS_IMETHODIMP
2613 HttpChannelChild::GetIsAuthChannel(bool* aIsAuthChannel) { DROP_DEAD(); }
2615 //-----------------------------------------------------------------------------
2616 // HttpChannelChild::nsICacheInfoChannel
2617 //-----------------------------------------------------------------------------
2619 NS_IMETHODIMP
2620 HttpChannelChild::GetCacheTokenFetchCount(uint32_t* _retval) {
2621 NS_ENSURE_ARG_POINTER(_retval);
2622 MOZ_ASSERT(NS_IsMainThread());
2624 if (!mCacheEntryAvailable && !mAltDataCacheEntryAvailable) {
2625 return NS_ERROR_NOT_AVAILABLE;
2628 *_retval = mCacheFetchCount;
2629 return NS_OK;
2632 NS_IMETHODIMP
2633 HttpChannelChild::GetCacheTokenExpirationTime(uint32_t* _retval) {
2634 NS_ENSURE_ARG_POINTER(_retval);
2635 MOZ_ASSERT(NS_IsMainThread());
2637 if (!mCacheEntryAvailable) return NS_ERROR_NOT_AVAILABLE;
2639 *_retval = mCacheExpirationTime;
2640 return NS_OK;
2643 NS_IMETHODIMP
2644 HttpChannelChild::IsFromCache(bool* value) {
2645 if (!LoadIsPending()) return NS_ERROR_NOT_AVAILABLE;
2647 *value = mIsFromCache;
2648 return NS_OK;
2651 NS_IMETHODIMP
2652 HttpChannelChild::GetCacheEntryId(uint64_t* aCacheEntryId) {
2653 bool fromCache = false;
2654 if (NS_FAILED(IsFromCache(&fromCache)) || !fromCache ||
2655 !mCacheEntryAvailable) {
2656 return NS_ERROR_NOT_AVAILABLE;
2659 *aCacheEntryId = mCacheEntryId;
2660 return NS_OK;
2663 NS_IMETHODIMP
2664 HttpChannelChild::IsRacing(bool* aIsRacing) {
2665 if (!LoadAfterOnStartRequestBegun()) {
2666 return NS_ERROR_NOT_AVAILABLE;
2668 *aIsRacing = mIsRacing;
2669 return NS_OK;
2672 NS_IMETHODIMP
2673 HttpChannelChild::GetCacheKey(uint32_t* cacheKey) {
2674 MOZ_ASSERT(NS_IsMainThread());
2676 *cacheKey = mCacheKey;
2677 return NS_OK;
2679 NS_IMETHODIMP
2680 HttpChannelChild::SetCacheKey(uint32_t cacheKey) {
2681 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
2683 mCacheKey = cacheKey;
2684 return NS_OK;
2687 NS_IMETHODIMP
2688 HttpChannelChild::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
2689 StoreAllowStaleCacheContent(aAllowStaleCacheContent);
2690 return NS_OK;
2692 NS_IMETHODIMP
2693 HttpChannelChild::GetAllowStaleCacheContent(bool* aAllowStaleCacheContent) {
2694 NS_ENSURE_ARG(aAllowStaleCacheContent);
2695 *aAllowStaleCacheContent = LoadAllowStaleCacheContent();
2696 return NS_OK;
2699 NS_IMETHODIMP
2700 HttpChannelChild::SetForceValidateCacheContent(
2701 bool aForceValidateCacheContent) {
2702 StoreForceValidateCacheContent(aForceValidateCacheContent);
2703 return NS_OK;
2705 NS_IMETHODIMP
2706 HttpChannelChild::GetForceValidateCacheContent(
2707 bool* aForceValidateCacheContent) {
2708 NS_ENSURE_ARG(aForceValidateCacheContent);
2709 *aForceValidateCacheContent = LoadForceValidateCacheContent();
2710 return NS_OK;
2713 NS_IMETHODIMP
2714 HttpChannelChild::SetPreferCacheLoadOverBypass(
2715 bool aPreferCacheLoadOverBypass) {
2716 StorePreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
2717 return NS_OK;
2719 NS_IMETHODIMP
2720 HttpChannelChild::GetPreferCacheLoadOverBypass(
2721 bool* aPreferCacheLoadOverBypass) {
2722 NS_ENSURE_ARG(aPreferCacheLoadOverBypass);
2723 *aPreferCacheLoadOverBypass = LoadPreferCacheLoadOverBypass();
2724 return NS_OK;
2727 NS_IMETHODIMP
2728 HttpChannelChild::PreferAlternativeDataType(
2729 const nsACString& aType, const nsACString& aContentType,
2730 PreferredAlternativeDataDeliveryType aDeliverAltData) {
2731 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
2733 mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
2734 nsCString(aType), nsCString(aContentType), aDeliverAltData));
2735 return NS_OK;
2738 const nsTArray<PreferredAlternativeDataTypeParams>&
2739 HttpChannelChild::PreferredAlternativeDataTypes() {
2740 return mPreferredCachedAltDataTypes;
2743 NS_IMETHODIMP
2744 HttpChannelChild::GetAlternativeDataType(nsACString& aType) {
2745 // Must be called during or after OnStartRequest
2746 if (!LoadAfterOnStartRequestBegun()) {
2747 return NS_ERROR_NOT_AVAILABLE;
2750 aType = mAvailableCachedAltDataType;
2751 return NS_OK;
2754 NS_IMETHODIMP
2755 HttpChannelChild::OpenAlternativeOutputStream(const nsACString& aType,
2756 int64_t aPredictedSize,
2757 nsIAsyncOutputStream** _retval) {
2758 MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
2760 if (!CanSend()) {
2761 return NS_ERROR_NOT_AVAILABLE;
2763 if (static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown()) {
2764 return NS_ERROR_NOT_AVAILABLE;
2767 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
2768 MOZ_ASSERT(neckoTarget);
2770 RefPtr<AltDataOutputStreamChild> stream = new AltDataOutputStreamChild();
2771 stream->AddIPDLReference();
2773 if (!gNeckoChild->SendPAltDataOutputStreamConstructor(
2774 stream, nsCString(aType), aPredictedSize, WrapNotNull(this))) {
2775 return NS_ERROR_FAILURE;
2778 stream.forget(_retval);
2779 return NS_OK;
2782 NS_IMETHODIMP
2783 HttpChannelChild::GetOriginalInputStream(nsIInputStreamReceiver* aReceiver) {
2784 if (aReceiver == nullptr) {
2785 return NS_ERROR_INVALID_ARG;
2788 if (!CanSend()) {
2789 return NS_ERROR_NOT_AVAILABLE;
2792 mOriginalInputStreamReceiver = aReceiver;
2793 Unused << SendOpenOriginalCacheInputStream();
2795 return NS_OK;
2798 NS_IMETHODIMP
2799 HttpChannelChild::GetAlternativeDataInputStream(nsIInputStream** aInputStream) {
2800 NS_ENSURE_ARG_POINTER(aInputStream);
2802 nsCOMPtr<nsIInputStream> is = mAltDataInputStream;
2803 is.forget(aInputStream);
2805 return NS_OK;
2808 mozilla::ipc::IPCResult HttpChannelChild::RecvOriginalCacheInputStreamAvailable(
2809 const Maybe<IPCStream>& aStream) {
2810 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
2811 nsCOMPtr<nsIInputStreamReceiver> receiver;
2812 receiver.swap(mOriginalInputStreamReceiver);
2813 if (receiver) {
2814 receiver->OnInputStreamReady(stream);
2817 return IPC_OK();
2820 //-----------------------------------------------------------------------------
2821 // HttpChannelChild::nsIResumableChannel
2822 //-----------------------------------------------------------------------------
2824 NS_IMETHODIMP
2825 HttpChannelChild::ResumeAt(uint64_t startPos, const nsACString& entityID) {
2826 LOG(("HttpChannelChild::ResumeAt [this=%p]\n", this));
2827 ENSURE_CALLED_BEFORE_CONNECT();
2828 mStartPos = startPos;
2829 mEntityID = entityID;
2830 mSendResumeAt = true;
2831 return NS_OK;
2834 // GetEntityID is shared in HttpBaseChannel
2836 //-----------------------------------------------------------------------------
2837 // HttpChannelChild::nsISupportsPriority
2838 //-----------------------------------------------------------------------------
2840 NS_IMETHODIMP
2841 HttpChannelChild::SetPriority(int32_t aPriority) {
2842 LOG(("HttpChannelChild::SetPriority %p p=%d", this, aPriority));
2843 int16_t newValue = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX);
2844 if (mPriority == newValue) return NS_OK;
2845 mPriority = newValue;
2846 if (RemoteChannelExists()) SendSetPriority(mPriority);
2847 return NS_OK;
2850 //-----------------------------------------------------------------------------
2851 // HttpChannelChild::nsIClassOfService
2852 //-----------------------------------------------------------------------------
2853 NS_IMETHODIMP
2854 HttpChannelChild::SetClassFlags(uint32_t inFlags) {
2855 if (mClassOfService.Flags() == inFlags) {
2856 return NS_OK;
2859 mClassOfService.SetFlags(inFlags);
2861 LOG(("HttpChannelChild %p ClassOfService flags=%lu inc=%d", this,
2862 mClassOfService.Flags(), mClassOfService.Incremental()));
2864 if (RemoteChannelExists()) {
2865 SendSetClassOfService(mClassOfService);
2867 return NS_OK;
2870 NS_IMETHODIMP
2871 HttpChannelChild::AddClassFlags(uint32_t inFlags) {
2872 mClassOfService.SetFlags(inFlags | mClassOfService.Flags());
2874 LOG(("HttpChannelChild %p ClassOfService flags=%lu inc=%d", this,
2875 mClassOfService.Flags(), mClassOfService.Incremental()));
2877 if (RemoteChannelExists()) {
2878 SendSetClassOfService(mClassOfService);
2880 return NS_OK;
2883 NS_IMETHODIMP
2884 HttpChannelChild::ClearClassFlags(uint32_t inFlags) {
2885 mClassOfService.SetFlags(~inFlags & mClassOfService.Flags());
2887 LOG(("HttpChannelChild %p ClassOfService=%lu", this,
2888 mClassOfService.Flags()));
2890 if (RemoteChannelExists()) {
2891 SendSetClassOfService(mClassOfService);
2893 return NS_OK;
2896 NS_IMETHODIMP
2897 HttpChannelChild::SetClassOfService(ClassOfService inCos) {
2898 mClassOfService = inCos;
2899 LOG(("HttpChannelChild %p ClassOfService flags=%lu inc=%d", this,
2900 mClassOfService.Flags(), mClassOfService.Incremental()));
2901 if (RemoteChannelExists()) {
2902 SendSetClassOfService(mClassOfService);
2904 return NS_OK;
2906 NS_IMETHODIMP
2907 HttpChannelChild::SetIncremental(bool inIncremental) {
2908 mClassOfService.SetIncremental(inIncremental);
2909 LOG(("HttpChannelChild %p ClassOfService flags=%lu inc=%d", this,
2910 mClassOfService.Flags(), mClassOfService.Incremental()));
2911 if (RemoteChannelExists()) {
2912 SendSetClassOfService(mClassOfService);
2914 return NS_OK;
2917 //-----------------------------------------------------------------------------
2918 // HttpChannelChild::nsIProxiedChannel
2919 //-----------------------------------------------------------------------------
2921 NS_IMETHODIMP
2922 HttpChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) { DROP_DEAD(); }
2924 NS_IMETHODIMP HttpChannelChild::GetHttpProxyConnectResponseCode(
2925 int32_t* aResponseCode) {
2926 DROP_DEAD();
2929 //-----------------------------------------------------------------------------
2930 // HttpChannelChild::nsIHttpChannelChild
2931 //-----------------------------------------------------------------------------
2933 NS_IMETHODIMP HttpChannelChild::AddCookiesToRequest() {
2934 HttpBaseChannel::AddCookiesToRequest();
2935 return NS_OK;
2938 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(
2939 RequestHeaderTuples** aRequestHeaders) {
2940 *aRequestHeaders = &mClientSetRequestHeaders;
2941 return NS_OK;
2944 void HttpChannelChild::GetClientSetCorsPreflightParameters(
2945 Maybe<CorsPreflightArgs>& aArgs) {
2946 if (LoadRequireCORSPreflight()) {
2947 CorsPreflightArgs args;
2948 args.unsafeHeaders() = mUnsafeHeaders.Clone();
2949 aArgs.emplace(args);
2950 } else {
2951 aArgs = Nothing();
2955 NS_IMETHODIMP
2956 HttpChannelChild::RemoveCorsPreflightCacheEntry(
2957 nsIURI* aURI, nsIPrincipal* aPrincipal,
2958 const OriginAttributes& aOriginAttributes) {
2959 PrincipalInfo principalInfo;
2960 MOZ_ASSERT(aURI, "aURI should not be null");
2961 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
2962 if (NS_WARN_IF(NS_FAILED(rv))) {
2963 return rv;
2965 bool result = false;
2966 // Be careful to not attempt to send a message to the parent after the
2967 // actor has been destroyed.
2968 if (CanSend()) {
2969 result = SendRemoveCorsPreflightCacheEntry(aURI, principalInfo,
2970 aOriginAttributes);
2972 return result ? NS_OK : NS_ERROR_FAILURE;
2975 //-----------------------------------------------------------------------------
2976 // HttpChannelChild::nsIMuliPartChannel
2977 //-----------------------------------------------------------------------------
2979 NS_IMETHODIMP
2980 HttpChannelChild::GetBaseChannel(nsIChannel** aBaseChannel) {
2981 if (!mMultiPartID) {
2982 MOZ_ASSERT(false, "Not a multipart channel");
2983 return NS_ERROR_NOT_AVAILABLE;
2985 nsCOMPtr<nsIChannel> channel = this;
2986 channel.forget(aBaseChannel);
2987 return NS_OK;
2990 NS_IMETHODIMP
2991 HttpChannelChild::GetPartID(uint32_t* aPartID) {
2992 if (!mMultiPartID) {
2993 MOZ_ASSERT(false, "Not a multipart channel");
2994 return NS_ERROR_NOT_AVAILABLE;
2996 *aPartID = *mMultiPartID;
2997 return NS_OK;
3000 NS_IMETHODIMP
3001 HttpChannelChild::GetIsFirstPart(bool* aIsFirstPart) {
3002 if (!mMultiPartID) {
3003 return NS_ERROR_NOT_AVAILABLE;
3005 *aIsFirstPart = mIsFirstPartOfMultiPart;
3006 return NS_OK;
3009 NS_IMETHODIMP
3010 HttpChannelChild::GetIsLastPart(bool* aIsLastPart) {
3011 if (!mMultiPartID) {
3012 return NS_ERROR_NOT_AVAILABLE;
3014 *aIsLastPart = mIsLastPartOfMultiPart;
3015 return NS_OK;
3018 //-----------------------------------------------------------------------------
3019 // HttpChannelChild::nsIThreadRetargetableRequest
3020 //-----------------------------------------------------------------------------
3022 NS_IMETHODIMP
3023 HttpChannelChild::RetargetDeliveryTo(nsISerialEventTarget* aNewTarget) {
3024 LOG(("HttpChannelChild::RetargetDeliveryTo [this=%p, aNewTarget=%p]", this,
3025 aNewTarget));
3026 MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
3027 MOZ_ASSERT(aNewTarget);
3029 NS_ENSURE_ARG(aNewTarget);
3030 if (aNewTarget->IsOnCurrentThread()) {
3031 NS_WARNING("Retargeting delivery to same thread");
3032 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::successMainThread;
3033 return NS_OK;
3036 if (mMultiPartID) {
3037 // TODO: Maybe add a new label for this? Maybe it doesn't
3038 // matter though, since we also blocked QI, so we shouldn't
3039 // ever get here.
3040 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
3041 return NS_ERROR_NO_INTERFACE;
3044 // Ensure that |mListener| and any subsequent listeners can be retargeted
3045 // to another thread.
3046 nsresult rv = NS_OK;
3047 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
3048 do_QueryInterface(mListener, &rv);
3049 if (!retargetableListener || NS_FAILED(rv)) {
3050 NS_WARNING("Listener is not retargetable");
3051 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListener;
3052 return NS_ERROR_NO_INTERFACE;
3055 rv = retargetableListener->CheckListenerChain();
3056 if (NS_FAILED(rv)) {
3057 NS_WARNING("Subsequent listeners are not retargetable");
3058 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::failListenerChain;
3059 return rv;
3063 MutexAutoLock lock(mEventTargetMutex);
3064 MOZ_ASSERT(!mODATarget);
3065 mODATarget = aNewTarget;
3068 mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::success;
3069 return NS_OK;
3072 NS_IMETHODIMP
3073 HttpChannelChild::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
3074 MutexAutoLock lock(mEventTargetMutex);
3076 nsCOMPtr<nsISerialEventTarget> target = mODATarget;
3077 if (!mODATarget) {
3078 target = GetCurrentSerialEventTarget();
3080 target.forget(aEventTarget);
3081 return NS_OK;
3084 void HttpChannelChild::TrySendDeletingChannel() {
3085 AUTO_PROFILER_LABEL("HttpChannelChild::TrySendDeletingChannel", NETWORK);
3086 if (!mDeletingChannelSent.compareExchange(false, true)) {
3087 // SendDeletingChannel is already sent.
3088 return;
3091 if (NS_IsMainThread()) {
3092 if (NS_WARN_IF(!CanSend())) {
3093 // IPC actor is destroyed already, do not send more messages.
3094 return;
3097 Unused << PHttpChannelChild::SendDeletingChannel();
3098 return;
3101 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
3102 MOZ_ASSERT(neckoTarget);
3104 DebugOnly<nsresult> rv = neckoTarget->Dispatch(
3105 NewNonOwningRunnableMethod(
3106 "net::HttpChannelChild::TrySendDeletingChannel", this,
3107 &HttpChannelChild::TrySendDeletingChannel),
3108 NS_DISPATCH_NORMAL);
3109 MOZ_ASSERT(NS_SUCCEEDED(rv));
3112 nsresult HttpChannelChild::AsyncCallImpl(
3113 void (HttpChannelChild::*funcPtr)(),
3114 nsRunnableMethod<HttpChannelChild>** retval) {
3115 nsresult rv;
3117 RefPtr<nsRunnableMethod<HttpChannelChild>> event =
3118 NewRunnableMethod("net::HttpChannelChild::AsyncCall", this, funcPtr);
3119 nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
3120 MOZ_ASSERT(neckoTarget);
3122 rv = neckoTarget->Dispatch(event, NS_DISPATCH_NORMAL);
3124 if (NS_SUCCEEDED(rv) && retval) {
3125 *retval = event;
3128 return rv;
3131 nsresult HttpChannelChild::SetReferrerHeader(const nsACString& aReferrer,
3132 bool aRespectBeforeConnect) {
3133 // Normally this would be ENSURE_CALLED_BEFORE_CONNECT, but since the
3134 // "connect" is done in the main process, and LoadRequestObserversCalled() is
3135 // never set in the ChannelChild, before connect basically means before
3136 // asyncOpen.
3137 if (aRespectBeforeConnect) {
3138 ENSURE_CALLED_BEFORE_ASYNC_OPEN();
3141 // remove old referrer if any
3142 mClientSetRequestHeaders.RemoveElementsBy(
3143 [](const auto& header) { return "Referer"_ns.Equals(header.mHeader); });
3145 return HttpBaseChannel::SetReferrerHeader(aReferrer, aRespectBeforeConnect);
3148 void HttpChannelChild::CancelOnMainThread(nsresult aRv,
3149 const nsACString& aReason) {
3150 LOG(("HttpChannelChild::CancelOnMainThread [this=%p]", this));
3152 if (NS_IsMainThread()) {
3153 CancelWithReason(aRv, aReason);
3154 return;
3157 mEventQ->Suspend();
3158 // Cancel is expected to preempt any other channel events, thus we put this
3159 // event in the front of mEventQ to make sure nsIStreamListener not receiving
3160 // any ODA/OnStopRequest callbacks.
3161 nsCString reason(aReason);
3162 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
3163 this, [self = UnsafePtr<HttpChannelChild>(this), aRv, reason]() {
3164 self->CancelWithReason(aRv, reason);
3165 }));
3166 mEventQ->Resume();
3169 mozilla::ipc::IPCResult HttpChannelChild::RecvSetPriority(
3170 const int16_t& aPriority) {
3171 mPriority = aPriority;
3172 return IPC_OK();
3175 // We don't have a copyable Endpoint and NeckoTargetChannelFunctionEvent takes
3176 // std::function<void()>. It's not possible to avoid the copy from the type of
3177 // lambda to std::function, so does the capture list. Hence, we're forced to
3178 // use the old-fashioned channel event inheritance.
3179 class AttachStreamFilterEvent : public ChannelEvent {
3180 public:
3181 AttachStreamFilterEvent(HttpChannelChild* aChild,
3182 already_AddRefed<nsIEventTarget> aTarget,
3183 Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
3184 : mChild(aChild), mTarget(aTarget), mEndpoint(std::move(aEndpoint)) {}
3186 already_AddRefed<nsIEventTarget> GetEventTarget() override {
3187 nsCOMPtr<nsIEventTarget> target = mTarget;
3188 return target.forget();
3191 void Run() override {
3192 extensions::StreamFilterParent::Attach(mChild, std::move(mEndpoint));
3195 private:
3196 HttpChannelChild* mChild;
3197 nsCOMPtr<nsIEventTarget> mTarget;
3198 Endpoint<extensions::PStreamFilterParent> mEndpoint;
3201 void HttpChannelChild::RegisterStreamFilter(
3202 RefPtr<extensions::StreamFilterParent>& aStreamFilter) {
3203 MOZ_ASSERT(NS_IsMainThread());
3204 mStreamFilters.AppendElement(aStreamFilter);
3207 void HttpChannelChild::ProcessAttachStreamFilter(
3208 Endpoint<extensions::PStreamFilterParent>&& aEndpoint) {
3209 LOG(("HttpChannelChild::ProcessAttachStreamFilter [this=%p]\n", this));
3210 MOZ_ASSERT(OnSocketThread());
3212 mEventQ->RunOrEnqueue(new AttachStreamFilterEvent(this, GetNeckoTarget(),
3213 std::move(aEndpoint)));
3216 void HttpChannelChild::OnDetachStreamFilters() {
3217 LOG(("HttpChannelChild::OnDetachStreamFilters [this=%p]\n", this));
3218 MOZ_ASSERT(NS_IsMainThread());
3219 for (auto& StreamFilter : mStreamFilters) {
3220 StreamFilter->Disconnect("ServiceWorker fallback redirection"_ns);
3222 mStreamFilters.Clear();
3225 void HttpChannelChild::ProcessDetachStreamFilters() {
3226 LOG(("HttpChannelChild::ProcessDetachStreamFilter [this=%p]\n", this));
3227 MOZ_ASSERT(OnSocketThread());
3229 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
3230 this, [self = UnsafePtr<HttpChannelChild>(this)]() {
3231 self->OnDetachStreamFilters();
3232 }));
3235 void HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
3236 MOZ_ASSERT(NS_IsMainThread());
3238 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3239 mActorDestroyReason.emplace(aWhy);
3240 #endif
3242 // OnStartRequest might be dropped if IPDL is destroyed abnormally
3243 // and BackgroundChild might have pending IPC messages.
3244 // Clean up BackgroundChild at this time to prevent memleak.
3245 if (aWhy != Deletion) {
3246 // Make sure all the messages are processed.
3247 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
3249 mStatus = NS_ERROR_DOCSHELL_DYING;
3250 HandleAsyncAbort();
3252 // Cleanup the background channel before we resume the eventQ so we don't
3253 // get any other events.
3254 CleanupBackgroundChannel();
3256 mIPCActorDeleted = true;
3257 mCanceled = true;
3261 mozilla::ipc::IPCResult HttpChannelChild::RecvLogBlockedCORSRequest(
3262 const nsAString& aMessage, const nsACString& aCategory,
3263 const bool& aIsWarning) {
3264 Unused << LogBlockedCORSRequest(aMessage, aCategory, aIsWarning);
3265 return IPC_OK();
3268 NS_IMETHODIMP
3269 HttpChannelChild::LogBlockedCORSRequest(const nsAString& aMessage,
3270 const nsACString& aCategory,
3271 bool aIsWarning) {
3272 uint64_t innerWindowID = mLoadInfo->GetInnerWindowID();
3273 bool privateBrowsing = !!mLoadInfo->GetOriginAttributes().mPrivateBrowsingId;
3274 bool fromChromeContext =
3275 mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal();
3276 nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID, privateBrowsing,
3277 fromChromeContext, aMessage,
3278 aCategory, aIsWarning);
3279 return NS_OK;
3282 mozilla::ipc::IPCResult HttpChannelChild::RecvLogMimeTypeMismatch(
3283 const nsACString& aMessageName, const bool& aWarning, const nsAString& aURL,
3284 const nsAString& aContentType) {
3285 Unused << LogMimeTypeMismatch(aMessageName, aWarning, aURL, aContentType);
3286 return IPC_OK();
3289 NS_IMETHODIMP
3290 HttpChannelChild::LogMimeTypeMismatch(const nsACString& aMessageName,
3291 bool aWarning, const nsAString& aURL,
3292 const nsAString& aContentType) {
3293 RefPtr<Document> doc;
3294 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
3296 AutoTArray<nsString, 2> params;
3297 params.AppendElement(aURL);
3298 params.AppendElement(aContentType);
3299 nsContentUtils::ReportToConsole(
3300 aWarning ? nsIScriptError::warningFlag : nsIScriptError::errorFlag,
3301 "MIMEMISMATCH"_ns, doc, nsContentUtils::eSECURITY_PROPERTIES,
3302 nsCString(aMessageName).get(), params);
3303 return NS_OK;
3306 nsresult HttpChannelChild::MaybeLogCOEPError(nsresult aStatus) {
3307 if (aStatus == NS_ERROR_DOM_CORP_FAILED) {
3308 RefPtr<Document> doc;
3309 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
3311 nsAutoCString url;
3312 mURI->GetSpec(url);
3314 AutoTArray<nsString, 2> params;
3315 params.AppendElement(NS_ConvertUTF8toUTF16(url));
3316 // The MDN URL intentionally ends with a # so the webconsole linkification
3317 // doesn't ignore the final ) of the URL
3318 params.AppendElement(
3319 u"https://developer.mozilla.org/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)#"_ns);
3320 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "COEP"_ns, doc,
3321 nsContentUtils::eNECKO_PROPERTIES,
3322 "CORPBlocked", params);
3325 return NS_OK;
3328 nsresult HttpChannelChild::CrossProcessRedirectFinished(nsresult aStatus) {
3329 if (!CanSend()) {
3330 return NS_BINDING_FAILED;
3333 if (!mCanceled && NS_SUCCEEDED(mStatus)) {
3334 mStatus = aStatus;
3337 return mStatus;
3340 void HttpChannelChild::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
3341 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3342 mDoDiagnosticAssertWhenOnStopNotCalledOnDestroy = true;
3343 #endif
3346 void HttpChannelChild::MaybeConnectToSocketProcess() {
3347 if (!nsIOService::UseSocketProcess()) {
3348 return;
3351 if (!StaticPrefs::network_send_ODA_to_content_directly()) {
3352 return;
3355 RefPtr<HttpBackgroundChannelChild> bgChild;
3357 MutexAutoLock lock(mBgChildMutex);
3358 bgChild = mBgChild;
3360 SocketProcessBridgeChild::GetSocketProcessBridge()->Then(
3361 GetCurrentSerialEventTarget(), __func__,
3362 [bgChild, channelId = ChannelId()](
3363 const RefPtr<SocketProcessBridgeChild>& aBridge) {
3364 Endpoint<PBackgroundDataBridgeParent> parentEndpoint;
3365 Endpoint<PBackgroundDataBridgeChild> childEndpoint;
3366 PBackgroundDataBridge::CreateEndpoints(&parentEndpoint, &childEndpoint);
3367 aBridge->SendInitBackgroundDataBridge(std::move(parentEndpoint),
3368 channelId);
3370 gSocketTransportService->Dispatch(
3371 NS_NewRunnableFunction(
3372 "HttpBackgroundChannelChild::CreateDataBridge",
3373 [bgChild, endpoint = std::move(childEndpoint)]() mutable {
3374 bgChild->CreateDataBridge(std::move(endpoint));
3376 NS_DISPATCH_NORMAL);
3378 []() { NS_WARNING("Failed to create SocketProcessBridgeChild"); });
3381 NS_IMETHODIMP
3382 HttpChannelChild::SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) {
3383 return NS_OK;
3386 NS_IMETHODIMP HttpChannelChild::SetWebTransportSessionEventListener(
3387 WebTransportSessionEventListener* aListener) {
3388 return NS_OK;
3391 } // namespace mozilla::net