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