tor-browser

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

FetchEventOpProxyChild.cpp (9777B)


      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 "FetchEventOpProxyChild.h"
      8 
      9 #include <utility>
     10 
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/RefPtr.h"
     13 #include "mozilla/dom/FetchTypes.h"
     14 #include "mozilla/dom/InternalRequest.h"
     15 #include "mozilla/dom/InternalResponse.h"
     16 #include "mozilla/dom/RemoteWorkerChild.h"
     17 #include "mozilla/dom/RemoteWorkerService.h"
     18 #include "mozilla/dom/ServiceWorkerOp.h"
     19 #include "mozilla/dom/ServiceWorkerOpPromise.h"
     20 #include "mozilla/dom/WorkerCommon.h"
     21 #include "mozilla/ipc/BackgroundChild.h"
     22 #include "mozilla/ipc/IPCStreamUtils.h"
     23 #include "nsCOMPtr.h"
     24 #include "nsDebug.h"
     25 #include "nsThreadUtils.h"
     26 
     27 namespace mozilla {
     28 
     29 using namespace ipc;
     30 
     31 namespace dom {
     32 
     33 namespace {
     34 
     35 nsresult GetIPCSynthesizeResponseArgs(
     36    ChildToParentSynthesizeResponseArgs* aIPCArgs,
     37    SynthesizeResponseArgs&& aArgs) {
     38  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
     39 
     40  auto [internalResponse, closure, timeStamps] = std::move(aArgs);
     41 
     42  aIPCArgs->closure() = std::move(closure);
     43  aIPCArgs->timeStamps() = std::move(timeStamps);
     44 
     45  PBackgroundChild* bgChild = BackgroundChild::GetOrCreateForCurrentThread();
     46 
     47  if (NS_WARN_IF(!bgChild)) {
     48    return NS_ERROR_DOM_INVALID_STATE_ERR;
     49  }
     50 
     51  internalResponse->ToChildToParentInternalResponse(
     52      &aIPCArgs->internalResponse(), bgChild);
     53  return NS_OK;
     54 }
     55 
     56 }  // anonymous namespace
     57 
     58 void FetchEventOpProxyChild::Initialize(
     59    const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
     60  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
     61  MOZ_ASSERT(!mOp);
     62 
     63  mInternalRequest =
     64      MakeSafeRefPtr<InternalRequest>(aArgs.common().internalRequest());
     65 
     66  if (aArgs.common().preloadNavigation()) {
     67    // We use synchronous task dispatch here to make sure that if the preload
     68    // response arrived before we dispatch the fetch event, then the JS preload
     69    // response promise will get resolved immediately.
     70    mPreloadResponseAvailablePromise =
     71        MakeRefPtr<FetchEventPreloadResponseAvailablePromise::Private>(
     72            __func__);
     73    mPreloadResponseAvailablePromise->UseDirectTaskDispatch(__func__);
     74    if (aArgs.preloadResponse().isSome()) {
     75      mPreloadResponseAvailablePromiseResolved = true;
     76      mPreloadResponseAvailablePromise->Resolve(
     77          InternalResponse::FromIPC(aArgs.preloadResponse().ref()), __func__);
     78    }
     79 
     80    mPreloadResponseTimingPromise =
     81        MakeRefPtr<FetchEventPreloadResponseTimingPromise::Private>(__func__);
     82    mPreloadResponseTimingPromise->UseDirectTaskDispatch(__func__);
     83    if (aArgs.preloadResponseTiming().isSome()) {
     84      mPreloadResponseTimingPromise->Resolve(
     85          aArgs.preloadResponseTiming().ref(), __func__);
     86    }
     87 
     88    mPreloadResponseEndPromise =
     89        MakeRefPtr<FetchEventPreloadResponseEndPromise::Private>(__func__);
     90    mPreloadResponseEndPromise->UseDirectTaskDispatch(__func__);
     91    if (aArgs.preloadResponseEndArgs().isSome()) {
     92      mPreloadResponseEndPromiseResolved = true;
     93      mPreloadResponseEndPromise->Resolve(aArgs.preloadResponseEndArgs().ref(),
     94                                          __func__);
     95    }
     96  }
     97 
     98  RemoteWorkerChild* manager = static_cast<RemoteWorkerChild*>(Manager());
     99  MOZ_ASSERT(manager);
    100 
    101  RefPtr<FetchEventOpProxyChild> self = this;
    102 
    103  auto callback = [self](const ServiceWorkerOpResult& aResult) {
    104    // FetchEventOp could finish before NavigationPreload fetch finishes.
    105    // If NavigationPreload is available in FetchEvent, caching FetchEventOp
    106    // result until RecvPreloadResponseEnd is called, such that the preload
    107    // response could be completed.
    108    if (self->mPreloadResponseEndPromise &&
    109        !self->mPreloadResponseEndPromiseResolved &&
    110        self->mPreloadResponseAvailablePromiseResolved) {
    111      self->mCachedOpResult = Some(aResult);
    112      return;
    113    }
    114    if (!self->CanSend()) {
    115      return;
    116    }
    117 
    118    if (NS_WARN_IF(aResult.type() == ServiceWorkerOpResult::Tnsresult)) {
    119      (void)self->Send__delete__(self, aResult.get_nsresult());
    120      return;
    121    }
    122 
    123    MOZ_ASSERT(aResult.type() ==
    124               ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult);
    125 
    126    (void)self->Send__delete__(self, aResult);
    127  };
    128 
    129  RefPtr<FetchEventOp> op = ServiceWorkerOp::Create(aArgs, std::move(callback))
    130                                .template downcast<FetchEventOp>();
    131 
    132  MOZ_ASSERT(op);
    133 
    134  op->SetActor(this);
    135  mOp = op;
    136 
    137  op->GetRespondWithPromise()
    138      ->Then(GetCurrentSerialEventTarget(), __func__,
    139             [self = std::move(self)](
    140                 FetchEventRespondWithPromise::ResolveOrRejectValue&& aResult) {
    141               self->mRespondWithPromiseRequestHolder.Complete();
    142 
    143               if (NS_WARN_IF(aResult.IsReject())) {
    144                 MOZ_ASSERT(NS_FAILED(aResult.RejectValue().status()));
    145 
    146                 (void)self->SendRespondWith(aResult.RejectValue());
    147                 return;
    148               }
    149 
    150               auto& result = aResult.ResolveValue();
    151 
    152               if (result.is<SynthesizeResponseArgs>()) {
    153                 ChildToParentSynthesizeResponseArgs ipcArgs;
    154                 nsresult rv = GetIPCSynthesizeResponseArgs(
    155                     &ipcArgs, result.extract<SynthesizeResponseArgs>());
    156 
    157                 if (NS_WARN_IF(NS_FAILED(rv))) {
    158                   (void)self->SendRespondWith(
    159                       CancelInterceptionArgs(rv, ipcArgs.timeStamps()));
    160                   return;
    161                 }
    162 
    163                 (void)self->SendRespondWith(ipcArgs);
    164               } else if (result.is<ResetInterceptionArgs>()) {
    165                 (void)self->SendRespondWith(
    166                     result.extract<ResetInterceptionArgs>());
    167               } else {
    168                 (void)self->SendRespondWith(
    169                     result.extract<CancelInterceptionArgs>());
    170               }
    171             })
    172      ->Track(mRespondWithPromiseRequestHolder);
    173 
    174  manager->MaybeStartOp(std::move(op));
    175 }
    176 
    177 SafeRefPtr<InternalRequest> FetchEventOpProxyChild::ExtractInternalRequest() {
    178  MOZ_ASSERT(IsCurrentThreadRunningWorker());
    179  MOZ_ASSERT(mInternalRequest);
    180 
    181  return std::move(mInternalRequest);
    182 }
    183 
    184 RefPtr<FetchEventPreloadResponseAvailablePromise>
    185 FetchEventOpProxyChild::GetPreloadResponseAvailablePromise() {
    186  return mPreloadResponseAvailablePromise;
    187 }
    188 
    189 RefPtr<FetchEventPreloadResponseTimingPromise>
    190 FetchEventOpProxyChild::GetPreloadResponseTimingPromise() {
    191  return mPreloadResponseTimingPromise;
    192 }
    193 
    194 RefPtr<FetchEventPreloadResponseEndPromise>
    195 FetchEventOpProxyChild::GetPreloadResponseEndPromise() {
    196  return mPreloadResponseEndPromise;
    197 }
    198 
    199 mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponse(
    200    ParentToChildInternalResponse&& aResponse) {
    201  // Receiving this message implies that navigation preload is enabled, so
    202  // Initialize() should have created this promise.
    203  MOZ_ASSERT(mPreloadResponseAvailablePromise);
    204 
    205  mPreloadResponseAvailablePromiseResolved = true;
    206  mPreloadResponseAvailablePromise->Resolve(
    207      InternalResponse::FromIPC(aResponse), __func__);
    208 
    209  return IPC_OK();
    210 }
    211 
    212 mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponseTiming(
    213    ResponseTiming&& aTiming) {
    214  // Receiving this message implies that navigation preload is enabled, so
    215  // Initialize() should have created this promise.
    216  MOZ_ASSERT(mPreloadResponseTimingPromise);
    217 
    218  mPreloadResponseTimingPromise->Resolve(std::move(aTiming), __func__);
    219  return IPC_OK();
    220 }
    221 
    222 mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponseEnd(
    223    ResponseEndArgs&& aArgs) {
    224  // Receiving this message implies that navigation preload is enabled, so
    225  // Initialize() should have created this promise.
    226  MOZ_ASSERT(mPreloadResponseEndPromise);
    227 
    228  mPreloadResponseEndPromiseResolved = true;
    229  mPreloadResponseEndPromise->Resolve(std::move(aArgs), __func__);
    230  // If mCachedOpResult is not nothing, it means FetchEventOp had already done
    231  // and the operation result is cached. Continue closing IPC here.
    232  if (mCachedOpResult.isNothing()) {
    233    return IPC_OK();
    234  }
    235 
    236  if (!CanSend()) {
    237    return IPC_OK();
    238  }
    239 
    240  if (NS_WARN_IF(mCachedOpResult.ref().type() ==
    241                 ServiceWorkerOpResult::Tnsresult)) {
    242    (void)Send__delete__(this, mCachedOpResult.ref().get_nsresult());
    243    return IPC_OK();
    244  }
    245 
    246  MOZ_ASSERT(mCachedOpResult.ref().type() ==
    247             ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult);
    248 
    249  (void)Send__delete__(this, mCachedOpResult.ref());
    250 
    251  return IPC_OK();
    252 }
    253 
    254 void FetchEventOpProxyChild::ActorDestroy(ActorDestroyReason) {
    255  (void)NS_WARN_IF(mRespondWithPromiseRequestHolder.Exists());
    256  mRespondWithPromiseRequestHolder.DisconnectIfExists();
    257 
    258  // If mPreloadResponseAvailablePromise exists, navigation preloading response
    259  // will not be valid anymore since it is too late to respond to the
    260  // FetchEvent. Resolve the preload response promise with
    261  // NS_ERROR_DOM_ABORT_ERR.
    262  if (mPreloadResponseAvailablePromise) {
    263    mPreloadResponseAvailablePromiseResolved = true;
    264    mPreloadResponseAvailablePromise->Resolve(
    265        InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
    266  }
    267 
    268  if (mPreloadResponseTimingPromise) {
    269    mPreloadResponseTimingPromise->Resolve(ResponseTiming(), __func__);
    270  }
    271 
    272  if (mPreloadResponseEndPromise) {
    273    mPreloadResponseEndPromiseResolved = true;
    274    ResponseEndArgs args(FetchDriverObserver::eAborted);
    275    mPreloadResponseEndPromise->Resolve(args, __func__);
    276  }
    277 
    278  mOp->RevokeActor(this);
    279  mOp = nullptr;
    280 }
    281 
    282 }  // namespace dom
    283 }  // namespace mozilla