tor-browser

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

CacheOpChild.cpp (12673B)


      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 "mozilla/dom/cache/CacheOpChild.h"
      8 
      9 #include "mozilla/dom/Promise.h"
     10 #include "mozilla/dom/Request.h"
     11 #include "mozilla/dom/Response.h"
     12 #include "mozilla/dom/cache/BoundStorageKey.h"
     13 #include "mozilla/dom/cache/BoundStorageKeyCache.h"
     14 #include "mozilla/dom/cache/Cache.h"
     15 #include "mozilla/dom/cache/CacheChild.h"
     16 #include "mozilla/dom/cache/CacheStreamControlChild.h"
     17 #include "mozilla/dom/cache/CacheWorkerRef.h"
     18 
     19 namespace mozilla::dom {
     20 // XXX Move this to ToJSValue.h
     21 template <typename T>
     22 [[nodiscard]] bool ToJSValue(JSContext* aCx, const SafeRefPtr<T>& aArgument,
     23                             JS::MutableHandle<JS::Value> aValue) {
     24  return ToJSValue(aCx, *aArgument.unsafeGetRawPtr(), aValue);
     25 }
     26 
     27 namespace cache {
     28 
     29 using mozilla::ipc::PBackgroundChild;
     30 using Promise = mozilla::dom::Promise;
     31 
     32 namespace {
     33 
     34 void AddWorkerRefToStreamChild(const CacheReadStream& aReadStream,
     35                               const SafeRefPtr<CacheWorkerRef>& aWorkerRef) {
     36  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerRef);
     37  CacheStreamControlChild* cacheControl =
     38      static_cast<CacheStreamControlChild*>(aReadStream.control().AsChild());
     39  if (cacheControl) {
     40    cacheControl->SetWorkerRef(aWorkerRef.clonePtr());
     41  }
     42 }
     43 
     44 void AddWorkerRefToStreamChild(const CacheResponse& aResponse,
     45                               const SafeRefPtr<CacheWorkerRef>& aWorkerRef) {
     46  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerRef);
     47 
     48  if (aResponse.body().isNothing()) {
     49    return;
     50  }
     51 
     52  AddWorkerRefToStreamChild(aResponse.body().ref(), aWorkerRef);
     53 }
     54 
     55 void AddWorkerRefToStreamChild(const CacheRequest& aRequest,
     56                               const SafeRefPtr<CacheWorkerRef>& aWorkerRef) {
     57  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerRef);
     58 
     59  if (aRequest.body().isNothing()) {
     60    return;
     61  }
     62 
     63  AddWorkerRefToStreamChild(aRequest.body().ref(), aWorkerRef);
     64 }
     65 
     66 }  // namespace
     67 
     68 CacheOpChild::CacheOpChild(SafeRefPtr<CacheWorkerRef> aWorkerRef,
     69                           nsIGlobalObject* aGlobal, nsISupports* aParent,
     70                           RefPtr<Promise>& aPromise, ActorChild* aParentActor)
     71    : mGlobal(aGlobal),
     72      mParent(aParent),
     73      mPromise(aPromise),
     74      mParentActor(aParentActor) {
     75  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
     76  MOZ_DIAGNOSTIC_ASSERT(mParent);
     77  MOZ_DIAGNOSTIC_ASSERT(aPromise);
     78 
     79  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerRef);
     80 
     81  SetWorkerRef(CacheWorkerRef::PreferBehavior(
     82      std::move(aWorkerRef), CacheWorkerRef::eStrongWorkerRef));
     83 }
     84 
     85 CacheOpChild::CacheOpChild(SafeRefPtr<CacheWorkerRef> aWorkerRef,
     86                           nsIGlobalObject* aGlobal, nsISupports* aParent,
     87                           RefPtr<CacheStoragePromise>& aPromise,
     88                           ActorChild* aParentActor)
     89    : mGlobal(aGlobal),
     90      mParent(aParent),
     91      mPromise(aPromise),
     92      mParentActor(aParentActor) {
     93  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
     94  MOZ_DIAGNOSTIC_ASSERT(mParent);
     95  MOZ_DIAGNOSTIC_ASSERT(aPromise);
     96 
     97  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerRef);
     98 
     99  SetWorkerRef(CacheWorkerRef::PreferBehavior(
    100      std::move(aWorkerRef), CacheWorkerRef::eStrongWorkerRef));
    101 }
    102 
    103 CacheOpChild::~CacheOpChild() {
    104  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
    105  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
    106 }
    107 
    108 void CacheOpChild::ActorDestroy(ActorDestroyReason aReason) {
    109  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
    110 
    111  // If the actor was terminated for some unknown reason, then indicate the
    112  // operation is dead.
    113  if (mPromise) {
    114    HandleAndSettle<CacheOpResult::Tvoid_t>(ErrorResult(NS_ERROR_FAILURE));
    115    mPromise.destroy();
    116  }
    117  mParentActor->NoteDeletedActor();
    118  RemoveWorkerRef();
    119 }
    120 
    121 using StorageOpenResultType = std::pair<CacheChild*, Namespace>;
    122 
    123 template <CacheOpResult::Type OP_TYPE, typename ResultType>
    124 void CacheOpChild::SettlePromise(
    125    ResultType&& aRes, ErrorResult&& aRv,
    126    const RefPtr<CacheStoragePromise>& aThePromise) {
    127  // picks the correct promise type using traits defined in BoundStorageKey.h
    128  // and BoundStorageKeyCache.h
    129  using TargetPromiseType =
    130      typename dom::cachestorage_traits<OP_TYPE>::PromiseType;
    131  auto* target = static_cast<TargetPromiseType*>(aThePromise.get());
    132  auto&& res = std::forward<ResultType>(aRes);
    133 
    134  if (aRv.Failed()) {
    135    target->Reject(std::move(aRv), __func__);
    136    return;
    137  }
    138 
    139  using ValueType = typename std::decay_t<decltype(aRes)>;
    140  if constexpr (std::is_same_v<ValueType, JS::HandleValue>) {
    141    // Not serializing JS types into MozPromise. Need to collapse JS values
    142    // into their raw types here. Since this is internal private method and
    143    // based on it's usage yet; just expecting Undefined or null values here.
    144    MOZ_ASSERT(res.isNullOrUndefined());
    145    target->Resolve(nullptr /*implicitly converts to false for boolean types */,
    146                    __func__);
    147  } else if constexpr (std::is_same_v<ValueType, StorageOpenResultType>) {
    148    // result would be of type CacheChild here and we need to properly wrap into
    149    // holder class BoundStorageKeyCache before resolving promise
    150    auto [cacheChild, ns] = res;
    151    auto* cache = new BoundStorageKeyCache(mGlobal, cacheChild, ns);
    152    target->Resolve(RefPtr(cache), __func__);
    153  } else {
    154    target->Resolve(std::forward<ResultType>(aRes), __func__);
    155  }
    156 }
    157 
    158 template <CacheOpResult::Type OP_TYPE, typename ResultType>
    159 void CacheOpChild::SettlePromise(ResultType&& aRes, ErrorResult&& aRv,
    160                                 const RefPtr<Promise>& aThePromise) {
    161  if (aRv.Failed()) {
    162    aThePromise->MaybeReject(aRv.StealNSResult());
    163  } else {
    164    using ValueType = typename std::decay_t<decltype(aRes)>;
    165 
    166    if constexpr (std::is_same_v<ValueType, StorageOpenResultType>) {
    167      // result would be of type CacheChild here and we need to properly wrap
    168      // into holder class Cache before resolving promise
    169      auto&& res = std::forward<ResultType>(aRes);
    170      auto [cacheChild, ns] = res;
    171      auto* cache = new Cache(mGlobal, cacheChild, ns);
    172      aThePromise->MaybeResolve(RefPtr(cache));
    173    } else {
    174      aThePromise->MaybeResolve(std::forward<ResultType>(aRes));
    175    }
    176  }
    177 }
    178 
    179 template <CacheOpResult::Type OP_TYPE, typename ResultType>
    180 void CacheOpChild::Settle(ResultType&& aRes, ErrorResult&& aRv) {
    181  if (mPromise->is<RefPtr<mozilla::dom::Promise>>()) {
    182    auto targetPromise = mPromise->as<RefPtr<mozilla::dom::Promise>>();
    183    MOZ_ASSERT(targetPromise);
    184 
    185    SettlePromise<OP_TYPE>(std::forward<ResultType>(aRes), std::move(aRv),
    186                           targetPromise);
    187  } else if (mPromise->is<RefPtr<CacheStoragePromise>>()) {
    188    auto targetPromise = mPromise->as<RefPtr<CacheStoragePromise>>();
    189    MOZ_ASSERT(targetPromise);
    190 
    191    SettlePromise<OP_TYPE>(std::forward<ResultType>(aRes), std::move(aRv),
    192                           targetPromise);
    193  }
    194 }
    195 
    196 template <CacheOpResult::Type OP_TYPE, typename TResponse>
    197 void CacheOpChild::HandleAndSettle(TResponse&& aRes) {
    198  using ValueType = typename std::decay_t<decltype(aRes)>;
    199  auto&& res = std::forward<TResponse>(aRes);
    200 
    201  if constexpr (std::is_same_v<ValueType, Maybe<CacheResponse>>) {
    202    if (res.isNothing()) {
    203      Settle<OP_TYPE>(JS::UndefinedHandleValue);
    204      return;
    205    }
    206 
    207    const CacheResponse& cacheResponse = res.ref();
    208 
    209    AddWorkerRefToStreamChild(cacheResponse, GetWorkerRefPtr());
    210    RefPtr<Response> response = ToResponse(cacheResponse);
    211    Settle<OP_TYPE>(response);
    212 
    213  } else if constexpr (std::is_same_v<ValueType, nsTArray<CacheResponse>>) {
    214    AutoTArray<RefPtr<Response>, 256> responses;
    215    responses.SetCapacity(res.Length());
    216 
    217    for (uint32_t i = 0; i < res.Length(); ++i) {
    218      AddWorkerRefToStreamChild(res[i], GetWorkerRefPtr());
    219      responses.AppendElement(ToResponse(res[i]));
    220    }
    221 
    222    Settle<OP_TYPE>(std::move(responses));
    223 
    224  } else if constexpr (std::is_same_v<ValueType, nsTArray<CacheRequest>>) {
    225    AutoTArray<SafeRefPtr<Request>, 256> requests;
    226    requests.SetCapacity(res.Length());
    227 
    228    for (uint32_t i = 0; i < res.Length(); ++i) {
    229      AddWorkerRefToStreamChild(res[i], GetWorkerRefPtr());
    230      requests.AppendElement(ToRequest(res[i]));
    231    }
    232 
    233    Settle<OP_TYPE>(std::move(requests));
    234 
    235  } else if constexpr (std::is_same_v<ValueType, bool> ||
    236                       std::is_same_v<ValueType, nsTArray<nsString>> ||
    237                       std::is_same_v<ValueType, JS::Handle<JS::Value>>) {
    238    Settle<OP_TYPE>(std::forward<TResponse>(aRes));
    239  } else if constexpr (std::is_same_v<ValueType, ErrorResult>) {
    240    Settle<OP_TYPE>(false /* valid response does not exist */, std::move(res));
    241  } else if constexpr (std::is_same_v<ValueType, StorageOpenResult>) {
    242    auto* actor = static_cast<CacheChild*>(res.actor().AsChild());
    243 
    244    // If we have a success status then we should have an actor.  Gracefully
    245    // reject instead of crashing, though, if we get a nullptr here.
    246    MOZ_DIAGNOSTIC_ASSERT(actor);
    247    if (!actor) {
    248      ErrorResult errRes;
    249      errRes.ThrowTypeError(
    250          "CacheStorage.open() failed to access the storage system.");
    251      Settle<OP_TYPE>(StorageOpenResultType{} /* dummy */, std::move(errRes));
    252    }
    253 
    254    actor->SetWorkerRef(CacheWorkerRef::PreferBehavior(
    255        GetWorkerRefPtr().clonePtr(), CacheWorkerRef::eIPCWorkerRef));
    256    Settle<OP_TYPE>(StorageOpenResultType{actor, res.ns()});
    257  } else {
    258    static_assert(std::is_same_v<ValueType, void>,
    259                  "There should be a block handling this type");
    260  }
    261 }
    262 
    263 mozilla::ipc::IPCResult CacheOpChild::Recv__delete__(
    264    ErrorResult&& aRv, const CacheOpResult& aResult) {
    265  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
    266 
    267  if (NS_WARN_IF(aRv.Failed())) {
    268    MOZ_DIAGNOSTIC_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
    269    HandleAndSettle<CacheOpResult::Tvoid_t>(aRv);
    270    mPromise.destroy();
    271    return IPC_OK();
    272  }
    273 
    274  switch (aResult.type()) {
    275    case CacheOpResult::TCacheMatchResult: {
    276      const auto& response = aResult.get_CacheMatchResult().maybeResponse();
    277      HandleAndSettle<CacheOpResult::TCacheMatchResult>(response);
    278      break;
    279    }
    280    case CacheOpResult::TCacheMatchAllResult: {
    281      const auto& response = aResult.get_CacheMatchAllResult().responseList();
    282      HandleAndSettle<CacheOpResult::TCacheMatchAllResult>(response);
    283      break;
    284    }
    285    case CacheOpResult::TCachePutAllResult: {
    286      // resolve with undefined
    287      HandleAndSettle<CacheOpResult::TCachePutAllResult>(
    288          JS::UndefinedHandleValue);
    289      break;
    290    }
    291    case CacheOpResult::TCacheDeleteResult: {
    292      const auto& response = aResult.get_CacheDeleteResult().success();
    293      HandleAndSettle<CacheOpResult::TCacheDeleteResult>(response);
    294      break;
    295    }
    296    case CacheOpResult::TCacheKeysResult: {
    297      const auto& response = aResult.get_CacheKeysResult().requestList();
    298      HandleAndSettle<CacheOpResult::TCacheKeysResult>(response);
    299      break;
    300    }
    301    case CacheOpResult::TStorageMatchResult: {
    302      const auto& response = aResult.get_StorageMatchResult().maybeResponse();
    303      HandleAndSettle<CacheOpResult::TStorageMatchResult>(response);
    304      break;
    305    }
    306    case CacheOpResult::TStorageHasResult: {
    307      const auto& response = aResult.get_StorageHasResult().success();
    308      HandleAndSettle<CacheOpResult::TStorageHasResult>(response);
    309      break;
    310    }
    311    case CacheOpResult::TStorageOpenResult: {
    312      const auto& response = aResult.get_StorageOpenResult();
    313      HandleAndSettle<CacheOpResult::TStorageOpenResult>(response);
    314      break;
    315    }
    316    case CacheOpResult::TStorageDeleteResult: {
    317      const auto& response = aResult.get_StorageDeleteResult().success();
    318      HandleAndSettle<CacheOpResult::TStorageDeleteResult>(response);
    319      break;
    320    }
    321    case CacheOpResult::TStorageKeysResult: {
    322      const auto& response = aResult.get_StorageKeysResult().keyList();
    323      HandleAndSettle<CacheOpResult::TStorageKeysResult>(response);
    324      break;
    325    }
    326    default:
    327      MOZ_CRASH("Unknown Cache op result type!");
    328  }
    329 
    330  mPromise.destroy();
    331  return IPC_OK();
    332 }
    333 
    334 void CacheOpChild::StartDestroy() {
    335  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
    336 
    337  // Do not cancel on-going operations when WorkerRef calls this.  Instead,
    338  // keep the Worker alive until we are done.
    339 }
    340 
    341 nsIGlobalObject* CacheOpChild::GetGlobalObject() const { return mGlobal; }
    342 
    343 #ifdef DEBUG
    344 void CacheOpChild::AssertOwningThread() const {
    345  NS_ASSERT_OWNINGTHREAD(CacheOpChild);
    346 }
    347 #endif
    348 
    349 }  // namespace cache
    350 }  // namespace mozilla::dom