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