CacheStorage.cpp (19939B)
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/CacheStorage.h" 8 9 #include "js/Object.h" // JS::GetClass 10 #include "js/PropertyAndElement.h" // JS_DefineProperty 11 #include "mozilla/Preferences.h" 12 #include "mozilla/StaticPrefs_dom.h" 13 #include "mozilla/StaticPrefs_extensions.h" 14 #include "mozilla/dom/CacheBinding.h" 15 #include "mozilla/dom/CacheStorageBinding.h" 16 #include "mozilla/dom/Document.h" 17 #include "mozilla/dom/InternalRequest.h" 18 #include "mozilla/dom/Promise.h" 19 #include "mozilla/dom/Response.h" 20 #include "mozilla/dom/WorkerPrivate.h" 21 #include "mozilla/dom/cache/AutoUtils.h" 22 #include "mozilla/dom/cache/Cache.h" 23 #include "mozilla/dom/cache/CacheChild.h" 24 #include "mozilla/dom/cache/CacheCommon.h" 25 #include "mozilla/dom/cache/CacheStorageChild.h" 26 #include "mozilla/dom/cache/CacheWorkerRef.h" 27 #include "mozilla/dom/cache/PCacheChild.h" 28 #include "mozilla/dom/cache/ReadStream.h" 29 #include "mozilla/dom/cache/TypeUtils.h" 30 #include "mozilla/dom/quota/PrincipalUtils.h" 31 #include "mozilla/dom/quota/ResultExtensions.h" 32 #include "mozilla/ipc/BackgroundChild.h" 33 #include "mozilla/ipc/BackgroundUtils.h" 34 #include "mozilla/ipc/PBackgroundChild.h" 35 #include "mozilla/ipc/PBackgroundSharedTypes.h" 36 #include "nsContentUtils.h" 37 #include "nsIGlobalObject.h" 38 #include "nsMixedContentBlocker.h" 39 #include "nsURLParsers.h" 40 41 namespace mozilla::dom::cache { 42 43 using mozilla::ErrorResult; 44 using mozilla::ipc::BackgroundChild; 45 using mozilla::ipc::PBackgroundChild; 46 using mozilla::ipc::PrincipalInfo; 47 using mozilla::ipc::PrincipalToPrincipalInfo; 48 49 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::CacheStorage); 50 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::CacheStorage); 51 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::CacheStorage, 52 mGlobal); 53 54 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage) 55 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 56 NS_INTERFACE_MAP_ENTRY(nsISupports) 57 NS_INTERFACE_MAP_END 58 59 // We cannot reference IPC types in a webidl binding implementation header. So 60 // define this in the .cpp. 61 struct CacheStorage::Entry final { 62 RefPtr<Promise> mPromise; 63 CacheOpArgs mArgs; 64 // We cannot add the requests until after the actor is present. So store 65 // the request data separately for now. 66 SafeRefPtr<InternalRequest> mRequest; 67 }; 68 69 bool IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled) { 70 // Can happen on main thread or worker thread 71 72 if (aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { 73 return true; 74 } 75 76 // Require a ContentPrincipal to avoid null principal, etc. 77 QM_TRY(OkIf(aPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo), 78 false); 79 80 // If we're in testing mode, then don't do any more work to determine if 81 // the origin is trusted. We have to run some tests as http. 82 if (aTestingPrefEnabled) { 83 return true; 84 } 85 86 // Now parse the scheme of the principal's origin. This is a short term 87 // method for determining "trust". In the long term we need to implement 88 // the full algorithm here: 89 // 90 // https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure 91 // 92 // TODO: Implement full secure setting algorithm. (bug 1177856) 93 94 const nsCString& flatURL = aPrincipalInfo.get_ContentPrincipalInfo().spec(); 95 const char* const url = flatURL.get(); 96 97 // off the main thread URL parsing using nsStdURLParser. 98 const nsCOMPtr<nsIURLParser> urlParser = new nsStdURLParser(); 99 100 uint32_t schemePos; 101 int32_t schemeLen; 102 uint32_t authPos; 103 int32_t authLen; 104 QM_TRY(MOZ_TO_RESULT(urlParser->ParseURL(url, flatURL.Length(), &schemePos, 105 &schemeLen, &authPos, &authLen, 106 nullptr, nullptr)), // ignore path 107 false); 108 109 const nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen)); 110 if (scheme.LowerCaseEqualsLiteral("https") || 111 scheme.LowerCaseEqualsLiteral("file") || 112 scheme.LowerCaseEqualsLiteral("moz-extension")) { 113 return true; 114 } 115 116 uint32_t hostPos; 117 int32_t hostLen; 118 QM_TRY(MOZ_TO_RESULT( 119 urlParser->ParseAuthority(url + authPos, authLen, nullptr, 120 nullptr, // ignore username 121 nullptr, nullptr, // ignore password 122 &hostPos, &hostLen, 123 nullptr)), // ignore port 124 false); 125 126 return nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost( 127 nsDependentCSubstring(url + authPos + hostPos, hostLen)); 128 } 129 130 // static 131 already_AddRefed<CacheStorage> CacheStorage::CreateOnMainThread( 132 Namespace aNamespace, nsIGlobalObject* aGlobal, nsIPrincipal* aPrincipal, 133 bool aForceTrustedOrigin, ErrorResult& aRv) { 134 MOZ_DIAGNOSTIC_ASSERT(aGlobal); 135 MOZ_DIAGNOSTIC_ASSERT(aPrincipal); 136 MOZ_ASSERT(NS_IsMainThread()); 137 138 PrincipalInfo principalInfo; 139 QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)), 140 nullptr, [&aRv](const nsresult rv) { aRv.Throw(rv); }); 141 142 QM_TRY(OkIf(quota::IsPrincipalInfoValid(principalInfo)), 143 RefPtr{new CacheStorage(NS_ERROR_DOM_SECURITY_ERR)}.forget(), 144 [](const auto) { 145 NS_WARNING("CacheStorage not supported on invalid origins."); 146 }); 147 148 const bool testingEnabled = 149 aForceTrustedOrigin || 150 Preferences::GetBool("dom.caches.testing.enabled", false) || 151 StaticPrefs::dom_serviceWorkers_testing_enabled(); 152 153 if (!IsTrusted(principalInfo, testingEnabled)) { 154 NS_WARNING("CacheStorage not supported on untrusted origins."); 155 RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); 156 return ref.forget(); 157 } 158 159 RefPtr<CacheStorage> ref = 160 new CacheStorage(aNamespace, aGlobal, principalInfo, nullptr); 161 return ref.forget(); 162 } 163 164 // static 165 already_AddRefed<CacheStorage> CacheStorage::CreateOnWorker( 166 Namespace aNamespace, nsIGlobalObject* aGlobal, 167 WorkerPrivate* aWorkerPrivate, ErrorResult& aRv) { 168 MOZ_DIAGNOSTIC_ASSERT(aGlobal); 169 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); 170 aWorkerPrivate->AssertIsOnWorkerThread(); 171 172 if (aWorkerPrivate->GetOriginAttributes().IsPrivateBrowsing() && 173 !StaticPrefs::dom_cache_privateBrowsing_enabled()) { 174 NS_WARNING("CacheStorage not supported during private browsing."); 175 RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); 176 return ref.forget(); 177 } 178 179 SafeRefPtr<CacheWorkerRef> workerRef = 180 CacheWorkerRef::Create(aWorkerPrivate, CacheWorkerRef::eIPCWorkerRef); 181 if (!workerRef) { 182 NS_WARNING("Worker thread is shutting down."); 183 aRv.Throw(NS_ERROR_FAILURE); 184 return nullptr; 185 } 186 187 const PrincipalInfo& principalInfo = 188 aWorkerPrivate->GetEffectiveStoragePrincipalInfo(); 189 190 QM_TRY(OkIf(quota::IsPrincipalInfoValid(principalInfo)), nullptr, 191 [&aRv](const auto) { aRv.Throw(NS_ERROR_FAILURE); }); 192 193 // We have a number of cases where we want to skip the https scheme 194 // validation: 195 // 196 // 1) Any worker when dom.caches.testing.enabled pref is true. 197 // 2) Any worker when dom.serviceWorkers.testing.enabled pref is true. This 198 // is mainly because most sites using SWs will expect Cache to work if 199 // SWs are enabled. 200 // 3) If the window that created this worker has the devtools SW testing 201 // option enabled. Same reasoning as (2). 202 // 4) If the worker itself is a ServiceWorker, then we always skip the 203 // origin checks. The ServiceWorker has its own trusted origin checks 204 // that are better than ours. In addition, we don't have information 205 // about the window any more, so we can't do our own checks. 206 bool testingEnabled = StaticPrefs::dom_caches_testing_enabled() || 207 StaticPrefs::dom_serviceWorkers_testing_enabled() || 208 aWorkerPrivate->ServiceWorkersTestingInWindow() || 209 aWorkerPrivate->IsServiceWorker(); 210 211 if (!IsTrusted(principalInfo, testingEnabled)) { 212 NS_WARNING("CacheStorage not supported on untrusted origins."); 213 RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); 214 return ref.forget(); 215 } 216 217 RefPtr<CacheStorage> ref = new CacheStorage( 218 aNamespace, aGlobal, principalInfo, std::move(workerRef)); 219 return ref.forget(); 220 } 221 222 // static 223 bool CacheStorage::DefineCachesForSandbox(JSContext* aCx, 224 JS::Handle<JSObject*> aGlobal) { 225 MOZ_ASSERT(NS_IsMainThread()); 226 MOZ_DIAGNOSTIC_ASSERT(JS::GetClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, 227 "Passed object is not a global object!"); 228 js::AssertSameCompartment(aCx, aGlobal); 229 230 if (NS_WARN_IF(!CacheStorage_Binding::CreateAndDefineOnGlobal(aCx) || 231 !Cache_Binding::CreateAndDefineOnGlobal(aCx))) { 232 return false; 233 } 234 235 nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal); 236 MOZ_DIAGNOSTIC_ASSERT(principal); 237 238 ErrorResult rv; 239 RefPtr<CacheStorage> storage = 240 CreateOnMainThread(DEFAULT_NAMESPACE, xpc::NativeGlobal(aGlobal), 241 principal, true, /* force trusted */ 242 rv); 243 if (NS_WARN_IF(rv.MaybeSetPendingException(aCx))) { 244 return false; 245 } 246 247 JS::Rooted<JS::Value> caches(aCx); 248 if (NS_WARN_IF(!ToJSValue(aCx, storage, &caches))) { 249 return false; 250 } 251 252 return JS_DefineProperty(aCx, aGlobal, "caches", caches, JSPROP_ENUMERATE); 253 } 254 255 // static 256 bool CacheStorage::CachesEnabled(JSContext* aCx, JSObject* aObj) { 257 return cache::Cache::CachesEnabled(aCx, aObj); 258 } 259 260 CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal, 261 const PrincipalInfo& aPrincipalInfo, 262 SafeRefPtr<CacheWorkerRef> aWorkerRef) 263 : mNamespace(aNamespace), 264 mGlobal(aGlobal), 265 mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo)), 266 mActor(nullptr), 267 mStatus(NS_OK) { 268 MOZ_DIAGNOSTIC_ASSERT(mGlobal); 269 270 // If the PBackground actor is already initialized then we can 271 // immediately use it 272 PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread(); 273 if (NS_WARN_IF(!actor)) { 274 mStatus = NS_ERROR_UNEXPECTED; 275 return; 276 } 277 278 // WorkerRef ownership is passed to the CacheStorageChild actor and any 279 // actors it may create. The WorkerRef will keep the worker thread alive 280 // until the actors can gracefully shutdown. 281 CacheStorageChild* newActor = 282 new CacheStorageChild(this, std::move(aWorkerRef)); 283 PCacheStorageChild* constructedActor = actor->SendPCacheStorageConstructor( 284 newActor, mNamespace, *mPrincipalInfo); 285 286 if (NS_WARN_IF(!constructedActor)) { 287 mStatus = NS_ERROR_UNEXPECTED; 288 return; 289 } 290 291 MOZ_DIAGNOSTIC_ASSERT(constructedActor == newActor); 292 mActor = newActor; 293 } 294 295 CacheStorage::CacheStorage(nsresult aFailureResult) 296 : mNamespace(INVALID_NAMESPACE), mActor(nullptr), mStatus(aFailureResult) { 297 MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mStatus)); 298 } 299 300 already_AddRefed<Promise> CacheStorage::Match( 301 JSContext* aCx, const RequestOrUTF8String& aRequest, 302 const MultiCacheQueryOptions& aOptions, ErrorResult& aRv) { 303 NS_ASSERT_OWNINGTHREAD(CacheStorage); 304 305 if (!HasStorageAccess(eUseCounter_custom_PrivateBrowsingCachesMatch, 306 UseCounterWorker::Custom_PrivateBrowsingCachesMatch)) { 307 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 308 return nullptr; 309 } 310 311 if (NS_WARN_IF(NS_FAILED(mStatus))) { 312 aRv.Throw(mStatus); 313 return nullptr; 314 } 315 316 SafeRefPtr<InternalRequest> request = 317 ToInternalRequest(aCx, aRequest, IgnoreBody, aRv); 318 if (NS_WARN_IF(aRv.Failed())) { 319 return nullptr; 320 } 321 322 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); 323 if (NS_WARN_IF(!promise)) { 324 return nullptr; 325 } 326 327 CacheQueryParams params; 328 ToCacheQueryParams(params, aOptions); 329 330 auto entry = MakeUnique<Entry>(); 331 entry->mPromise = promise; 332 entry->mArgs = StorageMatchArgs(CacheRequest(), params, GetOpenMode()); 333 entry->mRequest = std::move(request); 334 335 RunRequest(std::move(entry)); 336 337 return promise.forget(); 338 } 339 340 already_AddRefed<Promise> CacheStorage::Has(const nsAString& aKey, 341 ErrorResult& aRv) { 342 NS_ASSERT_OWNINGTHREAD(CacheStorage); 343 344 if (!HasStorageAccess(eUseCounter_custom_PrivateBrowsingCachesHas, 345 UseCounterWorker::Custom_PrivateBrowsingCachesHas)) { 346 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 347 return nullptr; 348 } 349 350 if (NS_WARN_IF(NS_FAILED(mStatus))) { 351 aRv.Throw(mStatus); 352 return nullptr; 353 } 354 355 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); 356 if (NS_WARN_IF(!promise)) { 357 return nullptr; 358 } 359 360 auto entry = MakeUnique<Entry>(); 361 entry->mPromise = promise; 362 entry->mArgs = StorageHasArgs(nsString(aKey)); 363 364 RunRequest(std::move(entry)); 365 366 return promise.forget(); 367 } 368 369 already_AddRefed<Promise> CacheStorage::Open(const nsAString& aKey, 370 ErrorResult& aRv) { 371 NS_ASSERT_OWNINGTHREAD(CacheStorage); 372 373 if (!HasStorageAccess(eUseCounter_custom_PrivateBrowsingCachesOpen, 374 UseCounterWorker::Custom_PrivateBrowsingCachesOpen)) { 375 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 376 return nullptr; 377 } 378 379 if (NS_WARN_IF(NS_FAILED(mStatus))) { 380 aRv.Throw(mStatus); 381 return nullptr; 382 } 383 384 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); 385 if (NS_WARN_IF(!promise)) { 386 return nullptr; 387 } 388 389 auto entry = MakeUnique<Entry>(); 390 entry->mPromise = promise; 391 entry->mArgs = StorageOpenArgs(nsString(aKey)); 392 393 RunRequest(std::move(entry)); 394 395 return promise.forget(); 396 } 397 398 already_AddRefed<Promise> CacheStorage::Delete(const nsAString& aKey, 399 ErrorResult& aRv) { 400 NS_ASSERT_OWNINGTHREAD(CacheStorage); 401 402 if (!HasStorageAccess(eUseCounter_custom_PrivateBrowsingCachesDelete, 403 UseCounterWorker::Custom_PrivateBrowsingCachesDelete)) { 404 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 405 return nullptr; 406 } 407 408 if (NS_WARN_IF(NS_FAILED(mStatus))) { 409 aRv.Throw(mStatus); 410 return nullptr; 411 } 412 413 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); 414 if (NS_WARN_IF(!promise)) { 415 return nullptr; 416 } 417 418 auto entry = MakeUnique<Entry>(); 419 entry->mPromise = promise; 420 entry->mArgs = StorageDeleteArgs(nsString(aKey)); 421 422 RunRequest(std::move(entry)); 423 424 return promise.forget(); 425 } 426 427 already_AddRefed<Promise> CacheStorage::Keys(ErrorResult& aRv) { 428 NS_ASSERT_OWNINGTHREAD(CacheStorage); 429 430 if (!HasStorageAccess(eUseCounter_custom_PrivateBrowsingCachesKeys, 431 UseCounterWorker::Custom_PrivateBrowsingCachesKeys)) { 432 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 433 return nullptr; 434 } 435 436 if (NS_WARN_IF(NS_FAILED(mStatus))) { 437 aRv.Throw(mStatus); 438 return nullptr; 439 } 440 441 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv); 442 if (NS_WARN_IF(!promise)) { 443 return nullptr; 444 } 445 446 auto entry = MakeUnique<Entry>(); 447 entry->mPromise = promise; 448 entry->mArgs = StorageKeysArgs(); 449 450 RunRequest(std::move(entry)); 451 452 return promise.forget(); 453 } 454 455 // static 456 already_AddRefed<CacheStorage> CacheStorage::Constructor( 457 const GlobalObject& aGlobal, CacheStorageNamespace aNamespace, 458 nsIPrincipal* aPrincipal, ErrorResult& aRv) { 459 if (NS_WARN_IF(!NS_IsMainThread())) { 460 aRv.Throw(NS_ERROR_FAILURE); 461 return nullptr; 462 } 463 464 // TODO: remove Namespace in favor of CacheStorageNamespace 465 static_assert(DEFAULT_NAMESPACE == (uint32_t)CacheStorageNamespace::Content, 466 "Default namespace should match webidl Content enum"); 467 static_assert( 468 CHROME_ONLY_NAMESPACE == (uint32_t)CacheStorageNamespace::Chrome, 469 "Chrome namespace should match webidl Chrome enum"); 470 static_assert( 471 NUMBER_OF_NAMESPACES == ContiguousEnumSize<CacheStorageNamespace>::value, 472 "Number of namespace should match webidl count"); 473 474 Namespace ns = static_cast<Namespace>(aNamespace); 475 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 476 477 bool privateBrowsing = false; 478 if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global)) { 479 RefPtr<Document> doc = window->GetExtantDoc(); 480 if (doc) { 481 nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext(); 482 privateBrowsing = loadContext && loadContext->UsePrivateBrowsing(); 483 } 484 } 485 486 if (privateBrowsing && !StaticPrefs::dom_cache_privateBrowsing_enabled()) { 487 RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR); 488 return ref.forget(); 489 } 490 491 // Create a CacheStorage object bypassing the trusted origin checks 492 // since this is a chrome-only constructor. 493 return CreateOnMainThread(ns, global, aPrincipal, 494 true /* force trusted origin */, aRv); 495 } 496 497 nsISupports* CacheStorage::GetParentObject() const { return mGlobal; } 498 499 JSObject* CacheStorage::WrapObject(JSContext* aContext, 500 JS::Handle<JSObject*> aGivenProto) { 501 return mozilla::dom::CacheStorage_Binding::Wrap(aContext, this, aGivenProto); 502 } 503 504 void CacheStorage::OnActorDestroy(CacheStorageChild* aActor) { 505 NS_ASSERT_OWNINGTHREAD(CacheStorage); 506 MOZ_DIAGNOSTIC_ASSERT(mActor); 507 MOZ_DIAGNOSTIC_ASSERT(mActor == aActor); 508 MOZ_DIAGNOSTIC_ASSERT(!NS_FAILED(mStatus)); 509 mActor->ClearListener(); 510 mActor = nullptr; 511 mStatus = NS_ERROR_UNEXPECTED; 512 513 // Note that we will never get an actor again in case another request is 514 // made before this object is destructed. 515 } 516 517 nsIGlobalObject* CacheStorage::GetGlobalObject() const { return mGlobal; } 518 519 #ifdef DEBUG 520 void CacheStorage::AssertOwningThread() const { 521 NS_ASSERT_OWNINGTHREAD(CacheStorage); 522 } 523 #endif 524 525 CacheStorage::~CacheStorage() { 526 NS_ASSERT_OWNINGTHREAD(CacheStorage); 527 if (mActor) { 528 mActor->StartDestroyFromListener(); 529 // OnActorDestroy() is called synchronously by StartDestroyFromListener(). 530 // So we should have already cleared the mActor. 531 MOZ_DIAGNOSTIC_ASSERT(!mActor); 532 } 533 } 534 535 void CacheStorage::RunRequest(UniquePtr<Entry> aEntry) { 536 MOZ_ASSERT(mActor); 537 538 AutoChildOpArgs args(this, aEntry->mArgs, 1); 539 540 if (aEntry->mRequest) { 541 ErrorResult rv; 542 args.Add(*aEntry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv); 543 if (NS_WARN_IF(rv.Failed())) { 544 aEntry->mPromise->MaybeReject(std::move(rv)); 545 return; 546 } 547 } 548 549 mActor->ExecuteOp(mGlobal, aEntry->mPromise, this, args.SendAsOpArgs()); 550 } 551 552 OpenMode CacheStorage::GetOpenMode() const { 553 return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy; 554 } 555 556 bool CacheStorage::HasStorageAccess(UseCounter aLabel, 557 UseCounterWorker aLabelWorker) const { 558 NS_ASSERT_OWNINGTHREAD(CacheStorage); 559 if (NS_WARN_IF(!mGlobal)) { 560 return false; 561 } 562 563 StorageAccess access = mGlobal->GetStorageAccess(); 564 if (access == StorageAccess::ePrivateBrowsing) { 565 if (NS_IsMainThread()) { 566 SetUseCounter(mGlobal->GetGlobalJSObject(), aLabel); 567 } else { 568 SetUseCounter(aLabelWorker); 569 } 570 } 571 572 // Deny storage access for private browsing unless pref is toggled on. 573 if (nsIPrincipal* principal = mGlobal->PrincipalOrNull()) { 574 if (!principal->IsSystemPrincipal() && 575 principal->GetPrivateBrowsingId() != 576 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID && 577 !StaticPrefs::dom_cache_privateBrowsing_enabled()) { 578 return false; 579 } 580 } 581 582 return access > StorageAccess::eDeny || 583 (StaticPrefs:: 584 privacy_partition_always_partition_third_party_non_cookie_storage() && 585 ShouldPartitionStorage(access)); 586 } 587 588 } // namespace mozilla::dom::cache