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