tor-browser

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

Cache.cpp (19191B)


      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/Cache.h"
      8 
      9 #include "js/Array.h"               // JS::GetArrayLength, JS::IsArrayObject
     10 #include "js/PropertyAndElement.h"  // JS_GetElement
     11 #include "mozilla/ErrorResult.h"
     12 #include "mozilla/Preferences.h"
     13 #include "mozilla/dom/CacheBinding.h"
     14 #include "mozilla/dom/Headers.h"
     15 #include "mozilla/dom/InternalResponse.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "mozilla/dom/PromiseNativeHandler.h"
     18 #include "mozilla/dom/Response.h"
     19 #include "mozilla/dom/RootedDictionary.h"
     20 #include "mozilla/dom/ServiceWorkerUtils.h"
     21 #include "mozilla/dom/WorkerPrivate.h"
     22 #include "mozilla/dom/cache/AutoUtils.h"
     23 #include "mozilla/dom/cache/CacheChild.h"
     24 #include "mozilla/dom/cache/CacheCommon.h"
     25 #include "mozilla/dom/cache/CacheWorkerRef.h"
     26 #include "mozilla/dom/quota/ResultExtensions.h"
     27 #include "nsIGlobalObject.h"
     28 
     29 namespace mozilla::dom::cache {
     30 
     31 using mozilla::ipc::PBackgroundChild;
     32 
     33 bool IsValidPutRequestURL(const nsACString& aUrl, ErrorResult& aRv) {
     34  bool validScheme = false;
     35 
     36  // make a copy because ProcessURL strips the fragmet
     37  nsAutoCString url(aUrl);
     38  TypeUtils::ProcessURL(url, &validScheme, nullptr, nullptr, aRv);
     39  if (aRv.Failed()) {
     40    return false;
     41  }
     42 
     43  if (!validScheme) {
     44    // `url` has been modified, so don't use it here.
     45    aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Request", aUrl);
     46    return false;
     47  }
     48 
     49  return true;
     50 }
     51 
     52 bool IsValidPutRequestMethod(const Request& aRequest, ErrorResult& aRv) {
     53  nsAutoCString method;
     54  aRequest.GetMethod(method);
     55  if (!method.LowerCaseEqualsLiteral("get")) {
     56    aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(method);
     57    return false;
     58  }
     59 
     60  return true;
     61 }
     62 
     63 bool IsValidPutRequestMethod(const RequestOrUTF8String& aRequest,
     64                             ErrorResult& aRv) {
     65  // If the provided request is a string URL, then it will default to
     66  // a valid http method automatically.
     67  if (!aRequest.IsRequest()) {
     68    return true;
     69  }
     70  return IsValidPutRequestMethod(aRequest.GetAsRequest(), aRv);
     71 }
     72 
     73 bool IsValidPutResponseStatus(Response& aResponse, PutStatusPolicy aPolicy,
     74                              ErrorResult& aRv) {
     75  if ((aPolicy == PutStatusPolicy::RequireOK && !aResponse.Ok()) ||
     76      aResponse.Status() == 206) {
     77    nsAutoCString url;
     78    aResponse.GetUrl(url);
     79    aRv.ThrowTypeError<MSG_CACHE_ADD_FAILED_RESPONSE>(
     80        GetEnumString(aResponse.Type()), IntToCString(aResponse.Status()), url);
     81    return false;
     82  }
     83 
     84  return true;
     85 }
     86 
     87 // Helper class to wait for Add()/AddAll() fetch requests to complete and
     88 // then perform a PutAll() with the responses.  This class holds a WorkerRef
     89 // to keep the Worker thread alive.  This is mainly to ensure that Add/AddAll
     90 // act the same as other Cache operations that directly create a CacheOpChild
     91 // actor.
     92 class Cache::FetchHandler final : public PromiseNativeHandler {
     93 public:
     94  FetchHandler(SafeRefPtr<CacheWorkerRef> aWorkerRef, Cache* aCache,
     95               nsTArray<SafeRefPtr<Request>>&& aRequestList, Promise* aPromise)
     96      : mWorkerRef(std::move(aWorkerRef)),
     97        mCache(aCache),
     98        mRequestList(std::move(aRequestList)),
     99        mPromise(aPromise) {
    100    MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
    101    MOZ_DIAGNOSTIC_ASSERT(mCache);
    102    MOZ_DIAGNOSTIC_ASSERT(mPromise);
    103  }
    104 
    105  virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    106                                ErrorResult& aRv) override {
    107    NS_ASSERT_OWNINGTHREAD(FetchHandler);
    108 
    109    // Stop holding the worker alive when we leave this method.
    110    const SafeRefPtr<CacheWorkerRef> workerRef = std::move(mWorkerRef);
    111 
    112    // Promise::All() passed an array of fetch() Promises should give us
    113    // an Array of Response objects.  The following code unwraps these
    114    // JS values back to an nsTArray<RefPtr<Response>>.
    115 
    116    AutoTArray<RefPtr<Response>, 256> responseList;
    117    responseList.SetCapacity(mRequestList.Length());
    118 
    119    const auto failOnErr = [this](const auto) { Fail(); };
    120 
    121    bool isArray;
    122    QM_TRY(OkIf(JS::IsArrayObject(aCx, aValue, &isArray)), QM_VOID, failOnErr);
    123    QM_TRY(OkIf(isArray), QM_VOID, failOnErr);
    124 
    125    JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
    126 
    127    uint32_t length;
    128    QM_TRY(OkIf(JS::GetArrayLength(aCx, obj, &length)), QM_VOID, failOnErr);
    129 
    130    for (uint32_t i = 0; i < length; ++i) {
    131      JS::Rooted<JS::Value> value(aCx);
    132 
    133      QM_TRY(OkIf(JS_GetElement(aCx, obj, i, &value)), QM_VOID, failOnErr);
    134 
    135      QM_TRY(OkIf(value.isObject()), QM_VOID, failOnErr);
    136 
    137      JS::Rooted<JSObject*> responseObj(aCx, &value.toObject());
    138 
    139      RefPtr<Response> response;
    140      QM_TRY(MOZ_TO_RESULT(UNWRAP_OBJECT(Response, responseObj, response)),
    141             QM_VOID, failOnErr);
    142 
    143      QM_TRY(OkIf(response->Type() != ResponseType::Error), QM_VOID, failOnErr);
    144 
    145      // Do not allow the convenience methods .add()/.addAll() to store failed
    146      // or invalid responses.  A consequence of this is that these methods
    147      // cannot be used to store opaque or opaqueredirect responses since they
    148      // always expose a 0 status value.
    149      ErrorResult errorResult;
    150      if (!IsValidPutResponseStatus(*response, PutStatusPolicy::RequireOK,
    151                                    errorResult)) {
    152        // TODO: abort the fetch requests we have running (bug 1157434)
    153        mPromise->MaybeReject(std::move(errorResult));
    154        return;
    155      }
    156 
    157      responseList.AppendElement(std::move(response));
    158    }
    159 
    160    MOZ_DIAGNOSTIC_ASSERT(mRequestList.Length() == responseList.Length());
    161 
    162    // Now store the unwrapped Response list in the Cache.
    163    ErrorResult result;
    164    // TODO: Here we use the JSContext as received by the ResolvedCallback, and
    165    // its state could be the wrong one. The spec doesn't say anything
    166    // about it, yet (bug 1384006)
    167    RefPtr<Promise> put =
    168        mCache->PutAll(aCx, mRequestList, responseList, result);
    169    result.WouldReportJSException();
    170    if (NS_WARN_IF(result.Failed())) {
    171      // TODO: abort the fetch requests we have running (bug 1157434)
    172      mPromise->MaybeReject(std::move(result));
    173      return;
    174    }
    175 
    176    // Chain the Cache::Put() promise to the original promise returned to
    177    // the content script.
    178    mPromise->MaybeResolve(put);
    179  }
    180 
    181  virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    182                                ErrorResult& aRv) override {
    183    NS_ASSERT_OWNINGTHREAD(FetchHandler);
    184    Fail();
    185  }
    186 
    187 private:
    188  ~FetchHandler() = default;
    189 
    190  void Fail() { mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>(); }
    191 
    192  SafeRefPtr<CacheWorkerRef> mWorkerRef;
    193  RefPtr<Cache> mCache;
    194  nsTArray<SafeRefPtr<Request>> mRequestList;
    195  RefPtr<Promise> mPromise;
    196 
    197  NS_DECL_ISUPPORTS
    198 };
    199 
    200 NS_IMPL_ISUPPORTS0(Cache::FetchHandler)
    201 
    202 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::Cache);
    203 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache);
    204 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::Cache, mGlobal);
    205 
    206 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
    207  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    208  NS_INTERFACE_MAP_ENTRY(nsISupports)
    209 NS_INTERFACE_MAP_END
    210 
    211 Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor, Namespace aNamespace)
    212    : mGlobal(aGlobal), mActor(aActor), mNamespace(aNamespace) {
    213  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
    214  MOZ_DIAGNOSTIC_ASSERT(mActor);
    215  MOZ_DIAGNOSTIC_ASSERT(mNamespace != INVALID_NAMESPACE);
    216  mActor->SetListener(this);
    217 }
    218 
    219 // static
    220 bool Cache::CachesEnabled(JSContext* aCx, JSObject* aObj) {
    221  if (!IsSecureContextOrObjectIsFromSecureContext(aCx, aObj)) {
    222    return StaticPrefs::dom_caches_testing_enabled() ||
    223           ServiceWorkersEnabled(aCx, aObj);
    224  }
    225  return true;
    226 }
    227 
    228 already_AddRefed<Promise> Cache::Match(JSContext* aCx,
    229                                       const RequestOrUTF8String& aRequest,
    230                                       const CacheQueryOptions& aOptions,
    231                                       ErrorResult& aRv) {
    232  if (NS_WARN_IF(!mActor)) {
    233    aRv.Throw(NS_ERROR_UNEXPECTED);
    234    return nullptr;
    235  }
    236 
    237  CacheChild::AutoLock actorLock(*mActor);
    238 
    239  SafeRefPtr<InternalRequest> ir =
    240      ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
    241  if (NS_WARN_IF(aRv.Failed())) {
    242    return nullptr;
    243  }
    244 
    245  CacheQueryParams params;
    246  ToCacheQueryParams(params, aOptions);
    247 
    248  AutoChildOpArgs args(
    249      this, CacheMatchArgs(CacheRequest(), params, GetOpenMode()), 1);
    250 
    251  args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv);
    252  if (NS_WARN_IF(aRv.Failed())) {
    253    return nullptr;
    254  }
    255 
    256  return ExecuteOp(args, aRv);
    257 }
    258 
    259 already_AddRefed<Promise> Cache::MatchAll(
    260    JSContext* aCx, const Optional<RequestOrUTF8String>& aRequest,
    261    const CacheQueryOptions& aOptions, ErrorResult& aRv) {
    262  if (NS_WARN_IF(!mActor)) {
    263    aRv.Throw(NS_ERROR_UNEXPECTED);
    264    return nullptr;
    265  }
    266 
    267  CacheChild::AutoLock actorLock(*mActor);
    268 
    269  CacheQueryParams params;
    270  ToCacheQueryParams(params, aOptions);
    271 
    272  AutoChildOpArgs args(this,
    273                       CacheMatchAllArgs(Nothing(), params, GetOpenMode()), 1);
    274 
    275  if (aRequest.WasPassed()) {
    276    SafeRefPtr<InternalRequest> ir =
    277        ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
    278    if (aRv.Failed()) {
    279      return nullptr;
    280    }
    281 
    282    args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv);
    283    if (aRv.Failed()) {
    284      return nullptr;
    285    }
    286  }
    287 
    288  return ExecuteOp(args, aRv);
    289 }
    290 
    291 already_AddRefed<Promise> Cache::Add(JSContext* aContext,
    292                                     const RequestOrUTF8String& aRequest,
    293                                     CallerType aCallerType, ErrorResult& aRv) {
    294  if (NS_WARN_IF(!mActor)) {
    295    aRv.Throw(NS_ERROR_UNEXPECTED);
    296    return nullptr;
    297  }
    298 
    299  CacheChild::AutoLock actorLock(*mActor);
    300 
    301  if (!IsValidPutRequestMethod(aRequest, aRv)) {
    302    return nullptr;
    303  }
    304 
    305  GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
    306  MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
    307 
    308  nsTArray<SafeRefPtr<Request>> requestList(1);
    309  RootedDictionary<RequestInit> requestInit(aContext);
    310  SafeRefPtr<Request> request =
    311      Request::Constructor(global, aRequest, requestInit, aRv);
    312  if (NS_WARN_IF(aRv.Failed())) {
    313    return nullptr;
    314  }
    315 
    316  nsAutoCString url;
    317  request->GetUrl(url);
    318  if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) {
    319    return nullptr;
    320  }
    321 
    322  requestList.AppendElement(std::move(request));
    323  return AddAll(global, std::move(requestList), aCallerType, aRv);
    324 }
    325 
    326 already_AddRefed<Promise> Cache::AddAll(
    327    JSContext* aContext,
    328    const Sequence<OwningRequestOrUTF8String>& aRequestList,
    329    CallerType aCallerType, ErrorResult& aRv) {
    330  if (NS_WARN_IF(!mActor)) {
    331    aRv.Throw(NS_ERROR_UNEXPECTED);
    332    return nullptr;
    333  }
    334 
    335  CacheChild::AutoLock actorLock(*mActor);
    336 
    337  GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
    338  MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
    339 
    340  nsTArray<SafeRefPtr<Request>> requestList(aRequestList.Length());
    341  for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
    342    RequestOrUTF8String requestOrString;
    343 
    344    if (aRequestList[i].IsRequest()) {
    345      requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest();
    346      if (NS_WARN_IF(
    347              !IsValidPutRequestMethod(requestOrString.GetAsRequest(), aRv))) {
    348        return nullptr;
    349      }
    350    } else {
    351      requestOrString.SetAsUTF8String().ShareOrDependUpon(
    352          aRequestList[i].GetAsUTF8String());
    353    }
    354 
    355    RootedDictionary<RequestInit> requestInit(aContext);
    356    SafeRefPtr<Request> request =
    357        Request::Constructor(global, requestOrString, requestInit, aRv);
    358    if (NS_WARN_IF(aRv.Failed())) {
    359      return nullptr;
    360    }
    361 
    362    nsAutoCString url;
    363    request->GetUrl(url);
    364    if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) {
    365      return nullptr;
    366    }
    367 
    368    requestList.AppendElement(std::move(request));
    369  }
    370 
    371  return AddAll(global, std::move(requestList), aCallerType, aRv);
    372 }
    373 
    374 already_AddRefed<Promise> Cache::Put(JSContext* aCx,
    375                                     const RequestOrUTF8String& aRequest,
    376                                     Response& aResponse, ErrorResult& aRv) {
    377  if (NS_WARN_IF(!mActor)) {
    378    aRv.Throw(NS_ERROR_UNEXPECTED);
    379    return nullptr;
    380  }
    381 
    382  CacheChild::AutoLock actorLock(*mActor);
    383 
    384  if (NS_WARN_IF(!IsValidPutRequestMethod(aRequest, aRv))) {
    385    return nullptr;
    386  }
    387 
    388  if (!IsValidPutResponseStatus(aResponse, PutStatusPolicy::Default, aRv)) {
    389    return nullptr;
    390  }
    391 
    392  if (NS_WARN_IF(aResponse.GetPrincipalInfo() &&
    393                 aResponse.GetPrincipalInfo()->type() ==
    394                     mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo)) {
    395    // WebExtensions Content Scripts can currently run fetch from their global
    396    // which will end up to have an expanded principal, but we require that the
    397    // contents of Cache storage for the content origin to be same-origin, and
    398    // never an expanded principal (See Bug 1753810).
    399    aRv.ThrowSecurityError("Disallowed on WebExtension ContentScript Request");
    400    return nullptr;
    401  }
    402 
    403  SafeRefPtr<InternalRequest> ir =
    404      ToInternalRequest(aCx, aRequest, ReadBody, aRv);
    405  if (NS_WARN_IF(aRv.Failed())) {
    406    return nullptr;
    407  }
    408 
    409  AutoChildOpArgs args(this, CachePutAllArgs(), 1);
    410 
    411  args.Add(aCx, *ir, ReadBody, TypeErrorOnInvalidScheme, aResponse, aRv);
    412  if (NS_WARN_IF(aRv.Failed())) {
    413    return nullptr;
    414  }
    415 
    416  return ExecuteOp(args, aRv);
    417 }
    418 
    419 already_AddRefed<Promise> Cache::Delete(JSContext* aCx,
    420                                        const RequestOrUTF8String& aRequest,
    421                                        const CacheQueryOptions& aOptions,
    422                                        ErrorResult& aRv) {
    423  if (NS_WARN_IF(!mActor)) {
    424    aRv.Throw(NS_ERROR_UNEXPECTED);
    425    return nullptr;
    426  }
    427 
    428  CacheChild::AutoLock actorLock(*mActor);
    429 
    430  SafeRefPtr<InternalRequest> ir =
    431      ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
    432  if (NS_WARN_IF(aRv.Failed())) {
    433    return nullptr;
    434  }
    435 
    436  CacheQueryParams params;
    437  ToCacheQueryParams(params, aOptions);
    438 
    439  AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params), 1);
    440 
    441  args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv);
    442  if (NS_WARN_IF(aRv.Failed())) {
    443    return nullptr;
    444  }
    445 
    446  return ExecuteOp(args, aRv);
    447 }
    448 
    449 already_AddRefed<Promise> Cache::Keys(
    450    JSContext* aCx, const Optional<RequestOrUTF8String>& aRequest,
    451    const CacheQueryOptions& aOptions, ErrorResult& aRv) {
    452  if (NS_WARN_IF(!mActor)) {
    453    aRv.Throw(NS_ERROR_UNEXPECTED);
    454    return nullptr;
    455  }
    456 
    457  CacheChild::AutoLock actorLock(*mActor);
    458 
    459  CacheQueryParams params;
    460  ToCacheQueryParams(params, aOptions);
    461 
    462  AutoChildOpArgs args(this, CacheKeysArgs(Nothing(), params, GetOpenMode()),
    463                       1);
    464 
    465  if (aRequest.WasPassed()) {
    466    SafeRefPtr<InternalRequest> ir =
    467        ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
    468    if (NS_WARN_IF(aRv.Failed())) {
    469      return nullptr;
    470    }
    471 
    472    args.Add(*ir, IgnoreBody, IgnoreInvalidScheme, aRv);
    473    if (NS_WARN_IF(aRv.Failed())) {
    474      return nullptr;
    475    }
    476  }
    477 
    478  return ExecuteOp(args, aRv);
    479 }
    480 
    481 nsISupports* Cache::GetParentObject() const { return mGlobal; }
    482 
    483 JSObject* Cache::WrapObject(JSContext* aContext,
    484                            JS::Handle<JSObject*> aGivenProto) {
    485  return Cache_Binding::Wrap(aContext, this, aGivenProto);
    486 }
    487 
    488 void Cache::OnActorDestroy(CacheChild* aActor) {
    489  MOZ_DIAGNOSTIC_ASSERT(mActor);
    490  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
    491  mActor->ClearListener();
    492  mActor = nullptr;
    493 }
    494 
    495 nsIGlobalObject* Cache::GetGlobalObject() const { return mGlobal; }
    496 
    497 #ifdef DEBUG
    498 void Cache::AssertOwningThread() const { NS_ASSERT_OWNINGTHREAD(Cache); }
    499 #endif
    500 
    501 Cache::~Cache() {
    502  NS_ASSERT_OWNINGTHREAD(Cache);
    503  if (mActor) {
    504    mActor->StartDestroyFromListener();
    505    // OnActorDestroy() is called synchronously by StartDestroyFromListener().
    506    // So we should have already cleared the mActor.
    507    MOZ_DIAGNOSTIC_ASSERT(!mActor);
    508  }
    509 }
    510 
    511 already_AddRefed<Promise> Cache::ExecuteOp(AutoChildOpArgs& aOpArgs,
    512                                           ErrorResult& aRv) {
    513  MOZ_DIAGNOSTIC_ASSERT(mActor);
    514 
    515  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    516  if (NS_WARN_IF(!promise)) {
    517    return nullptr;
    518  }
    519 
    520  mActor->ExecuteOp(mGlobal, promise, this, aOpArgs.SendAsOpArgs());
    521  return promise.forget();
    522 }
    523 
    524 already_AddRefed<Promise> Cache::AddAll(
    525    const GlobalObject& aGlobal, nsTArray<SafeRefPtr<Request>>&& aRequestList,
    526    CallerType aCallerType, ErrorResult& aRv) {
    527  MOZ_DIAGNOSTIC_ASSERT(mActor);
    528 
    529  // If there is no work to do, then resolve immediately
    530  if (aRequestList.IsEmpty()) {
    531    RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    532    if (NS_WARN_IF(!promise)) {
    533      return nullptr;
    534    }
    535 
    536    promise->MaybeResolveWithUndefined();
    537    return promise.forget();
    538  }
    539 
    540  AutoTArray<RefPtr<Promise>, 256> fetchList;
    541  fetchList.SetCapacity(aRequestList.Length());
    542 
    543  // Begin fetching each request in parallel.  For now, if an error occurs just
    544  // abandon our previous fetch calls.  In theory we could cancel them in the
    545  // future once fetch supports it.
    546 
    547  for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
    548    RequestOrUTF8String requestOrString;
    549    requestOrString.SetAsRequest() = aRequestList[i].unsafeGetRawPtr();
    550    RootedDictionary<RequestInit> requestInit(aGlobal.Context());
    551    RefPtr<Promise> fetch =
    552        FetchRequest(mGlobal, requestOrString, requestInit, aCallerType, aRv);
    553    if (NS_WARN_IF(aRv.Failed())) {
    554      return nullptr;
    555    }
    556 
    557    fetchList.AppendElement(std::move(fetch));
    558  }
    559 
    560  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    561  if (NS_WARN_IF(aRv.Failed())) {
    562    return nullptr;
    563  }
    564 
    565  RefPtr<FetchHandler> handler =
    566      new FetchHandler(mActor->GetWorkerRefPtr().clonePtr(), this,
    567                       std::move(aRequestList), promise);
    568 
    569  RefPtr<Promise> fetchPromise =
    570      Promise::All(aGlobal.Context(), fetchList, aRv);
    571  if (NS_WARN_IF(aRv.Failed())) {
    572    return nullptr;
    573  }
    574  fetchPromise->AppendNativeHandler(handler);
    575 
    576  return promise.forget();
    577 }
    578 
    579 already_AddRefed<Promise> Cache::PutAll(
    580    JSContext* aCx, const nsTArray<SafeRefPtr<Request>>& aRequestList,
    581    const nsTArray<RefPtr<Response>>& aResponseList, ErrorResult& aRv) {
    582  MOZ_DIAGNOSTIC_ASSERT(aRequestList.Length() == aResponseList.Length());
    583 
    584  if (NS_WARN_IF(!mActor)) {
    585    aRv.Throw(NS_ERROR_UNEXPECTED);
    586    return nullptr;
    587  }
    588 
    589  CacheChild::AutoLock actorLock(*mActor);
    590 
    591  AutoChildOpArgs args(this, CachePutAllArgs(), aRequestList.Length());
    592 
    593  for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
    594    SafeRefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest();
    595    args.Add(aCx, *ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i],
    596             aRv);
    597    if (NS_WARN_IF(aRv.Failed())) {
    598      return nullptr;
    599    }
    600  }
    601 
    602  return ExecuteOp(args, aRv);
    603 }
    604 
    605 OpenMode Cache::GetOpenMode() const {
    606  return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy;
    607 }
    608 
    609 }  // namespace mozilla::dom::cache