tor-browser

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

InterceptedHttpChannel.cpp (53900B)


      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 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 *  License, v. 2.0. If a copy of the MPL was not distributed with this
      5 *  file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "InterceptedHttpChannel.h"
      8 #include "NetworkMarker.h"
      9 #include "nsContentSecurityManager.h"
     10 #include "nsEscape.h"
     11 #include "mozilla/SchedulerGroup.h"
     12 #include "mozilla/ScopeExit.h"
     13 #include "mozilla/dom/ChannelInfo.h"
     14 #include "mozilla/dom/PerformanceStorage.h"
     15 #include "mozilla/glean/DomServiceworkersMetrics.h"
     16 #include "nsHttpChannel.h"
     17 #include "nsIHttpHeaderVisitor.h"
     18 #include "nsIRedirectResultListener.h"
     19 #include "nsStringStream.h"
     20 #include "nsStreamUtils.h"
     21 #include "nsQueryObject.h"
     22 #include "mozilla/Logging.h"
     23 
     24 namespace mozilla::net {
     25 
     26 mozilla::LazyLogModule gInterceptedLog("Intercepted");
     27 
     28 #define INTERCEPTED_LOG(args) MOZ_LOG(gInterceptedLog, LogLevel::Debug, args)
     29 
     30 NS_IMPL_ISUPPORTS_INHERITED(InterceptedHttpChannel, HttpBaseChannel,
     31                            nsIInterceptedChannel, nsICacheInfoChannel,
     32                            nsIAsyncVerifyRedirectCallback, nsIRequestObserver,
     33                            nsIStreamListener, nsIThreadRetargetableRequest,
     34                            nsIThreadRetargetableStreamListener,
     35                            nsIClassOfService)
     36 
     37 InterceptedHttpChannel::InterceptedHttpChannel(
     38    PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
     39    const TimeStamp& aAsyncOpenTimestamp)
     40    : HttpAsyncAborter<InterceptedHttpChannel>(this),
     41      mProgress(0),
     42      mProgressReported(0),
     43      mSynthesizedStreamLength(-1),
     44      mResumeStartPos(0),
     45      mCallingStatusAndProgress(false) {
     46  // Pre-set the creation and AsyncOpen times based on the original channel
     47  // we are intercepting.  We don't want our extra internal redirect to mask
     48  // any time spent processing the channel.
     49  INTERCEPTED_LOG(("Creating InterceptedHttpChannel [%p]", this));
     50  mChannelCreationTime = aCreationTime;
     51  mChannelCreationTimestamp = aCreationTimestamp;
     52  mInterceptedChannelCreationTimestamp = TimeStamp::Now();
     53  mAsyncOpenTime = aAsyncOpenTimestamp;
     54 }
     55 
     56 void InterceptedHttpChannel::ReleaseListeners() {
     57  if (mLoadGroup) {
     58    mLoadGroup->RemoveRequest(this, nullptr, mStatus);
     59  }
     60  HttpBaseChannel::ReleaseListeners();
     61  mSynthesizedResponseHead.reset();
     62  mRedirectChannel = nullptr;
     63  mBodyReader = nullptr;
     64  mReleaseHandle = nullptr;
     65  mProgressSink = nullptr;
     66  mBodyCallback = nullptr;
     67  mPump = nullptr;
     68 
     69  MOZ_DIAGNOSTIC_ASSERT(!LoadIsPending());
     70 }
     71 
     72 nsresult InterceptedHttpChannel::SetupReplacementChannel(
     73    nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
     74    uint32_t aRedirectFlags) {
     75  INTERCEPTED_LOG(
     76      ("InterceptedHttpChannel::SetupReplacementChannel [%p] flag: %u", this,
     77       aRedirectFlags));
     78  nsresult rv = HttpBaseChannel::SetupReplacementChannel(
     79      aURI, aChannel, aPreserveMethod, aRedirectFlags);
     80  if (NS_FAILED(rv)) {
     81    return rv;
     82  }
     83 
     84  rv = CheckRedirectLimit(aURI, aRedirectFlags);
     85  NS_ENSURE_SUCCESS(rv, rv);
     86 
     87  // While we can't resume an synthetic response, we can still propagate
     88  // the resume params across redirects for other channels to handle.
     89  if (mResumeStartPos > 0) {
     90    nsCOMPtr<nsIResumableChannel> resumable = do_QueryInterface(aChannel);
     91    if (!resumable) {
     92      return NS_ERROR_NOT_RESUMABLE;
     93    }
     94 
     95    resumable->ResumeAt(mResumeStartPos, mResumeEntityId);
     96  }
     97 
     98  return NS_OK;
     99 }
    100 
    101 void InterceptedHttpChannel::AsyncOpenInternal() {
    102  // We save this timestamp from outside of the if block in case we enable the
    103  // profiler after AsyncOpen().
    104  INTERCEPTED_LOG(("InterceptedHttpChannel::AsyncOpenInternal [%p]", this));
    105  mLastStatusReported = TimeStamp::Now();
    106  if (profiler_thread_is_being_profiled_for_markers()) {
    107    nsAutoCString requestMethod;
    108    GetRequestMethod(requestMethod);
    109 
    110    profiler_add_network_marker(
    111        mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
    112        mChannelCreationTimestamp, mLastStatusReported, 0, kCacheUnknown,
    113        mLoadInfo->GetInnerWindowID(),
    114        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus);
    115  }
    116 
    117  // If an error occurs in this file we must ensure mListener callbacks are
    118  // invoked in some way.  We either Cancel() or ResetInterception below
    119  // depending on which path we take.
    120  nsresult rv = NS_OK;
    121 
    122  // Start the interception, record the start time.
    123  mTimeStamps.Init(this);
    124  mTimeStamps.RecordTime();
    125 
    126  // We should have pre-set the AsyncOpen time based on the original channel if
    127  // timings are enabled.
    128  MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull());
    129 
    130  StoreIsPending(true);
    131  StoreResponseCouldBeSynthesized(true);
    132 
    133  if (mLoadGroup) {
    134    mLoadGroup->AddRequest(this, nullptr);
    135  }
    136 
    137  // If we already have a synthesized body then we are pre-synthesized.
    138  // This can happen for two reasons:
    139  //  1. We have a pre-synthesized redirect in e10s mode.  In this case
    140  //     we should follow the redirect.
    141  //  2. We are handling a "fake" redirect for an opaque response.  Here
    142  //     we should just process the synthetic body.
    143  if (mBodyReader) {
    144    // If we fail in this path, then cancel the channel.  We don't want
    145    // to ResetInterception() after a synthetic result has already been
    146    // produced by the ServiceWorker.
    147    auto autoCancel = MakeScopeExit([&] {
    148      if (NS_FAILED(rv)) {
    149        Cancel(rv);
    150      }
    151    });
    152 
    153    // The fetch event will not be dispatched, record current time for
    154    // FetchHandlerStart and FetchHandlerFinish.
    155    SetFetchHandlerStart(TimeStamp::Now());
    156    SetFetchHandlerFinish(TimeStamp::Now());
    157 
    158    if (ShouldRedirect()) {
    159      rv = FollowSyntheticRedirect();
    160      return;
    161    }
    162 
    163    rv = StartPump();
    164    return;
    165  }
    166 
    167  // If we fail the initial interception, then attempt to ResetInterception
    168  // to fall back to network.  We only cancel if the reset fails.
    169  auto autoReset = MakeScopeExit([&] {
    170    if (NS_FAILED(rv)) {
    171      rv = ResetInterception(false);
    172      if (NS_WARN_IF(NS_FAILED(rv))) {
    173        Cancel(rv);
    174      }
    175    }
    176  });
    177 
    178  // Otherwise we need to trigger a FetchEvent in a ServiceWorker.
    179  nsCOMPtr<nsINetworkInterceptController> controller;
    180  GetCallback(controller);
    181 
    182  if (NS_WARN_IF(!controller)) {
    183    rv = NS_ERROR_DOM_INVALID_STATE_ERR;
    184    return;
    185  }
    186 
    187  rv = controller->ChannelIntercepted(this);
    188  NS_ENSURE_SUCCESS_VOID(rv);
    189 }
    190 
    191 bool InterceptedHttpChannel::ShouldRedirect() const {
    192  // Determine if the synthetic response requires us to perform a real redirect.
    193  return nsHttpChannel::WillRedirect(*mResponseHead) &&
    194         !mLoadInfo->GetDontFollowRedirects();
    195 }
    196 
    197 nsresult InterceptedHttpChannel::FollowSyntheticRedirect() {
    198  // Perform a real redirect based on the synthetic response.
    199 
    200  nsCOMPtr<nsIIOService> ioService;
    201  nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    202  NS_ENSURE_SUCCESS(rv, rv);
    203 
    204  nsAutoCString location;
    205  rv = mResponseHead->GetHeader(nsHttp::Location, location);
    206  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    207 
    208  // make sure non-ASCII characters in the location header are escaped.
    209  nsAutoCString locationBuf;
    210  if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
    211                   locationBuf)) {
    212    location = locationBuf;
    213  }
    214 
    215  nsCOMPtr<nsIURI> redirectURI;
    216  rv = ioService->NewURI(nsDependentCString(location.get()), nullptr, mURI,
    217                         getter_AddRefs(redirectURI));
    218  NS_ENSURE_SUCCESS(rv, NS_ERROR_CORRUPTED_CONTENT);
    219 
    220  uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
    221  if (nsHttp::IsPermanentRedirect(mResponseHead->Status())) {
    222    redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
    223  }
    224 
    225  PropagateReferenceIfNeeded(mURI, redirectURI);
    226 
    227  bool rewriteToGET = ShouldRewriteRedirectToGET(mResponseHead->Status(),
    228                                                 mRequestHead.ParsedMethod());
    229 
    230  nsCOMPtr<nsIChannel> newChannel;
    231  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
    232      CloneLoadInfoForRedirect(redirectURI, redirectFlags);
    233  rv = NS_NewChannelInternal(getter_AddRefs(newChannel), redirectURI,
    234                             redirectLoadInfo,
    235                             nullptr,  // PerformanceStorage
    236                             nullptr,  // aLoadGroup
    237                             nullptr,  // aCallbacks
    238                             mLoadFlags, ioService);
    239  NS_ENSURE_SUCCESS(rv, rv);
    240 
    241  rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET,
    242                               redirectFlags);
    243  NS_ENSURE_SUCCESS(rv, rv);
    244 
    245  mRedirectChannel = std::move(newChannel);
    246 
    247  rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel,
    248                                            redirectFlags);
    249 
    250  if (NS_WARN_IF(NS_FAILED(rv))) {
    251    OnRedirectVerifyCallback(rv);
    252  } else {
    253    // Redirect success, record the finish time and the final status.
    254    mTimeStamps.RecordTime(InterceptionTimeStamps::Redirected);
    255  }
    256 
    257  return rv;
    258 }
    259 
    260 nsresult InterceptedHttpChannel::RedirectForResponseURL(
    261    nsIURI* aResponseURI, bool aResponseRedirected) {
    262  // Perform a service worker redirect to another InterceptedHttpChannel using
    263  // the given response URL. It allows content to see the final URL where
    264  // appropriate and also helps us enforce cross-origin restrictions. The
    265  // resulting channel will then process the synthetic response as normal. This
    266  // extra redirect is performed so that listeners treat the result as unsafe
    267  // cross-origin data.
    268 
    269  nsresult rv = NS_OK;
    270 
    271  // We want to pass ownership of the body callback to the new synthesized
    272  // channel.  We need to hold a reference to the callbacks on the stack
    273  // as well, though, so we can call them if a failure occurs.
    274  nsCOMPtr<nsIInterceptedBodyCallback> bodyCallback = std::move(mBodyCallback);
    275 
    276  RefPtr<InterceptedHttpChannel> newChannel = CreateForSynthesis(
    277      mResponseHead.get(), mBodyReader, bodyCallback, mChannelCreationTime,
    278      mChannelCreationTimestamp, mAsyncOpenTime);
    279 
    280  // If the response has been redirected, propagate all the URLs to content.
    281  // Thus, the exact value of the redirect flag does not matter as long as it's
    282  // not REDIRECT_INTERNAL.
    283  uint32_t flags = aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY
    284                                       : nsIChannelEventSink::REDIRECT_INTERNAL;
    285 
    286  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
    287      CloneLoadInfoForRedirect(aResponseURI, flags);
    288 
    289  rv = newChannel->Init(
    290      aResponseURI, mCaps, static_cast<nsProxyInfo*>(mProxyInfo.get()),
    291      mProxyResolveFlags, mProxyURI, mChannelId, redirectLoadInfo);
    292  NS_ENSURE_SUCCESS(rv, rv);
    293 
    294  // Normally we don't propagate the LoadInfo's service worker tainting
    295  // synthesis flag on redirect.  A real redirect normally will want to allow
    296  // normal tainting to proceed from its starting taint.  For this particular
    297  // redirect, though, we are performing a redirect to communicate the URL of
    298  // the service worker synthetic response itself.  This redirect still
    299  // represents the synthetic response, so we must preserve the flag.
    300  if (redirectLoadInfo && mLoadInfo &&
    301      mLoadInfo->GetServiceWorkerTaintingSynthesized()) {
    302    redirectLoadInfo->SynthesizeServiceWorkerTainting(mLoadInfo->GetTainting());
    303  }
    304 
    305  rv = SetupReplacementChannel(aResponseURI, newChannel, true, flags);
    306  NS_ENSURE_SUCCESS(rv, rv);
    307 
    308  mRedirectChannel = newChannel;
    309 
    310  MOZ_ASSERT(mBodyReader);
    311  MOZ_ASSERT(!LoadApplyConversion());
    312  newChannel->SetApplyConversion(false);
    313 
    314  rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
    315 
    316  if (NS_FAILED(rv)) {
    317    // Make sure to call the body callback since we took ownership
    318    // above.  Neither the new channel or our standard
    319    // OnRedirectVerifyCallback() code will invoke the callback.  Do it here.
    320    bodyCallback->BodyComplete(rv);
    321 
    322    OnRedirectVerifyCallback(rv);
    323  }
    324 
    325  return rv;
    326 }
    327 
    328 nsresult InterceptedHttpChannel::StartPump() {
    329  MOZ_DIAGNOSTIC_ASSERT(!mPump);
    330  MOZ_DIAGNOSTIC_ASSERT(mBodyReader);
    331 
    332  // We don't support resuming an intercepted channel.  We can't guarantee the
    333  // ServiceWorker will always return the same data and we can't rely on the
    334  // http cache code to detect changes.  For now, just force the channel to
    335  // NS_ERROR_NOT_RESUMABLE which should cause the front-end to recreate the
    336  // channel without calling ResumeAt().
    337  //
    338  // It would also be possible to convert this information to a range request,
    339  // but its unclear if we should do that for ServiceWorker FetchEvents.  See:
    340  //
    341  //  https://github.com/w3c/ServiceWorker/issues/1201
    342  if (mResumeStartPos > 0) {
    343    return NS_ERROR_NOT_RESUMABLE;
    344  }
    345 
    346  // For progress we trust the content-length for the "maximum" size.
    347  // We can't determine the full size from the stream itself since
    348  // we may only receive the data incrementally.  We can't trust
    349  // Available() here.
    350  // TODO: We could implement an nsIFixedLengthInputStream interface and
    351  //       QI to it here.  This would let us determine the total length
    352  //       for streams that support it.  See bug 1388774.
    353  (void)GetContentLength(&mSynthesizedStreamLength);
    354 
    355  nsresult rv =
    356      nsInputStreamPump::Create(getter_AddRefs(mPump), mBodyReader, 0, 0, true);
    357  NS_ENSURE_SUCCESS(rv, rv);
    358 
    359  rv = mPump->AsyncRead(this);
    360  NS_ENSURE_SUCCESS(rv, rv);
    361 
    362  uint32_t suspendCount = mSuspendCount;
    363  while (suspendCount--) {
    364    mPump->Suspend();
    365  }
    366 
    367  MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
    368 
    369  return rv;
    370 }
    371 
    372 nsresult InterceptedHttpChannel::OpenRedirectChannel() {
    373  INTERCEPTED_LOG(
    374      ("InterceptedHttpChannel::OpenRedirectChannel [%p], mRedirectChannel: %p",
    375       this, mRedirectChannel.get()));
    376  nsresult rv = NS_OK;
    377 
    378  if (NS_FAILED(mStatus)) {
    379    return mStatus;
    380  }
    381 
    382  if (!mRedirectChannel) {
    383    return NS_ERROR_DOM_ABORT_ERR;
    384  }
    385 
    386  // Make sure to do this after we received redirect veto answer,
    387  // i.e. after all sinks had been notified
    388  mRedirectChannel->SetOriginalURI(mOriginalURI);
    389 
    390  // open new channel
    391  rv = mRedirectChannel->AsyncOpen(mListener);
    392  NS_ENSURE_SUCCESS(rv, rv);
    393 
    394  mStatus = NS_BINDING_REDIRECTED;
    395 
    396  return rv;
    397 }
    398 
    399 void InterceptedHttpChannel::MaybeCallStatusAndProgress() {
    400  // OnStatus() and OnProgress() must only be called on the main thread.  If
    401  // we are on a separate thread, then we maybe need to schedule a runnable
    402  // to call them asynchronousnly.
    403  if (!NS_IsMainThread()) {
    404    // Check to see if we are already trying to call OnStatus/OnProgress
    405    // asynchronously.  If we are, then don't queue up another runnable.
    406    // We don't want to flood the main thread.
    407    if (mCallingStatusAndProgress) {
    408      return;
    409    }
    410    mCallingStatusAndProgress = true;
    411 
    412    nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
    413        "InterceptedHttpChannel::MaybeCallStatusAndProgress", this,
    414        &InterceptedHttpChannel::MaybeCallStatusAndProgress);
    415    MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    416 
    417    return;
    418  }
    419 
    420  MOZ_ASSERT(NS_IsMainThread());
    421 
    422  // We are about to capture out progress position.  Clear the flag we use
    423  // to de-duplicate progress report runnables.  We want any further progress
    424  // updates to trigger another runnable.  We do this before capture the
    425  // progress value since we're using atomics and not a mutex lock.
    426  mCallingStatusAndProgress = false;
    427 
    428  // Capture the current status from our atomic count.
    429  int64_t progress = mProgress;
    430 
    431  MOZ_DIAGNOSTIC_ASSERT(progress >= mProgressReported);
    432 
    433  // Do nothing if we've already made the calls for this amount of progress
    434  // or if the channel is not configured for these calls.  Note, the check
    435  // for mProgressSink here means we will not fire any spurious late calls
    436  // after ReleaseListeners() is executed.
    437  if (progress <= mProgressReported || mCanceled || !mProgressSink ||
    438      (mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
    439    return;
    440  }
    441 
    442  // Capture the host name on the first set of calls to avoid doing this
    443  // string processing repeatedly.
    444  if (mProgressReported == 0) {
    445    nsAutoCString host;
    446    MOZ_ALWAYS_SUCCEEDS(mURI->GetHost(host));
    447    CopyUTF8toUTF16(host, mStatusHost);
    448  }
    449 
    450  mProgressSink->OnStatus(this, NS_NET_STATUS_READING, mStatusHost.get());
    451 
    452  mProgressSink->OnProgress(this, progress, mSynthesizedStreamLength);
    453 
    454  mProgressReported = progress;
    455 }
    456 
    457 void InterceptedHttpChannel::MaybeCallBodyCallback() {
    458  nsCOMPtr<nsIInterceptedBodyCallback> callback = std::move(mBodyCallback);
    459  if (callback) {
    460    callback->BodyComplete(mStatus);
    461  }
    462 }
    463 
    464 // static
    465 already_AddRefed<InterceptedHttpChannel>
    466 InterceptedHttpChannel::CreateForInterception(
    467    PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
    468    const TimeStamp& aAsyncOpenTimestamp) {
    469  // Create an InterceptedHttpChannel that will trigger a FetchEvent
    470  // in a ServiceWorker when opened.
    471  RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
    472      aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
    473 
    474  return ref.forget();
    475 }
    476 
    477 // static
    478 already_AddRefed<InterceptedHttpChannel>
    479 InterceptedHttpChannel::CreateForSynthesis(
    480    const nsHttpResponseHead* aHead, nsIInputStream* aBody,
    481    nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
    482    const TimeStamp& aCreationTimestamp, const TimeStamp& aAsyncOpenTimestamp) {
    483  MOZ_DIAGNOSTIC_ASSERT(aHead);
    484  MOZ_DIAGNOSTIC_ASSERT(aBody);
    485 
    486  // Create an InterceptedHttpChannel that already has a synthesized response.
    487  // The synthetic response will be processed when opened.  A FetchEvent
    488  // will not be triggered.
    489  RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
    490      aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
    491 
    492  ref->mResponseHead = MakeUnique<nsHttpResponseHead>(*aHead);
    493  ref->mBodyReader = aBody;
    494  ref->mBodyCallback = aBodyCallback;
    495 
    496  return ref.forget();
    497 }
    498 
    499 NS_IMETHODIMP InterceptedHttpChannel::SetCanceledReason(
    500    const nsACString& aReason) {
    501  return SetCanceledReasonImpl(aReason);
    502 }
    503 
    504 NS_IMETHODIMP InterceptedHttpChannel::GetCanceledReason(nsACString& aReason) {
    505  return GetCanceledReasonImpl(aReason);
    506 }
    507 
    508 NS_IMETHODIMP
    509 InterceptedHttpChannel::CancelWithReason(nsresult aStatus,
    510                                         const nsACString& aReason) {
    511  return CancelWithReasonImpl(aStatus, aReason);
    512 }
    513 
    514 NS_IMETHODIMP
    515 InterceptedHttpChannel::Cancel(nsresult aStatus) {
    516  INTERCEPTED_LOG(("InterceptedHttpChannel::Cancel [%p]", this));
    517  // Note: This class has been designed to send all error results through
    518  //       Cancel().  Don't add calls directly to AsyncAbort() or
    519  //       DoNotifyListener().  Instead call Cancel().
    520 
    521  if (mCanceled) {
    522    return NS_OK;
    523  }
    524 
    525  // The interception is canceled, record the finish time stamp and the final
    526  // status
    527  mTimeStamps.RecordTime(InterceptionTimeStamps::Canceled);
    528 
    529  mCanceled = true;
    530 
    531  if (mLastStatusReported && profiler_thread_is_being_profiled_for_markers()) {
    532    // These do allocations/frees/etc; avoid if not active
    533    // mLastStatusReported can be null if Cancel is called before we added the
    534    // start marker.
    535    nsAutoCString requestMethod;
    536    GetRequestMethod(requestMethod);
    537 
    538    int32_t priority = PRIORITY_NORMAL;
    539    GetPriority(&priority);
    540 
    541    uint64_t size = 0;
    542    GetEncodedBodySize(&size);
    543 
    544    profiler_add_network_marker(
    545        mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_CANCEL,
    546        mLastStatusReported, TimeStamp::Now(), size, kCacheUnknown,
    547        mLoadInfo->GetInnerWindowID(),
    548        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
    549        &mTransactionTimings, std::move(mSource));
    550  }
    551 
    552  MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus));
    553  if (NS_SUCCEEDED(mStatus)) {
    554    mStatus = aStatus;
    555  }
    556 
    557  if (mPump) {
    558    return mPump->Cancel(mStatus);
    559  }
    560 
    561  return AsyncAbort(mStatus);
    562 }
    563 
    564 NS_IMETHODIMP
    565 InterceptedHttpChannel::Suspend(void) {
    566  ++mSuspendCount;
    567  if (mPump) {
    568    return mPump->Suspend();
    569  }
    570  return NS_OK;
    571 }
    572 
    573 NS_IMETHODIMP
    574 InterceptedHttpChannel::Resume(void) {
    575  --mSuspendCount;
    576  if (mPump) {
    577    return mPump->Resume();
    578  }
    579  return NS_OK;
    580 }
    581 
    582 NS_IMETHODIMP
    583 InterceptedHttpChannel::GetSecurityInfo(
    584    nsITransportSecurityInfo** aSecurityInfo) {
    585  nsCOMPtr<nsITransportSecurityInfo> ref(mSecurityInfo);
    586  ref.forget(aSecurityInfo);
    587  return NS_OK;
    588 }
    589 
    590 NS_IMETHODIMP
    591 InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
    592  INTERCEPTED_LOG(("InterceptedHttpChannel::AsyncOpen [%p], listener: %p", this,
    593                   aListener));
    594  nsCOMPtr<nsIStreamListener> listener(aListener);
    595 
    596  nsresult rv =
    597      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    598  if (NS_WARN_IF(NS_FAILED(rv))) {
    599    Cancel(rv);
    600    return rv;
    601  }
    602  if (mCanceled) {
    603    return mStatus;
    604  }
    605 
    606  // After this point we should try to return NS_OK and notify the listener
    607  // of the result.
    608  mListener = aListener;
    609 
    610  AsyncOpenInternal();
    611 
    612  return NS_OK;
    613 }
    614 
    615 NS_IMETHODIMP
    616 InterceptedHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
    617                                              const nsACString& aCategory,
    618                                              bool aIsWarning) {
    619  // Synthetic responses should not trigger CORS blocking.
    620  return NS_ERROR_NOT_IMPLEMENTED;
    621 }
    622 
    623 NS_IMETHODIMP
    624 InterceptedHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
    625                                            bool aWarning,
    626                                            const nsAString& aURL,
    627                                            const nsAString& aContentType) {
    628  return NS_ERROR_NOT_IMPLEMENTED;
    629 }
    630 
    631 NS_IMETHODIMP
    632 InterceptedHttpChannel::GetIsAuthChannel(bool* aIsAuthChannel) {
    633  return NS_ERROR_NOT_IMPLEMENTED;
    634 }
    635 
    636 NS_IMETHODIMP
    637 InterceptedHttpChannel::SetPriority(int32_t aPriority) {
    638  mPriority = std::clamp<int32_t>(aPriority, INT16_MIN, INT16_MAX);
    639  return NS_OK;
    640 }
    641 
    642 NS_IMETHODIMP
    643 InterceptedHttpChannel::SetClassFlags(uint32_t aClassFlags) {
    644  mClassOfService.SetFlags(aClassFlags);
    645  return NS_OK;
    646 }
    647 
    648 NS_IMETHODIMP
    649 InterceptedHttpChannel::ClearClassFlags(uint32_t aClassFlags) {
    650  mClassOfService.SetFlags(~aClassFlags & mClassOfService.Flags());
    651  return NS_OK;
    652 }
    653 
    654 NS_IMETHODIMP
    655 InterceptedHttpChannel::AddClassFlags(uint32_t aClassFlags) {
    656  mClassOfService.SetFlags(aClassFlags | mClassOfService.Flags());
    657  return NS_OK;
    658 }
    659 
    660 NS_IMETHODIMP
    661 InterceptedHttpChannel::SetClassOfService(ClassOfService cos) {
    662  mClassOfService = cos;
    663  return NS_OK;
    664 }
    665 
    666 NS_IMETHODIMP
    667 InterceptedHttpChannel::SetIncremental(bool incremental) {
    668  mClassOfService.SetIncremental(incremental);
    669  return NS_OK;
    670 }
    671 
    672 NS_IMETHODIMP
    673 InterceptedHttpChannel::ResumeAt(uint64_t aStartPos,
    674                                 const nsACString& aEntityId) {
    675  // We don't support resuming synthesized responses, but we do track this
    676  // information so it can be passed on to the resulting nsHttpChannel if
    677  // ResetInterception is called.
    678  mResumeStartPos = aStartPos;
    679  mResumeEntityId = aEntityId;
    680  return NS_OK;
    681 }
    682 
    683 void InterceptedHttpChannel::DoNotifyListenerCleanup() {
    684  // Prefer to cleanup in ReleaseListeners() as it seems to be called
    685  // more consistently in necko.
    686 }
    687 
    688 void InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus) {
    689  (void)AsyncAbort(aStatus);
    690 }
    691 
    692 namespace {
    693 
    694 class ResetInterceptionHeaderVisitor final : public nsIHttpHeaderVisitor {
    695  nsCOMPtr<nsIHttpChannel> mTarget;
    696 
    697  ~ResetInterceptionHeaderVisitor() = default;
    698 
    699  NS_IMETHOD
    700  VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
    701    // We skip Cookie header here, since it will be added during
    702    // nsHttpChannel::AsyncOpen.
    703    if (aHeader.Equals(nsHttp::Cookie.val())) {
    704      return NS_OK;
    705    }
    706    if (aValue.IsEmpty()) {
    707      return mTarget->SetEmptyRequestHeader(aHeader);
    708    }
    709    return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
    710  }
    711 
    712 public:
    713  explicit ResetInterceptionHeaderVisitor(nsIHttpChannel* aTarget)
    714      : mTarget(aTarget) {
    715    MOZ_DIAGNOSTIC_ASSERT(mTarget);
    716  }
    717 
    718  NS_DECL_ISUPPORTS
    719 };
    720 
    721 NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor)
    722 
    723 }  // anonymous namespace
    724 
    725 NS_IMETHODIMP
    726 InterceptedHttpChannel::ResetInterception(bool aBypass) {
    727  INTERCEPTED_LOG(("InterceptedHttpChannel::ResetInterception [%p] bypass: %s",
    728                   this, aBypass ? "true" : "false"));
    729  if (mCanceled) {
    730    return mStatus;
    731  }
    732 
    733  mInterceptionReset = true;
    734 
    735  uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
    736 
    737  nsCOMPtr<nsIChannel> newChannel;
    738  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
    739      CloneLoadInfoForRedirect(mURI, flags);
    740 
    741  if (aBypass) {
    742    redirectLoadInfo->ClearController();
    743    // TODO: Audit whether we should also be calling
    744    // ServiceWorkerManager::StopControllingClient for maximum correctness.
    745  }
    746 
    747  nsresult rv =
    748      NS_NewChannelInternal(getter_AddRefs(newChannel), mURI, redirectLoadInfo,
    749                            nullptr,  // PerformanceStorage
    750                            nullptr,  // aLoadGroup
    751                            nullptr,  // aCallbacks
    752                            mLoadFlags);
    753  NS_ENSURE_SUCCESS(rv, rv);
    754 
    755  if (profiler_thread_is_being_profiled_for_markers()) {
    756    nsAutoCString requestMethod;
    757    GetRequestMethod(requestMethod);
    758 
    759    int32_t priority = PRIORITY_NORMAL;
    760    GetPriority(&priority);
    761 
    762    uint64_t size = 0;
    763    GetEncodedBodySize(&size);
    764 
    765    nsAutoCString contentType;
    766    mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing();
    767    mozilla::Maybe<uint32_t> responseStatus = Nothing();
    768    if (mResponseHead) {
    769      mResponseHead->ContentType(contentType);
    770      httpVersion = Some(mResponseHead->Version());
    771      responseStatus = Some(mResponseHead->Status());
    772    }
    773 
    774    RefPtr<HttpBaseChannel> newBaseChannel = do_QueryObject(newChannel);
    775    MOZ_ASSERT(newBaseChannel,
    776               "The redirect channel should be a base channel.");
    777    profiler_add_network_marker(
    778        mURI, requestMethod, priority, mChannelId,
    779        NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(),
    780        size, kCacheUnknown, mLoadInfo->GetInnerWindowID(),
    781        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
    782        &mTransactionTimings, std::move(mSource), httpVersion, responseStatus,
    783        Some(nsDependentCString(contentType.get())), mURI, flags,
    784        newBaseChannel->ChannelId());
    785  }
    786 
    787  rv = SetupReplacementChannel(mURI, newChannel, true, flags);
    788  NS_ENSURE_SUCCESS(rv, rv);
    789 
    790  // Restore the non-default headers for fallback channel.
    791  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(newChannel));
    792  nsCOMPtr<nsIHttpHeaderVisitor> visitor =
    793      new ResetInterceptionHeaderVisitor(httpChannel);
    794  rv = VisitNonDefaultRequestHeaders(visitor);
    795  NS_ENSURE_SUCCESS(rv, rv);
    796 
    797  nsCOMPtr<nsITimedChannel> newTimedChannel = do_QueryInterface(newChannel);
    798  if (newTimedChannel) {
    799    if (!mAsyncOpenTime.IsNull()) {
    800      newTimedChannel->SetAsyncOpen(mAsyncOpenTime);
    801    }
    802    if (!mChannelCreationTimestamp.IsNull()) {
    803      newTimedChannel->SetChannelCreation(mChannelCreationTimestamp);
    804    }
    805  }
    806 
    807  if (mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) {
    808    nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
    809    rv = newChannel->GetLoadFlags(&loadFlags);
    810    NS_ENSURE_SUCCESS(rv, rv);
    811    loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
    812    rv = newChannel->SetLoadFlags(loadFlags);
    813    NS_ENSURE_SUCCESS(rv, rv);
    814  }
    815 
    816  mRedirectChannel = std::move(newChannel);
    817 
    818  rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
    819 
    820  if (NS_FAILED(rv)) {
    821    OnRedirectVerifyCallback(rv);
    822  } else {
    823    // ResetInterception success, record the finish time stamps and the final
    824    // status.
    825    mTimeStamps.RecordTime(InterceptionTimeStamps::Reset);
    826  }
    827 
    828  return rv;
    829 }
    830 
    831 NS_IMETHODIMP
    832 InterceptedHttpChannel::SynthesizeStatus(uint16_t aStatus,
    833                                         const nsACString& aReason) {
    834  if (mCanceled) {
    835    return mStatus;
    836  }
    837 
    838  if (!mSynthesizedResponseHead) {
    839    mSynthesizedResponseHead.reset(new nsHttpResponseHead());
    840  }
    841 
    842  nsAutoCString statusLine;
    843  statusLine.AppendLiteral("HTTP/1.1 ");
    844  statusLine.AppendInt(aStatus);
    845  statusLine.AppendLiteral(" ");
    846  statusLine.Append(aReason);
    847 
    848  NS_ENSURE_SUCCESS(mSynthesizedResponseHead->ParseStatusLine(statusLine),
    849                    NS_ERROR_FAILURE);
    850  return NS_OK;
    851 }
    852 
    853 NS_IMETHODIMP
    854 InterceptedHttpChannel::SynthesizeHeader(const nsACString& aName,
    855                                         const nsACString& aValue) {
    856  if (mCanceled) {
    857    return mStatus;
    858  }
    859 
    860  if (!mSynthesizedResponseHead) {
    861    mSynthesizedResponseHead.reset(new nsHttpResponseHead());
    862  }
    863 
    864  nsAutoCString header = aName + ": "_ns + aValue;
    865  // Overwrite any existing header.
    866  nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header);
    867  NS_ENSURE_SUCCESS(rv, rv);
    868  return NS_OK;
    869 }
    870 
    871 NS_IMETHODIMP
    872 InterceptedHttpChannel::StartSynthesizedResponse(
    873    nsIInputStream* aBody, nsIInterceptedBodyCallback* aBodyCallback,
    874    nsICacheInfoChannel* aSynthesizedCacheInfo, const nsACString& aFinalURLSpec,
    875    bool aResponseRedirected) {
    876  nsresult rv = NS_OK;
    877 
    878  auto autoCleanup = MakeScopeExit([&] {
    879    // Auto-cancel on failure.  Do this first to get mStatus set, if necessary.
    880    if (NS_FAILED(rv)) {
    881      Cancel(rv);
    882    }
    883 
    884    // If we early exit before taking ownership of the body, then automatically
    885    // invoke the callback.  This could be due to an error or because we're not
    886    // going to consume it due to a redirect, etc.
    887    if (aBodyCallback) {
    888      aBodyCallback->BodyComplete(mStatus);
    889    }
    890  });
    891 
    892  if (NS_FAILED(mStatus)) {
    893    // Return NS_OK.  The channel should fire callbacks with an error code
    894    // if it was cancelled before this point.
    895    return NS_OK;
    896  }
    897 
    898  // Take ownership of the body callbacks  If a failure occurs we will
    899  // automatically Cancel() the channel.  This will then invoke OnStopRequest()
    900  // which will invoke the correct callback.  In the case of an opaque response
    901  // redirect we pass ownership of the callback to the new channel.
    902  mBodyCallback = aBodyCallback;
    903  aBodyCallback = nullptr;
    904 
    905  mSynthesizedCacheInfo = aSynthesizedCacheInfo;
    906 
    907  if (!mSynthesizedResponseHead) {
    908    mSynthesizedResponseHead.reset(new nsHttpResponseHead());
    909  }
    910 
    911  mResponseHead = std::move(mSynthesizedResponseHead);
    912 
    913  if (ShouldRedirect()) {
    914    rv = FollowSyntheticRedirect();
    915    NS_ENSURE_SUCCESS(rv, rv);
    916 
    917    return NS_OK;
    918  }
    919 
    920  // Intercepted responses should already be decoded.
    921  SetApplyConversion(false);
    922 
    923  // Errors and redirects may not have a body.  Synthesize an empty string
    924  // stream here so later code can be simpler.
    925  mBodyReader = aBody;
    926  if (!mBodyReader) {
    927    rv = NS_NewCStringInputStream(getter_AddRefs(mBodyReader), ""_ns);
    928    NS_ENSURE_SUCCESS(rv, rv);
    929  }
    930 
    931  nsCOMPtr<nsIURI> responseURI;
    932  if (!aFinalURLSpec.IsEmpty()) {
    933    rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
    934    NS_ENSURE_SUCCESS(rv, rv);
    935  } else {
    936    responseURI = mURI;
    937  }
    938 
    939  bool equal = false;
    940  (void)mURI->Equals(responseURI, &equal);
    941  if (!equal) {
    942    rv = RedirectForResponseURL(responseURI, aResponseRedirected);
    943    NS_ENSURE_SUCCESS(rv, rv);
    944 
    945    return NS_OK;
    946  }
    947 
    948  rv = StartPump();
    949  NS_ENSURE_SUCCESS(rv, rv);
    950 
    951  return NS_OK;
    952 }
    953 
    954 NS_IMETHODIMP
    955 InterceptedHttpChannel::FinishSynthesizedResponse() {
    956  if (mCanceled) {
    957    // Return NS_OK.  The channel should fire callbacks with an error code
    958    // if it was cancelled before this point.
    959    return NS_OK;
    960  }
    961 
    962  return NS_OK;
    963 }
    964 
    965 NS_IMETHODIMP
    966 InterceptedHttpChannel::CancelInterception(nsresult aStatus) {
    967  return Cancel(aStatus);
    968 }
    969 
    970 NS_IMETHODIMP
    971 InterceptedHttpChannel::GetChannel(nsIChannel** aChannel) {
    972  nsCOMPtr<nsIChannel> ref(this);
    973  ref.forget(aChannel);
    974  return NS_OK;
    975 }
    976 
    977 NS_IMETHODIMP
    978 InterceptedHttpChannel::GetSecureUpgradedChannelURI(
    979    nsIURI** aSecureUpgradedChannelURI) {
    980  nsCOMPtr<nsIURI> ref(mURI);
    981  ref.forget(aSecureUpgradedChannelURI);
    982  return NS_OK;
    983 }
    984 
    985 NS_IMETHODIMP
    986 InterceptedHttpChannel::SetChannelInfo(
    987    mozilla::dom::ChannelInfo* aChannelInfo) {
    988  return aChannelInfo->ResurrectInfoOnChannel(this);
    989 }
    990 
    991 NS_IMETHODIMP
    992 InterceptedHttpChannel::GetInternalContentPolicyType(
    993    nsContentPolicyType* aPolicyType) {
    994  if (mLoadInfo) {
    995    *aPolicyType = mLoadInfo->InternalContentPolicyType();
    996  }
    997  return NS_OK;
    998 }
    999 
   1000 NS_IMETHODIMP
   1001 InterceptedHttpChannel::GetConsoleReportCollector(
   1002    nsIConsoleReportCollector** aConsoleReportCollector) {
   1003  nsCOMPtr<nsIConsoleReportCollector> ref(this);
   1004  ref.forget(aConsoleReportCollector);
   1005  return NS_OK;
   1006 }
   1007 
   1008 NS_IMETHODIMP
   1009 InterceptedHttpChannel::SetFetchHandlerStart(TimeStamp aTimeStamp) {
   1010  mTimeStamps.RecordTime(std::move(aTimeStamp));
   1011  return NS_OK;
   1012 }
   1013 
   1014 NS_IMETHODIMP
   1015 InterceptedHttpChannel::SetFetchHandlerFinish(TimeStamp aTimeStamp) {
   1016  mTimeStamps.RecordTime(std::move(aTimeStamp));
   1017  return NS_OK;
   1018 }
   1019 
   1020 NS_IMETHODIMP
   1021 InterceptedHttpChannel::SetRemoteWorkerLaunchStart(TimeStamp aTimeStamp) {
   1022  mServiceWorkerLaunchStart = aTimeStamp > mTimeStamps.mInterceptionStart
   1023                                  ? aTimeStamp
   1024                                  : mTimeStamps.mInterceptionStart;
   1025  return NS_OK;
   1026 }
   1027 
   1028 NS_IMETHODIMP
   1029 InterceptedHttpChannel::SetRemoteWorkerLaunchEnd(TimeStamp aTimeStamp) {
   1030  mServiceWorkerLaunchEnd = aTimeStamp > mTimeStamps.mInterceptionStart
   1031                                ? aTimeStamp
   1032                                : mTimeStamps.mInterceptionStart;
   1033  return NS_OK;
   1034 }
   1035 
   1036 NS_IMETHODIMP
   1037 InterceptedHttpChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) {
   1038  mServiceWorkerLaunchStart = aTimeStamp;
   1039  return NS_OK;
   1040 }
   1041 
   1042 NS_IMETHODIMP
   1043 InterceptedHttpChannel::GetLaunchServiceWorkerStart(TimeStamp* aRetVal) {
   1044  MOZ_ASSERT(aRetVal);
   1045  *aRetVal = mServiceWorkerLaunchStart;
   1046  return NS_OK;
   1047 }
   1048 
   1049 NS_IMETHODIMP
   1050 InterceptedHttpChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) {
   1051  mServiceWorkerLaunchEnd = aTimeStamp;
   1052  return NS_OK;
   1053 }
   1054 
   1055 NS_IMETHODIMP
   1056 InterceptedHttpChannel::GetLaunchServiceWorkerEnd(TimeStamp* aRetVal) {
   1057  MOZ_ASSERT(aRetVal);
   1058  *aRetVal = mServiceWorkerLaunchEnd;
   1059  return NS_OK;
   1060 }
   1061 
   1062 NS_IMETHODIMP
   1063 InterceptedHttpChannel::GetDispatchFetchEventStart(TimeStamp* aRetVal) {
   1064  MOZ_ASSERT(aRetVal);
   1065  *aRetVal = mTimeStamps.mInterceptionStart;
   1066  return NS_OK;
   1067 }
   1068 
   1069 NS_IMETHODIMP
   1070 InterceptedHttpChannel::GetDispatchFetchEventEnd(TimeStamp* aRetVal) {
   1071  MOZ_ASSERT(aRetVal);
   1072  *aRetVal = mTimeStamps.mFetchHandlerStart;
   1073  return NS_OK;
   1074 }
   1075 
   1076 NS_IMETHODIMP
   1077 InterceptedHttpChannel::GetHandleFetchEventStart(TimeStamp* aRetVal) {
   1078  MOZ_ASSERT(aRetVal);
   1079  *aRetVal = mTimeStamps.mFetchHandlerStart;
   1080  return NS_OK;
   1081 }
   1082 
   1083 NS_IMETHODIMP
   1084 InterceptedHttpChannel::GetHandleFetchEventEnd(TimeStamp* aRetVal) {
   1085  MOZ_ASSERT(aRetVal);
   1086  *aRetVal = mTimeStamps.mFetchHandlerFinish;
   1087  return NS_OK;
   1088 }
   1089 
   1090 NS_IMETHODIMP
   1091 InterceptedHttpChannel::GetIsReset(bool* aResult) {
   1092  *aResult = mInterceptionReset;
   1093  return NS_OK;
   1094 }
   1095 
   1096 NS_IMETHODIMP
   1097 InterceptedHttpChannel::SetReleaseHandle(nsISupports* aHandle) {
   1098  mReleaseHandle = aHandle;
   1099  return NS_OK;
   1100 }
   1101 
   1102 NS_IMETHODIMP
   1103 InterceptedHttpChannel::OnRedirectVerifyCallback(nsresult rv) {
   1104  MOZ_ASSERT(NS_IsMainThread());
   1105 
   1106  if (NS_SUCCEEDED(rv)) {
   1107    rv = OpenRedirectChannel();
   1108  }
   1109 
   1110  nsCOMPtr<nsIRedirectResultListener> hook;
   1111  GetCallback(hook);
   1112  if (hook) {
   1113    hook->OnRedirectResult(rv);
   1114  }
   1115 
   1116  if (NS_FAILED(rv)) {
   1117    Cancel(rv);
   1118  }
   1119 
   1120  MaybeCallBodyCallback();
   1121 
   1122  StoreIsPending(false);
   1123  // We can only release listeners after the redirected channel really owns
   1124  // mListener. Otherwise, the OnStart/OnStopRequest functions of mListener will
   1125  // not be called.
   1126  if (NS_SUCCEEDED(rv)) {
   1127    ReleaseListeners();
   1128  }
   1129 
   1130  return NS_OK;
   1131 }
   1132 
   1133 NS_IMETHODIMP
   1134 InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
   1135  INTERCEPTED_LOG(("InterceptedHttpChannel::OnStartRequest [%p]", this));
   1136  MOZ_ASSERT(NS_IsMainThread());
   1137 
   1138  if (!mProgressSink) {
   1139    GetCallback(mProgressSink);
   1140  }
   1141 
   1142  MOZ_ASSERT_IF(!mLoadInfo->GetServiceWorkerTaintingSynthesized(),
   1143                mLoadInfo->GetLoadingPrincipal());
   1144  // No need to do ORB checks if these conditions hold.
   1145  MOZ_DIAGNOSTIC_ASSERT(mLoadInfo->GetServiceWorkerTaintingSynthesized() ||
   1146                        mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal());
   1147 
   1148  if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
   1149    mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
   1150  }
   1151 
   1152  nsresult rv = ProcessCrossOriginEmbedderPolicyHeader();
   1153  if (NS_FAILED(rv)) {
   1154    mStatus = NS_ERROR_BLOCKED_BY_POLICY;
   1155    Cancel(mStatus);
   1156  }
   1157 
   1158  rv = ProcessCrossOriginResourcePolicyHeader();
   1159  if (NS_FAILED(rv)) {
   1160    mStatus = NS_ERROR_DOM_CORP_FAILED;
   1161    Cancel(mStatus);
   1162  }
   1163 
   1164  rv = ComputeCrossOriginOpenerPolicyMismatch();
   1165  if (rv == NS_ERROR_BLOCKED_BY_POLICY) {
   1166    mStatus = NS_ERROR_BLOCKED_BY_POLICY;
   1167    Cancel(mStatus);
   1168  }
   1169 
   1170  rv = ValidateMIMEType();
   1171  if (NS_FAILED(rv)) {
   1172    mStatus = rv;
   1173    Cancel(mStatus);
   1174  }
   1175 
   1176  StoreOnStartRequestCalled(true);
   1177  if (mListener) {
   1178    return mListener->OnStartRequest(this);
   1179  }
   1180  return NS_OK;
   1181 }
   1182 
   1183 NS_IMETHODIMP
   1184 InterceptedHttpChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
   1185  INTERCEPTED_LOG(("InterceptedHttpChannel::OnStopRequest [%p]", this));
   1186  MOZ_ASSERT(NS_IsMainThread());
   1187 
   1188  if (NS_SUCCEEDED(mStatus)) {
   1189    mStatus = aStatus;
   1190  }
   1191 
   1192  MaybeCallBodyCallback();
   1193 
   1194  mTimeStamps.RecordTime(InterceptionTimeStamps::Synthesized);
   1195 
   1196  // Its possible that we have any async runnable queued to report some
   1197  // progress when OnStopRequest() is triggered.  Report any left over
   1198  // progress immediately.  The extra runnable will then do nothing thanks
   1199  // to the ReleaseListeners() call below.
   1200  MaybeCallStatusAndProgress();
   1201 
   1202  StoreIsPending(false);
   1203 
   1204  // Register entry to the PerformanceStorage resource timing
   1205  MaybeReportTimingData();
   1206 
   1207  if (profiler_thread_is_being_profiled_for_markers()) {
   1208    // These do allocations/frees/etc; avoid if not active
   1209    nsAutoCString requestMethod;
   1210    GetRequestMethod(requestMethod);
   1211 
   1212    int32_t priority = PRIORITY_NORMAL;
   1213    GetPriority(&priority);
   1214 
   1215    uint64_t size = 0;
   1216    GetEncodedBodySize(&size);
   1217 
   1218    nsAutoCString contentType;
   1219    mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing();
   1220    mozilla::Maybe<uint32_t> responseStatus = Nothing();
   1221    if (mResponseHead) {
   1222      mResponseHead->ContentType(contentType);
   1223      httpVersion = Some(mResponseHead->Version());
   1224      responseStatus = Some(mResponseHead->Status());
   1225    }
   1226    profiler_add_network_marker(
   1227        mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP,
   1228        mLastStatusReported, TimeStamp::Now(), size, kCacheUnknown,
   1229        mLoadInfo->GetInnerWindowID(),
   1230        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
   1231        &mTransactionTimings, std::move(mSource), httpVersion, responseStatus,
   1232        Some(nsDependentCString(contentType.get())));
   1233  }
   1234 
   1235  nsresult rv = NS_OK;
   1236  if (mListener) {
   1237    rv = mListener->OnStopRequest(this, mStatus);
   1238  }
   1239 
   1240  gHttpHandler->OnStopRequest(this);
   1241 
   1242  ReleaseListeners();
   1243 
   1244  return rv;
   1245 }
   1246 
   1247 NS_IMETHODIMP
   1248 InterceptedHttpChannel::OnDataAvailable(nsIRequest* aRequest,
   1249                                        nsIInputStream* aInputStream,
   1250                                        uint64_t aOffset, uint32_t aCount) {
   1251  // Any thread if the channel has been retargeted.
   1252 
   1253  if (mCanceled || !mListener) {
   1254    // If there is no listener, we still need to drain the stream in order
   1255    // maintain necko invariants.
   1256    uint32_t unused = 0;
   1257    aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &unused);
   1258    return mStatus;
   1259  }
   1260  if (mProgressSink) {
   1261    if (!(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
   1262      mProgress = aOffset + aCount;
   1263      MaybeCallStatusAndProgress();
   1264    }
   1265  }
   1266 
   1267  return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount);
   1268 }
   1269 
   1270 NS_IMETHODIMP
   1271 InterceptedHttpChannel::OnDataFinished(nsresult aStatus) {
   1272  if (mCanceled || !mListener) {
   1273    return aStatus;
   1274  }
   1275  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
   1276      do_QueryInterface(mListener);
   1277  if (retargetableListener) {
   1278    return retargetableListener->OnDataFinished(aStatus);
   1279  }
   1280 
   1281  return NS_OK;
   1282 }
   1283 
   1284 NS_IMETHODIMP
   1285 InterceptedHttpChannel::RetargetDeliveryTo(nsISerialEventTarget* aNewTarget) {
   1286  MOZ_ASSERT(NS_IsMainThread());
   1287  NS_ENSURE_ARG(aNewTarget);
   1288 
   1289  // If retargeting to the main thread, do nothing.
   1290  if (aNewTarget->IsOnCurrentThread()) {
   1291    return NS_OK;
   1292  }
   1293 
   1294  // Retargeting is only valid during OnStartRequest for nsIChannels.  So
   1295  // we should only be called if we have a pump.
   1296  if (!mPump) {
   1297    return NS_ERROR_NOT_AVAILABLE;
   1298  }
   1299 
   1300  return mPump->RetargetDeliveryTo(aNewTarget);
   1301 }
   1302 
   1303 NS_IMETHODIMP
   1304 InterceptedHttpChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
   1305  if (!mPump) {
   1306    return NS_ERROR_NOT_AVAILABLE;
   1307  }
   1308  return mPump->GetDeliveryTarget(aEventTarget);
   1309 }
   1310 
   1311 NS_IMETHODIMP
   1312 InterceptedHttpChannel::CheckListenerChain() {
   1313  MOZ_ASSERT(NS_IsMainThread());
   1314  nsresult rv = NS_OK;
   1315  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
   1316      do_QueryInterface(mListener, &rv);
   1317  if (retargetableListener) {
   1318    rv = retargetableListener->CheckListenerChain();
   1319  }
   1320  return rv;
   1321 }
   1322 
   1323 //-----------------------------------------------------------------------------
   1324 // InterceptedHttpChannel::nsICacheInfoChannel
   1325 //-----------------------------------------------------------------------------
   1326 // InterceptedHttpChannel does not really implement the nsICacheInfoChannel
   1327 // interface, we tranfers parameters to the saved
   1328 // nsICacheInfoChannel(mSynthesizedCacheInfo) from StartSynthesizedResponse. And
   1329 // we return false in IsFromCache and NS_ERROR_NOT_AVAILABLE for all other
   1330 // methods while the saved mSynthesizedCacheInfo does not exist.
   1331 NS_IMETHODIMP
   1332 InterceptedHttpChannel::IsFromCache(bool* value) {
   1333  if (mSynthesizedCacheInfo) {
   1334    return mSynthesizedCacheInfo->IsFromCache(value);
   1335  }
   1336  *value = false;
   1337  return NS_OK;
   1338 }
   1339 
   1340 NS_IMETHODIMP
   1341 InterceptedHttpChannel::HasCacheEntry(bool* value) {
   1342  if (mSynthesizedCacheInfo) {
   1343    return mSynthesizedCacheInfo->HasCacheEntry(value);
   1344  }
   1345  *value = false;
   1346  return NS_OK;
   1347 }
   1348 
   1349 NS_IMETHODIMP
   1350 InterceptedHttpChannel::IsRacing(bool* value) {
   1351  if (mSynthesizedCacheInfo) {
   1352    return mSynthesizedCacheInfo->IsRacing(value);
   1353  }
   1354  *value = false;
   1355  return NS_OK;
   1356 }
   1357 
   1358 NS_IMETHODIMP
   1359 InterceptedHttpChannel::GetCacheEntryId(uint64_t* aCacheEntryId) {
   1360  if (mSynthesizedCacheInfo) {
   1361    return mSynthesizedCacheInfo->GetCacheEntryId(aCacheEntryId);
   1362  }
   1363  return NS_ERROR_NOT_AVAILABLE;
   1364 }
   1365 
   1366 NS_IMETHODIMP
   1367 InterceptedHttpChannel::GetCacheTokenFetchCount(uint32_t* _retval) {
   1368  NS_ENSURE_ARG_POINTER(_retval);
   1369 
   1370  if (mSynthesizedCacheInfo) {
   1371    return mSynthesizedCacheInfo->GetCacheTokenFetchCount(_retval);
   1372  }
   1373  return NS_ERROR_NOT_AVAILABLE;
   1374 }
   1375 
   1376 NS_IMETHODIMP
   1377 InterceptedHttpChannel::GetCacheTokenExpirationTime(uint32_t* _retval) {
   1378  NS_ENSURE_ARG_POINTER(_retval);
   1379 
   1380  if (mSynthesizedCacheInfo) {
   1381    return mSynthesizedCacheInfo->GetCacheTokenExpirationTime(_retval);
   1382  }
   1383  return NS_ERROR_NOT_AVAILABLE;
   1384 }
   1385 
   1386 NS_IMETHODIMP
   1387 InterceptedHttpChannel::SetAllowStaleCacheContent(
   1388    bool aAllowStaleCacheContent) {
   1389  if (mSynthesizedCacheInfo) {
   1390    return mSynthesizedCacheInfo->SetAllowStaleCacheContent(
   1391        aAllowStaleCacheContent);
   1392  }
   1393  return NS_ERROR_NOT_AVAILABLE;
   1394 }
   1395 
   1396 NS_IMETHODIMP
   1397 InterceptedHttpChannel::GetAllowStaleCacheContent(
   1398    bool* aAllowStaleCacheContent) {
   1399  if (mSynthesizedCacheInfo) {
   1400    return mSynthesizedCacheInfo->GetAllowStaleCacheContent(
   1401        aAllowStaleCacheContent);
   1402  }
   1403  return NS_ERROR_NOT_AVAILABLE;
   1404 }
   1405 
   1406 NS_IMETHODIMP
   1407 InterceptedHttpChannel::SetForceValidateCacheContent(
   1408    bool aForceValidateCacheContent) {
   1409  // We store aForceValidateCacheContent locally because
   1410  // mSynthesizedCacheInfo isn't present until a response
   1411  // is actually synthesized, which is too late for the value
   1412  // to be forwarded during the redirect to the intercepted
   1413  // channel.
   1414  StoreForceValidateCacheContent(aForceValidateCacheContent);
   1415 
   1416  if (mSynthesizedCacheInfo) {
   1417    return mSynthesizedCacheInfo->SetForceValidateCacheContent(
   1418        aForceValidateCacheContent);
   1419  }
   1420  return NS_OK;
   1421 }
   1422 NS_IMETHODIMP
   1423 InterceptedHttpChannel::GetForceValidateCacheContent(
   1424    bool* aForceValidateCacheContent) {
   1425  *aForceValidateCacheContent = LoadForceValidateCacheContent();
   1426 #ifdef DEBUG
   1427  if (mSynthesizedCacheInfo) {
   1428    bool synthesizedForceValidateCacheContent;
   1429    mSynthesizedCacheInfo->GetForceValidateCacheContent(
   1430        &synthesizedForceValidateCacheContent);
   1431    MOZ_ASSERT(*aForceValidateCacheContent ==
   1432               synthesizedForceValidateCacheContent);
   1433  }
   1434 #endif
   1435  return NS_OK;
   1436 }
   1437 
   1438 NS_IMETHODIMP
   1439 InterceptedHttpChannel::GetPreferCacheLoadOverBypass(
   1440    bool* aPreferCacheLoadOverBypass) {
   1441  if (mSynthesizedCacheInfo) {
   1442    return mSynthesizedCacheInfo->GetPreferCacheLoadOverBypass(
   1443        aPreferCacheLoadOverBypass);
   1444  }
   1445  return NS_ERROR_NOT_AVAILABLE;
   1446 }
   1447 
   1448 NS_IMETHODIMP
   1449 InterceptedHttpChannel::SetPreferCacheLoadOverBypass(
   1450    bool aPreferCacheLoadOverBypass) {
   1451  if (mSynthesizedCacheInfo) {
   1452    return mSynthesizedCacheInfo->SetPreferCacheLoadOverBypass(
   1453        aPreferCacheLoadOverBypass);
   1454  }
   1455  return NS_ERROR_NOT_AVAILABLE;
   1456 }
   1457 
   1458 NS_IMETHODIMP
   1459 InterceptedHttpChannel::PreferAlternativeDataType(
   1460    const nsACString& aType, const nsACString& aContentType,
   1461    PreferredAlternativeDataDeliveryType aDeliverAltData) {
   1462  ENSURE_CALLED_BEFORE_ASYNC_OPEN();
   1463  mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
   1464      nsCString(aType), nsCString(aContentType), aDeliverAltData));
   1465  return NS_OK;
   1466 }
   1467 
   1468 const nsTArray<PreferredAlternativeDataTypeParams>&
   1469 InterceptedHttpChannel::PreferredAlternativeDataTypes() {
   1470  return mPreferredCachedAltDataTypes;
   1471 }
   1472 
   1473 NS_IMETHODIMP
   1474 InterceptedHttpChannel::GetAlternativeDataType(nsACString& aType) {
   1475  if (mSynthesizedCacheInfo) {
   1476    return mSynthesizedCacheInfo->GetAlternativeDataType(aType);
   1477  }
   1478  return NS_ERROR_NOT_AVAILABLE;
   1479 }
   1480 
   1481 NS_IMETHODIMP
   1482 InterceptedHttpChannel::GetCacheEntryWriteHandle(
   1483    nsICacheEntryWriteHandle** _retval) {
   1484  if (mSynthesizedCacheInfo) {
   1485    return mSynthesizedCacheInfo->GetCacheEntryWriteHandle(_retval);
   1486  }
   1487  return NS_ERROR_NOT_AVAILABLE;
   1488 }
   1489 
   1490 NS_IMETHODIMP
   1491 InterceptedHttpChannel::OpenAlternativeOutputStream(
   1492    const nsACString& type, int64_t predictedSize,
   1493    nsIAsyncOutputStream** _retval) {
   1494  if (mSynthesizedCacheInfo) {
   1495    return mSynthesizedCacheInfo->OpenAlternativeOutputStream(
   1496        type, predictedSize, _retval);
   1497  }
   1498  return NS_ERROR_NOT_AVAILABLE;
   1499 }
   1500 
   1501 NS_IMETHODIMP
   1502 InterceptedHttpChannel::GetOriginalInputStream(
   1503    nsIInputStreamReceiver* aReceiver) {
   1504  if (mSynthesizedCacheInfo) {
   1505    return mSynthesizedCacheInfo->GetOriginalInputStream(aReceiver);
   1506  }
   1507  return NS_ERROR_NOT_AVAILABLE;
   1508 }
   1509 
   1510 NS_IMETHODIMP
   1511 InterceptedHttpChannel::GetAlternativeDataInputStream(
   1512    nsIInputStream** aInputStream) {
   1513  if (mSynthesizedCacheInfo) {
   1514    return mSynthesizedCacheInfo->GetAlternativeDataInputStream(aInputStream);
   1515  }
   1516  return NS_ERROR_NOT_AVAILABLE;
   1517 }
   1518 
   1519 NS_IMETHODIMP
   1520 InterceptedHttpChannel::GetCacheKey(uint32_t* key) {
   1521  if (mSynthesizedCacheInfo) {
   1522    return mSynthesizedCacheInfo->GetCacheKey(key);
   1523  }
   1524  return NS_ERROR_NOT_AVAILABLE;
   1525 }
   1526 
   1527 NS_IMETHODIMP
   1528 InterceptedHttpChannel::SetCacheKey(uint32_t key) {
   1529  if (mSynthesizedCacheInfo) {
   1530    return mSynthesizedCacheInfo->SetCacheKey(key);
   1531  }
   1532  return NS_ERROR_NOT_AVAILABLE;
   1533 }
   1534 
   1535 NS_IMETHODIMP
   1536 InterceptedHttpChannel::GetCacheDisposition(CacheDisposition* aDisposition) {
   1537  if (mSynthesizedCacheInfo) {
   1538    return mSynthesizedCacheInfo->GetCacheDisposition(aDisposition);
   1539  }
   1540  return NS_ERROR_NOT_AVAILABLE;
   1541 }
   1542 
   1543 // InterceptionTimeStamps implementation
   1544 InterceptedHttpChannel::InterceptionTimeStamps::InterceptionTimeStamps()
   1545    : mStage(InterceptedHttpChannel::InterceptionTimeStamps::InterceptionStart),
   1546      mStatus(InterceptedHttpChannel::InterceptionTimeStamps::Created) {}
   1547 
   1548 void InterceptedHttpChannel::InterceptionTimeStamps::Init(
   1549    nsIChannel* aChannel) {
   1550  MOZ_ASSERT(aChannel);
   1551  MOZ_ASSERT(mStatus == Created);
   1552 
   1553  mStatus = Initialized;
   1554 
   1555  mIsNonSubresourceRequest = nsContentUtils::IsNonSubresourceRequest(aChannel);
   1556  mKey = mIsNonSubresourceRequest ? "navigation"_ns : "subresource"_ns;
   1557  nsCOMPtr<nsIInterceptedChannel> interceptedChannel =
   1558      do_QueryInterface(aChannel);
   1559  // It must be a InterceptedHttpChannel
   1560  MOZ_ASSERT(interceptedChannel);
   1561  if (!mIsNonSubresourceRequest) {
   1562    interceptedChannel->GetSubresourceTimeStampKey(aChannel, mSubresourceKey);
   1563  }
   1564 }
   1565 
   1566 void InterceptedHttpChannel::InterceptionTimeStamps::RecordTime(
   1567    InterceptedHttpChannel::InterceptionTimeStamps::Status&& aStatus,
   1568    TimeStamp&& aTimeStamp) {
   1569  // Only allow passing Synthesized, Reset, Redirected, and Canceled in this
   1570  // method.
   1571  MOZ_ASSERT(aStatus == Synthesized || aStatus == Reset ||
   1572             aStatus == Canceled || aStatus == Redirected);
   1573  if (mStatus == Canceled) {
   1574    return;
   1575  }
   1576 
   1577  // If current status is not Initialized, only Canceled can be recorded.
   1578  // That means it is canceled after other operation is done, ex. synthesized.
   1579  MOZ_ASSERT(mStatus == Initialized || aStatus == Canceled);
   1580 
   1581  switch (mStatus) {
   1582    case Initialized:
   1583      mStatus = aStatus;
   1584      break;
   1585    case Synthesized:
   1586      mStatus = CanceledAfterSynthesized;
   1587      break;
   1588    case Reset:
   1589      mStatus = CanceledAfterReset;
   1590      break;
   1591    case Redirected:
   1592      mStatus = CanceledAfterRedirected;
   1593      break;
   1594    // Channel is cancelled before calling AsyncOpenInternal(), no need to
   1595    // record the cancel time stamp.
   1596    case Created:
   1597      return;
   1598    default:
   1599      MOZ_ASSERT(false);
   1600      break;
   1601  }
   1602 
   1603  RecordTimeInternal(std::move(aTimeStamp));
   1604 }
   1605 
   1606 void InterceptedHttpChannel::InterceptionTimeStamps::RecordTime(
   1607    TimeStamp&& aTimeStamp) {
   1608  MOZ_ASSERT(mStatus == Initialized || mStatus == Canceled);
   1609  if (mStatus == Canceled) {
   1610    return;
   1611  }
   1612  RecordTimeInternal(std::move(aTimeStamp));
   1613 }
   1614 
   1615 void InterceptedHttpChannel::InterceptionTimeStamps::RecordTimeInternal(
   1616    TimeStamp&& aTimeStamp) {
   1617  MOZ_ASSERT(mStatus != Created);
   1618 
   1619  if (mStatus == Canceled && mStage != InterceptionFinish) {
   1620    mFetchHandlerStart = aTimeStamp;
   1621    mFetchHandlerFinish = aTimeStamp;
   1622    mStage = InterceptionFinish;
   1623  }
   1624 
   1625  switch (mStage) {
   1626    case InterceptionStart: {
   1627      MOZ_ASSERT(mInterceptionStart.IsNull());
   1628      mInterceptionStart = aTimeStamp;
   1629      mStage = FetchHandlerStart;
   1630      break;
   1631    }
   1632    case (FetchHandlerStart): {
   1633      MOZ_ASSERT(mFetchHandlerStart.IsNull());
   1634      mFetchHandlerStart = aTimeStamp;
   1635      mStage = FetchHandlerFinish;
   1636      break;
   1637    }
   1638    case (FetchHandlerFinish): {
   1639      MOZ_ASSERT(mFetchHandlerFinish.IsNull());
   1640      mFetchHandlerFinish = aTimeStamp;
   1641      mStage = InterceptionFinish;
   1642      break;
   1643    }
   1644    case InterceptionFinish: {
   1645      mInterceptionFinish = aTimeStamp;
   1646      SaveTimeStamps();
   1647      return;
   1648    }
   1649    default: {
   1650      return;
   1651    }
   1652  }
   1653 }
   1654 
   1655 void InterceptedHttpChannel::InterceptionTimeStamps::GenKeysWithStatus(
   1656    nsCString& aKey, nsCString& aSubresourceKey) {
   1657  nsAutoCString statusString;
   1658  switch (mStatus) {
   1659    case Synthesized:
   1660      statusString = "synthesized"_ns;
   1661      break;
   1662    case Reset:
   1663      statusString = "reset"_ns;
   1664      break;
   1665    case Redirected:
   1666      statusString = "redirected"_ns;
   1667      break;
   1668    case Canceled:
   1669      statusString = "canceled"_ns;
   1670      break;
   1671    case CanceledAfterSynthesized:
   1672      statusString = "canceled-after-synthesized"_ns;
   1673      break;
   1674    case CanceledAfterReset:
   1675      statusString = "canceled-after-reset"_ns;
   1676      break;
   1677    case CanceledAfterRedirected:
   1678      statusString = "canceled-after-redirected"_ns;
   1679      break;
   1680    default:
   1681      return;
   1682  }
   1683  aKey = mKey;
   1684  aSubresourceKey = mSubresourceKey;
   1685  aKey.AppendLiteral("_");
   1686  aSubresourceKey.AppendLiteral("_");
   1687  aKey.Append(statusString);
   1688  aSubresourceKey.Append(statusString);
   1689 }
   1690 
   1691 void InterceptedHttpChannel::InterceptionTimeStamps::SaveTimeStamps() {
   1692  MOZ_ASSERT(mStatus != Initialized && mStatus != Created);
   1693 
   1694  if (mStatus == Reset) {
   1695    glean::service_worker::fetch_event_channel_reset.Get(mKey)
   1696        .AccumulateRawDuration(mInterceptionFinish - mFetchHandlerFinish);
   1697    if (!mIsNonSubresourceRequest && !mSubresourceKey.IsEmpty()) {
   1698      glean::service_worker::fetch_event_channel_reset.Get(mSubresourceKey)
   1699          .AccumulateRawDuration(mInterceptionFinish - mFetchHandlerFinish);
   1700    }
   1701  } else if (mStatus == Synthesized) {
   1702    glean::service_worker::fetch_event_finish_synthesized_response.Get(mKey)
   1703        .AccumulateRawDuration(mInterceptionFinish - mFetchHandlerFinish);
   1704    if (!mIsNonSubresourceRequest && !mSubresourceKey.IsEmpty()) {
   1705      glean::service_worker::fetch_event_finish_synthesized_response
   1706          .Get(mSubresourceKey)
   1707          .AccumulateRawDuration(mInterceptionFinish - mFetchHandlerFinish);
   1708    }
   1709  }
   1710 
   1711  if (!mFetchHandlerStart.IsNull()) {
   1712    glean::service_worker::fetch_event_dispatch.Get(mKey).AccumulateRawDuration(
   1713        mFetchHandlerStart - mInterceptionStart);
   1714 
   1715    if (!mIsNonSubresourceRequest && !mSubresourceKey.IsEmpty()) {
   1716      glean::service_worker::fetch_event_dispatch.Get(mSubresourceKey)
   1717          .AccumulateRawDuration(mFetchHandlerStart - mInterceptionStart);
   1718    }
   1719  }
   1720 
   1721  nsAutoCString key, subresourceKey;
   1722  GenKeysWithStatus(key, subresourceKey);
   1723 
   1724  glean::service_worker::fetch_interception_duration.Get(key)
   1725      .AccumulateRawDuration(mInterceptionFinish - mInterceptionStart);
   1726  if (!mIsNonSubresourceRequest && !mSubresourceKey.IsEmpty()) {
   1727    glean::service_worker::fetch_interception_duration.Get(subresourceKey)
   1728        .AccumulateRawDuration(mInterceptionFinish - mInterceptionStart);
   1729  }
   1730 }
   1731 
   1732 }  // namespace mozilla::net