tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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