tor-browser

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

FetchEventOpChild.cpp (22265B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 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 "FetchEventOpChild.h"
      8 
      9 #include <utility>
     10 
     11 #include "MainThreadUtils.h"
     12 #include "ServiceWorkerPrivate.h"
     13 #include "mozilla/Assertions.h"
     14 #include "mozilla/LoadInfo.h"
     15 #include "mozilla/Services.h"
     16 #include "mozilla/StaticPrefs_dom.h"
     17 #include "mozilla/dom/FetchService.h"
     18 #include "mozilla/dom/InternalHeaders.h"
     19 #include "mozilla/dom/InternalResponse.h"
     20 #include "mozilla/dom/PRemoteWorkerControllerChild.h"
     21 #include "mozilla/dom/RemoteWorkerControllerChild.h"
     22 #include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
     23 #include "mozilla/ipc/BackgroundChild.h"
     24 #include "mozilla/net/NeckoChannelParams.h"
     25 #include "nsContentPolicyUtils.h"
     26 #include "nsContentUtils.h"
     27 #include "nsDebug.h"
     28 #include "nsError.h"
     29 #include "nsIChannel.h"
     30 #include "nsIConsoleReportCollector.h"
     31 #include "nsIContentPolicy.h"
     32 #include "nsIInputStream.h"
     33 #include "nsILoadInfo.h"
     34 #include "nsINetworkInterceptController.h"
     35 #include "nsIObserverService.h"
     36 #include "nsIScriptError.h"
     37 #include "nsISupportsImpl.h"
     38 #include "nsIURI.h"
     39 #include "nsNetUtil.h"
     40 #include "nsProxyRelease.h"
     41 #include "nsTArray.h"
     42 #include "nsThreadUtils.h"
     43 
     44 namespace mozilla::dom {
     45 
     46 namespace {
     47 
     48 bool CSPPermitsResponse(nsILoadInfo* aLoadInfo,
     49                        SafeRefPtr<InternalResponse> aResponse,
     50                        const nsACString& aWorkerScriptSpec) {
     51  AssertIsOnMainThread();
     52  MOZ_ASSERT(aLoadInfo);
     53 
     54  nsCString url = aResponse->GetUnfilteredURL();
     55  if (url.IsEmpty()) {
     56    // Synthetic response.
     57    url = aWorkerScriptSpec;
     58  }
     59 
     60  nsCOMPtr<nsIURI> uri;
     61  nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr);
     62  if (NS_WARN_IF(NS_FAILED(rv))) {
     63    return false;
     64  }
     65 
     66  int16_t decision = nsIContentPolicy::ACCEPT;
     67  rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, &decision);
     68  if (NS_WARN_IF(NS_FAILED(rv))) {
     69    return false;
     70  }
     71 
     72  return decision == nsIContentPolicy::ACCEPT;
     73 }
     74 
     75 void AsyncLog(nsIInterceptedChannel* aChannel, const nsACString& aScriptSpec,
     76              uint32_t aLineNumber, uint32_t aColumnNumber,
     77              const nsACString& aMessageName, nsTArray<nsString>&& aParams) {
     78  AssertIsOnMainThread();
     79  MOZ_ASSERT(aChannel);
     80 
     81  nsCOMPtr<nsIConsoleReportCollector> reporter =
     82      aChannel->GetConsoleReportCollector();
     83 
     84  if (reporter) {
     85    // NOTE: is appears that `const nsTArray<nsString>&` is required for
     86    // nsIConsoleReportCollector::AddConsoleReport to resolve to the correct
     87    // overload.
     88    const nsTArray<nsString> params = std::move(aParams);
     89 
     90    reporter->AddConsoleReport(
     91        nsIScriptError::errorFlag, "Service Worker Interception"_ns,
     92        nsContentUtils::eDOM_PROPERTIES, aScriptSpec, aLineNumber,
     93        aColumnNumber, aMessageName, params);
     94  }
     95 }
     96 
     97 class SynthesizeResponseWatcher final : public nsIInterceptedBodyCallback {
     98 public:
     99  NS_DECL_THREADSAFE_ISUPPORTS
    100 
    101  SynthesizeResponseWatcher(
    102      const nsMainThreadPtrHandle<nsIInterceptedChannel>& aInterceptedChannel,
    103      const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
    104      const bool aIsNonSubresourceRequest,
    105      FetchEventRespondWithClosure&& aClosure, nsAString&& aRequestURL)
    106      : mInterceptedChannel(aInterceptedChannel),
    107        mRegistration(aRegistration),
    108        mIsNonSubresourceRequest(aIsNonSubresourceRequest),
    109        mClosure(std::move(aClosure)),
    110        mRequestURL(std::move(aRequestURL)) {
    111    AssertIsOnMainThread();
    112    MOZ_ASSERT(mInterceptedChannel);
    113    MOZ_ASSERT(mRegistration);
    114  }
    115 
    116  NS_IMETHOD
    117  BodyComplete(nsresult aRv) override {
    118    AssertIsOnMainThread();
    119    MOZ_ASSERT(mInterceptedChannel);
    120 
    121    if (NS_WARN_IF(NS_FAILED(aRv))) {
    122      AsyncLog(mInterceptedChannel, mClosure.respondWithScriptSpec(),
    123               mClosure.respondWithLineNumber(),
    124               mClosure.respondWithColumnNumber(),
    125               "InterceptionFailedWithURL"_ns, {mRequestURL});
    126 
    127      CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
    128 
    129      return NS_OK;
    130    }
    131 
    132    nsresult rv = mInterceptedChannel->FinishSynthesizedResponse();
    133 
    134    if (NS_WARN_IF(NS_FAILED(rv))) {
    135      CancelInterception(rv);
    136    }
    137 
    138    mInterceptedChannel = nullptr;
    139 
    140    return NS_OK;
    141  }
    142 
    143  // See FetchEventOpChild::MaybeScheduleRegistrationUpdate() for comments.
    144  void CancelInterception(nsresult aStatus) {
    145    AssertIsOnMainThread();
    146    MOZ_ASSERT(mInterceptedChannel);
    147    MOZ_ASSERT(mRegistration);
    148 
    149    mInterceptedChannel->CancelInterception(aStatus);
    150 
    151    if (mIsNonSubresourceRequest) {
    152      mRegistration->MaybeScheduleUpdate();
    153    } else {
    154      mRegistration->MaybeScheduleTimeCheckAndUpdate();
    155    }
    156 
    157    mInterceptedChannel = nullptr;
    158    mRegistration = nullptr;
    159  }
    160 
    161 private:
    162  ~SynthesizeResponseWatcher() {
    163    if (NS_WARN_IF(mInterceptedChannel)) {
    164      CancelInterception(NS_ERROR_DOM_ABORT_ERR);
    165    }
    166  }
    167 
    168  nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
    169  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
    170  const bool mIsNonSubresourceRequest;
    171  const FetchEventRespondWithClosure mClosure;
    172  const nsString mRequestURL;
    173 };
    174 
    175 NS_IMPL_ISUPPORTS(SynthesizeResponseWatcher, nsIInterceptedBodyCallback)
    176 
    177 }  // anonymous namespace
    178 
    179 /* static */ RefPtr<GenericPromise> FetchEventOpChild::SendFetchEvent(
    180    PRemoteWorkerControllerChild* aManager,
    181    ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
    182    nsCOMPtr<nsIInterceptedChannel> aInterceptedChannel,
    183    RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
    184    RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
    185    RefPtr<KeepAliveToken>&& aKeepAliveToken) {
    186  AssertIsOnMainThread();
    187  MOZ_ASSERT(aManager);
    188  MOZ_ASSERT(aInterceptedChannel);
    189  MOZ_ASSERT(aKeepAliveToken);
    190 
    191  FetchEventOpChild* actor = new FetchEventOpChild(
    192      std::move(aArgs), std::move(aInterceptedChannel),
    193      std::move(aRegistration), std::move(aPreloadResponseReadyPromises),
    194      std::move(aKeepAliveToken));
    195 
    196  actor->mWasSent = true;
    197  RefPtr<GenericPromise> promise = actor->mPromiseHolder.Ensure(__func__);
    198  (void)aManager->SendPFetchEventOpConstructor(actor, actor->mArgs);
    199  // NOTE: actor may have been destroyed
    200  return promise;
    201 }
    202 
    203 FetchEventOpChild::~FetchEventOpChild() {
    204  AssertIsOnMainThread();
    205  MOZ_ASSERT(mInterceptedChannelHandled);
    206  MOZ_DIAGNOSTIC_ASSERT(mPromiseHolder.IsEmpty());
    207 }
    208 
    209 FetchEventOpChild::FetchEventOpChild(
    210    ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
    211    nsCOMPtr<nsIInterceptedChannel>&& aInterceptedChannel,
    212    RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
    213    RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
    214    RefPtr<KeepAliveToken>&& aKeepAliveToken)
    215    : mArgs(std::move(aArgs)),
    216      mInterceptedChannel(std::move(aInterceptedChannel)),
    217      mRegistration(std::move(aRegistration)),
    218      mKeepAliveToken(std::move(aKeepAliveToken)),
    219      mPreloadResponseReadyPromises(std::move(aPreloadResponseReadyPromises)) {
    220  if (mPreloadResponseReadyPromises) {
    221    // This promise should be configured to use synchronous dispatch, so if it's
    222    // already resolved when we run this code then the callback will be called
    223    // synchronously and pass the preload response with the constructor message.
    224    //
    225    // Note that it's fine to capture the this pointer in the callbacks because
    226    // we disconnect the request in Recv__delete__().
    227    mPreloadResponseReadyPromises->GetResponseAvailablePromise()
    228        ->Then(
    229            GetCurrentSerialEventTarget(), __func__,
    230            [this](FetchServiceResponse&& aResponse) {
    231              if (!mWasSent) {
    232                // The actor wasn't sent yet, we can still send the preload
    233                // response with it.
    234                mArgs.preloadResponse() =
    235                    Some(aResponse->ToParentToParentInternalResponse());
    236              } else {
    237                // It's too late to send the preload response with the actor, we
    238                // have to send it in a separate message.
    239                SendPreloadResponse(
    240                    aResponse->ToParentToParentInternalResponse());
    241              }
    242              mPreloadResponseAvailablePromiseRequestHolder.Complete();
    243            },
    244            [this](const CopyableErrorResult&) {
    245              mPreloadResponseAvailablePromiseRequestHolder.Complete();
    246            })
    247        ->Track(mPreloadResponseAvailablePromiseRequestHolder);
    248 
    249    mPreloadResponseReadyPromises->GetResponseTimingPromise()
    250        ->Then(
    251            GetCurrentSerialEventTarget(), __func__,
    252            [this](ResponseTiming&& aTiming) {
    253              if (!mWasSent) {
    254                // The actor wasn't sent yet, we can still send the preload
    255                // response timing with it.
    256                mArgs.preloadResponseTiming() = Some(std::move(aTiming));
    257              } else {
    258                SendPreloadResponseTiming(aTiming);
    259              }
    260              mPreloadResponseTimingPromiseRequestHolder.Complete();
    261            },
    262            [this](const CopyableErrorResult&) {
    263              mPreloadResponseTimingPromiseRequestHolder.Complete();
    264            })
    265        ->Track(mPreloadResponseTimingPromiseRequestHolder);
    266 
    267    mPreloadResponseReadyPromises->GetResponseEndPromise()
    268        ->Then(
    269            GetCurrentSerialEventTarget(), __func__,
    270            [this](ResponseEndArgs&& aResponse) {
    271              if (!mWasSent) {
    272                // The actor wasn't sent yet, we can still send the preload
    273                // response end args with it.
    274                mArgs.preloadResponseEndArgs() = Some(std::move(aResponse));
    275              } else {
    276                // It's too late to send the preload response end with the
    277                // actor, we have to send it in a separate message.
    278                SendPreloadResponseEnd(aResponse);
    279              }
    280              mPreloadResponseReadyPromises = nullptr;
    281              mPreloadResponseEndPromiseRequestHolder.Complete();
    282            },
    283            [this](const CopyableErrorResult&) {
    284              mPreloadResponseReadyPromises = nullptr;
    285              mPreloadResponseEndPromiseRequestHolder.Complete();
    286            })
    287        ->Track(mPreloadResponseEndPromiseRequestHolder);
    288  }
    289 }
    290 
    291 mozilla::ipc::IPCResult FetchEventOpChild::RecvAsyncLog(
    292    const nsCString& aScriptSpec, const uint32_t& aLineNumber,
    293    const uint32_t& aColumnNumber, const nsCString& aMessageName,
    294    nsTArray<nsString>&& aParams) {
    295  AssertIsOnMainThread();
    296  MOZ_ASSERT(mInterceptedChannel);
    297 
    298  AsyncLog(mInterceptedChannel, aScriptSpec, aLineNumber, aColumnNumber,
    299           aMessageName, std::move(aParams));
    300 
    301  return IPC_OK();
    302 }
    303 
    304 mozilla::ipc::IPCResult FetchEventOpChild::RecvRespondWith(
    305    ParentToParentFetchEventRespondWithResult&& aResult) {
    306  AssertIsOnMainThread();
    307 
    308  RefPtr<RemoteWorkerControllerChild> mgr =
    309      static_cast<RemoteWorkerControllerChild*>(Manager());
    310 
    311  mInterceptedChannel->SetRemoteWorkerLaunchStart(
    312      mgr->GetRemoteWorkerLaunchStart());
    313  mInterceptedChannel->SetRemoteWorkerLaunchEnd(
    314      mgr->GetRemoteWorkerLaunchEnd());
    315 
    316  switch (aResult.type()) {
    317    case ParentToParentFetchEventRespondWithResult::
    318        TParentToParentSynthesizeResponseArgs:
    319      mInterceptedChannel->SetFetchHandlerStart(
    320          aResult.get_ParentToParentSynthesizeResponseArgs()
    321              .timeStamps()
    322              .fetchHandlerStart());
    323      mInterceptedChannel->SetFetchHandlerFinish(
    324          aResult.get_ParentToParentSynthesizeResponseArgs()
    325              .timeStamps()
    326              .fetchHandlerFinish());
    327      SynthesizeResponse(
    328          std::move(aResult.get_ParentToParentSynthesizeResponseArgs()));
    329      break;
    330    case ParentToParentFetchEventRespondWithResult::TResetInterceptionArgs:
    331      mInterceptedChannel->SetFetchHandlerStart(
    332          aResult.get_ResetInterceptionArgs().timeStamps().fetchHandlerStart());
    333      mInterceptedChannel->SetFetchHandlerFinish(
    334          aResult.get_ResetInterceptionArgs()
    335              .timeStamps()
    336              .fetchHandlerFinish());
    337      ResetInterception(false);
    338      break;
    339    case ParentToParentFetchEventRespondWithResult::TCancelInterceptionArgs:
    340      mInterceptedChannel->SetFetchHandlerStart(
    341          aResult.get_CancelInterceptionArgs()
    342              .timeStamps()
    343              .fetchHandlerStart());
    344      mInterceptedChannel->SetFetchHandlerFinish(
    345          aResult.get_CancelInterceptionArgs()
    346              .timeStamps()
    347              .fetchHandlerFinish());
    348      CancelInterception(aResult.get_CancelInterceptionArgs().status());
    349      break;
    350    default:
    351      MOZ_CRASH("Unknown IPCFetchEventRespondWithResult type!");
    352      break;
    353  }
    354 
    355  return IPC_OK();
    356 }
    357 
    358 mozilla::ipc::IPCResult FetchEventOpChild::Recv__delete__(
    359    const ServiceWorkerFetchEventOpResult& aResult) {
    360  AssertIsOnMainThread();
    361  MOZ_ASSERT(mRegistration);
    362 
    363  if (NS_WARN_IF(!mInterceptedChannelHandled)) {
    364    MOZ_ASSERT(NS_FAILED(aResult.rv()));
    365    NS_WARNING(
    366        "Failed to handle intercepted network request; canceling "
    367        "interception!");
    368 
    369    CancelInterception(aResult.rv());
    370  }
    371 
    372  mPromiseHolder.ResolveIfExists(true, __func__);
    373 
    374  // FetchEvent is completed.
    375  // Disconnect preload response related promises and cancel the preload.
    376  mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
    377  mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists();
    378  mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
    379  if (mPreloadResponseReadyPromises) {
    380    RefPtr<FetchService> fetchService = FetchService::GetInstance();
    381    fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises), false);
    382  }
    383 
    384  /**
    385   * This corresponds to the "Fire Functional Event" algorithm's step 9:
    386   *
    387   * "If the time difference in seconds calculated by the current time minus
    388   * registration's last update check time is greater than 84600, invoke Soft
    389   * Update algorithm with registration."
    390   *
    391   * TODO: this is probably being called later than it should be; it should be
    392   * called ASAP after dispatching the FetchEvent.
    393   */
    394  mRegistration->MaybeScheduleTimeCheckAndUpdate();
    395 
    396  return IPC_OK();
    397 }
    398 
    399 void FetchEventOpChild::ActorDestroy(ActorDestroyReason) {
    400  AssertIsOnMainThread();
    401 
    402  // If `Recv__delete__` was called, it would have resolved the promise already.
    403  mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
    404 
    405  if (NS_WARN_IF(!mInterceptedChannelHandled)) {
    406    (void)Recv__delete__(NS_ERROR_DOM_ABORT_ERR);
    407  }
    408 }
    409 
    410 nsresult FetchEventOpChild::StartSynthesizedResponse(
    411    ParentToParentSynthesizeResponseArgs&& aArgs) {
    412  AssertIsOnMainThread();
    413  MOZ_ASSERT(mInterceptedChannel);
    414  MOZ_ASSERT(!mInterceptedChannelHandled);
    415  MOZ_ASSERT(mRegistration);
    416 
    417  /**
    418   * TODO: moving the IPCInternalResponse won't do anything right now because
    419   * there isn't a prefect-forwarding or rvalue-ref-parameter overload of
    420   * `InternalResponse::FromIPC().`
    421   */
    422  SafeRefPtr<InternalResponse> response =
    423      InternalResponse::FromIPC(aArgs.internalResponse());
    424  if (NS_WARN_IF(!response)) {
    425    return NS_ERROR_FAILURE;
    426  }
    427 
    428  nsCOMPtr<nsIChannel> underlyingChannel;
    429  nsresult rv =
    430      mInterceptedChannel->GetChannel(getter_AddRefs(underlyingChannel));
    431  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!underlyingChannel)) {
    432    return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
    433  }
    434 
    435  nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->LoadInfo();
    436  if (!CSPPermitsResponse(loadInfo, response.clonePtr(),
    437                          mArgs.common().workerScriptSpec())) {
    438    return NS_ERROR_CONTENT_BLOCKED;
    439  }
    440 
    441  MOZ_ASSERT(response->GetChannelInfo().IsInitialized());
    442  ChannelInfo channelInfo = response->GetChannelInfo();
    443  rv = mInterceptedChannel->SetChannelInfo(&channelInfo);
    444  if (NS_WARN_IF(NS_FAILED(rv))) {
    445    return NS_ERROR_INTERCEPTION_FAILED;
    446  }
    447 
    448  rv = mInterceptedChannel->SynthesizeStatus(
    449      response->GetUnfilteredStatus(), response->GetUnfilteredStatusText());
    450  if (NS_WARN_IF(NS_FAILED(rv))) {
    451    return rv;
    452  }
    453 
    454  AutoTArray<InternalHeaders::Entry, 5> entries;
    455  response->UnfilteredHeaders()->GetEntries(entries);
    456  for (auto& entry : entries) {
    457    mInterceptedChannel->SynthesizeHeader(entry.mName, entry.mValue);
    458  }
    459 
    460  auto castLoadInfo = static_cast<mozilla::net::LoadInfo*>(loadInfo.get());
    461  castLoadInfo->SynthesizeServiceWorkerTainting(response->GetTainting());
    462 
    463  // Get the preferred alternative data type of the outer channel
    464  nsAutoCString preferredAltDataType(""_ns);
    465  nsCOMPtr<nsICacheInfoChannel> outerChannel =
    466      do_QueryInterface(underlyingChannel);
    467  if (outerChannel &&
    468      !outerChannel->PreferredAlternativeDataTypes().IsEmpty()) {
    469    preferredAltDataType.Assign(
    470        outerChannel->PreferredAlternativeDataTypes()[0].type());
    471  }
    472 
    473  nsCOMPtr<nsIInputStream> body;
    474  if (preferredAltDataType.Equals(response->GetAlternativeDataType())) {
    475    body = response->TakeAlternativeBody();
    476  }
    477  if (!body) {
    478    response->GetUnfilteredBody(getter_AddRefs(body));
    479  }
    480 
    481  // Propagate the URL to the content if the request mode is not "navigate".
    482  // Note that, we only reflect the final URL if the response.redirected is
    483  // false. We propagate all the URLs if the response.redirected is true.
    484  const IPCInternalRequest& request = mArgs.common().internalRequest();
    485  nsAutoCString responseURL;
    486  if (request.requestMode() != RequestMode::Navigate) {
    487    responseURL = response->GetUnfilteredURL();
    488 
    489    // Similar to how we apply the request fragment to redirects automatically
    490    // we also want to apply it automatically when propagating the response
    491    // URL from a service worker interception.  Currently response.url strips
    492    // the fragment, so this will never conflict with an existing fragment
    493    // on the response.  In the future we will have to check for a response
    494    // fragment and avoid overriding in that case.
    495    if (!request.fragment().IsEmpty() && !responseURL.IsEmpty()) {
    496      MOZ_ASSERT(!responseURL.Contains('#'));
    497      responseURL.AppendLiteral("#");
    498      responseURL.Append(request.fragment());
    499    }
    500  }
    501 
    502  nsMainThreadPtrHandle<nsIInterceptedChannel> interceptedChannel(
    503      new nsMainThreadPtrHolder<nsIInterceptedChannel>(
    504          "nsIInterceptedChannel", mInterceptedChannel, false));
    505 
    506  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> registration(
    507      new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
    508          "ServiceWorkerRegistrationInfo", mRegistration, false));
    509 
    510  nsCString requestURL = request.urlList().LastElement();
    511  if (!request.fragment().IsEmpty()) {
    512    requestURL.AppendLiteral("#");
    513    requestURL.Append(request.fragment());
    514  }
    515 
    516  RefPtr<SynthesizeResponseWatcher> watcher = new SynthesizeResponseWatcher(
    517      interceptedChannel, registration,
    518      mArgs.common().isNonSubresourceRequest(), std::move(aArgs.closure()),
    519      NS_ConvertUTF8toUTF16(responseURL));
    520 
    521  rv = mInterceptedChannel->StartSynthesizedResponse(
    522      body, watcher, nullptr /* TODO */, responseURL, response->IsRedirected());
    523  if (NS_WARN_IF(NS_FAILED(rv))) {
    524    return rv;
    525  }
    526 
    527  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    528  if (obsService) {
    529    obsService->NotifyObservers(underlyingChannel,
    530                                "service-worker-synthesized-response", nullptr);
    531  }
    532 
    533  return rv;
    534 }
    535 
    536 void FetchEventOpChild::SynthesizeResponse(
    537    ParentToParentSynthesizeResponseArgs&& aArgs) {
    538  AssertIsOnMainThread();
    539  MOZ_ASSERT(mInterceptedChannel);
    540  MOZ_ASSERT(!mInterceptedChannelHandled);
    541 
    542  nsresult rv = StartSynthesizedResponse(std::move(aArgs));
    543 
    544  if (NS_WARN_IF(NS_FAILED(rv))) {
    545    NS_WARNING("Failed to synthesize response!");
    546 
    547    mInterceptedChannel->CancelInterception(rv);
    548  }
    549 
    550  mInterceptedChannelHandled = true;
    551 
    552  MaybeScheduleRegistrationUpdate();
    553 }
    554 
    555 void FetchEventOpChild::ResetInterception(bool aBypass) {
    556  AssertIsOnMainThread();
    557  MOZ_ASSERT(mInterceptedChannel);
    558  MOZ_ASSERT(!mInterceptedChannelHandled);
    559 
    560  nsresult rv = mInterceptedChannel->ResetInterception(aBypass);
    561 
    562  if (NS_WARN_IF(NS_FAILED(rv))) {
    563    NS_WARNING("Failed to resume intercepted network request!");
    564 
    565    mInterceptedChannel->CancelInterception(rv);
    566  }
    567 
    568  mInterceptedChannelHandled = true;
    569 
    570  MaybeScheduleRegistrationUpdate();
    571 }
    572 
    573 void FetchEventOpChild::CancelInterception(nsresult aStatus) {
    574  AssertIsOnMainThread();
    575  MOZ_ASSERT(mInterceptedChannel);
    576  MOZ_ASSERT(!mInterceptedChannelHandled);
    577  MOZ_ASSERT(NS_FAILED(aStatus));
    578 
    579  // Report a navigation fault if this is a navigation (and we have an active
    580  // worker, which should be the case in non-shutdown/content-process-crash
    581  // situations).
    582  RefPtr<ServiceWorkerInfo> mActive = mRegistration->GetActive();
    583  if (mActive && mArgs.common().isNonSubresourceRequest()) {
    584    mActive->ReportNavigationFault();
    585    // Additional mitigations such as unregistering the registration are handled
    586    // in ServiceWorkerRegistrationInfo::MaybeScheduleUpdate which will be
    587    // called by MaybeScheduleRegistrationUpdate which gets called by our call
    588    // to ResetInterception.
    589    if (StaticPrefs::dom_serviceWorkers_mitigations_bypass_on_fault()) {
    590      ResetInterception(true);
    591      return;
    592    }
    593  }
    594 
    595  mInterceptedChannel->CancelInterception(aStatus);
    596  mInterceptedChannelHandled = true;
    597 
    598  MaybeScheduleRegistrationUpdate();
    599 }
    600 
    601 /**
    602 * This corresponds to the "Handle Fetch" algorithm's steps 20.3, 21.2, and
    603 * 22.2:
    604 *
    605 * "If request is a non-subresource request, or request is a subresource
    606 * request and the time difference in seconds calculated by the current time
    607 * minus registration's last update check time is greater than 86400, invoke
    608 * Soft Update algorithm with registration."
    609 */
    610 void FetchEventOpChild::MaybeScheduleRegistrationUpdate() const {
    611  AssertIsOnMainThread();
    612  MOZ_ASSERT(mRegistration);
    613  MOZ_ASSERT(mInterceptedChannelHandled);
    614 
    615  if (mArgs.common().isNonSubresourceRequest()) {
    616    mRegistration->MaybeScheduleUpdate();
    617  } else {
    618    mRegistration->MaybeScheduleTimeCheckAndUpdate();
    619  }
    620 }
    621 
    622 }  // namespace mozilla::dom