SessionStorageManager.cpp (30504B)
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 "SessionStorageManager.h" 8 9 #include "SessionStorage.h" 10 #include "SessionStorageCache.h" 11 #include "SessionStorageObserver.h" 12 #include "StorageIPC.h" 13 #include "StorageUtils.h" 14 #include "mozilla/ClearOnShutdown.h" 15 #include "mozilla/OriginAttributes.h" 16 #include "mozilla/PrincipalHashKey.h" 17 #include "mozilla/ScopeExit.h" 18 #include "mozilla/StoragePrincipalHelper.h" 19 #include "mozilla/dom/CanonicalBrowsingContext.h" 20 #include "mozilla/dom/ContentChild.h" 21 #include "mozilla/dom/LocalStorageCommon.h" 22 #include "mozilla/dom/PBackgroundSessionStorageCache.h" 23 #include "mozilla/dom/PBackgroundSessionStorageManager.h" 24 #include "mozilla/dom/PermissionMessageUtils.h" 25 #include "mozilla/dom/WindowGlobalParent.h" 26 #include "mozilla/ipc/BackgroundChild.h" 27 #include "mozilla/ipc/BackgroundParent.h" 28 #include "mozilla/ipc/PBackgroundChild.h" 29 #include "nsIXULRuntime.h" 30 #include "nsTHashMap.h" 31 #include "nsThreadUtils.h" 32 33 namespace mozilla::dom { 34 35 using namespace StorageUtils; 36 37 static bool ExactDomainMatch(const nsACString& aOriginKey, 38 const nsACString& aOriginScope) { 39 // aOriginScope is reversed domain with trailing dot. 40 // e.g: example.com => moc.elpmaxe. (see StorageUtils.cpp) 41 // aOriginKey is reversed domain with trailing dot, plus ":", 42 // scheme, and optional port. e.g: moc.elpmaxe.:http:80 (see 43 // https://searchfox.org/firefox-main/rev/987f566373ea82403c5c1235b219bd9e7d56a4aa/caps/BasePrincipal.cpp#1539) 44 // To check if it is an exact match, we need to ensure that aOriginKey starts 45 // with aOriginScope and the first character immediately following the 46 // matching part is ":". i.e: Domain part of aOriginKey is identical to 47 // aOriginScope. 48 return StringBeginsWith(aOriginKey, aOriginScope) && 49 aOriginKey.CharAt(aOriginScope.Length()) == ':'; 50 } 51 52 // Parent process, background thread hashmap that stores top context id and 53 // manager pair. 54 static StaticAutoPtr< 55 nsRefPtrHashtable<nsUint64HashKey, BackgroundSessionStorageManager>> 56 sManagers; 57 58 bool RecvShutdownBackgroundSessionStorageManagers() { 59 ::mozilla::ipc::AssertIsOnBackgroundThread(); 60 61 sManagers = nullptr; 62 return true; 63 } 64 65 void RecvPropagateBackgroundSessionStorageManager( 66 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) { 67 ::mozilla::ipc::AssertIsOnBackgroundThread(); 68 69 if (sManagers) { 70 if (RefPtr<BackgroundSessionStorageManager> mgr = 71 sManagers->Get(aCurrentTopContextId)) { 72 mgr->MaybeDispatchSessionStoreUpdate(); 73 mgr->SetCurrentBrowsingContextId(aTargetTopContextId); 74 // Because of bfcache, we may re-register aTargetTopContextId in 75 // CanonicalBrowsingContext::ReplacedBy. 76 // XXXBFCache do we want to tweak this behavior and ensure this is 77 // called only once? 78 sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr)); 79 } 80 } 81 } 82 83 bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) { 84 ::mozilla::ipc::AssertIsOnBackgroundThread(); 85 86 if (sManagers) { 87 RefPtr<BackgroundSessionStorageManager> mgr; 88 sManagers->Remove(aTopContextId, getter_AddRefs(mgr)); 89 90 if (mgr) { 91 mgr->CancelSessionStoreUpdate(); 92 } 93 } 94 95 return true; 96 } 97 98 bool RecvLoadSessionStorageData( 99 uint64_t aTopContextId, 100 nsTArray<mozilla::dom::SSCacheCopy>&& aCacheCopyList) { 101 if (aCacheCopyList.IsEmpty()) { 102 return true; 103 } 104 105 RefPtr<BackgroundSessionStorageManager> manager = 106 BackgroundSessionStorageManager::GetOrCreate(aTopContextId); 107 108 if (!manager) { 109 return true; 110 } 111 112 for (const auto& cacheInit : aCacheCopyList) { 113 OriginAttributes attrs; 114 StoragePrincipalHelper::GetOriginAttributes(cacheInit.principalInfo(), 115 attrs); 116 117 nsAutoCString originAttrs; 118 attrs.CreateSuffix(originAttrs); 119 120 manager->UpdateData(originAttrs, cacheInit.originKey(), cacheInit.data()); 121 } 122 123 return true; 124 } 125 126 bool RecvGetSessionStorageData( 127 uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer, 128 ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&& 129 aResolver) { 130 nsTArray<mozilla::dom::SSCacheCopy> data; 131 auto resolve = MakeScopeExit([&]() { aResolver(std::move(data)); }); 132 133 if (!sManagers) { 134 return true; 135 } 136 137 RefPtr<BackgroundSessionStorageManager> manager = 138 sManagers->Get(aTopContextId); 139 if (!manager) { 140 return true; 141 } 142 143 if (aCancelSessionStoreTimer) { 144 manager->CancelSessionStoreUpdate(); 145 } 146 147 manager->GetData(aSizeLimit, data); 148 149 return true; 150 } 151 152 bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs, 153 const nsACString& aOriginKey) { 154 mozilla::ipc::AssertIsInMainProcess(); 155 mozilla::ipc::AssertIsOnBackgroundThread(); 156 157 if (!sManagers) { 158 return true; 159 } 160 161 for (auto& entry : *sManagers) { 162 entry.GetData()->ClearStoragesForOrigin(aOriginAttrs, aOriginKey); 163 } 164 165 return true; 166 } 167 168 void SessionStorageManagerBase::ClearStoragesInternal( 169 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, 170 DomainMatchingMode aMode) { 171 const bool isExactMatch = aMode == DomainMatchingMode::EXACT_MATCH; 172 173 for (const auto& oaEntry : mOATable) { 174 OriginAttributes oa; 175 DebugOnly<bool> ok = oa.PopulateFromSuffix(oaEntry.GetKey()); 176 MOZ_ASSERT(ok); 177 if (!aPattern.Matches(oa)) { 178 // This table doesn't match the given origin attributes pattern 179 continue; 180 } 181 182 OriginKeyHashTable* table = oaEntry.GetWeak(); 183 for (const auto& originKeyEntry : *table) { 184 if (aOriginScope.IsEmpty() || 185 (!isExactMatch && 186 StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) || 187 (isExactMatch && 188 ExactDomainMatch(originKeyEntry.GetKey(), aOriginScope))) { 189 const auto cache = originKeyEntry.GetData()->mCache; 190 cache->Clear(false); 191 cache->ResetWriteInfos(); 192 } 193 } 194 } 195 } 196 197 void SessionStorageManagerBase::ClearStoragesForOriginInternal( 198 const nsACString& aOriginAttrs, const nsACString& aOriginKey) { 199 for (const auto& oaEntry : mOATable) { 200 // Filter tables which match the given origin attrs. 201 if (oaEntry.GetKey() != aOriginAttrs) { 202 continue; 203 } 204 205 OriginKeyHashTable* table = oaEntry.GetWeak(); 206 for (const auto& originKeyEntry : *table) { 207 // Match exact origin (without origin attrs). 208 if (originKeyEntry.GetKey() != aOriginKey) { 209 continue; 210 } 211 212 const auto cache = originKeyEntry.GetData()->mCache; 213 cache->Clear(false); 214 cache->ResetWriteInfos(); 215 } 216 } 217 } 218 219 SessionStorageManagerBase::OriginRecord* 220 SessionStorageManagerBase::GetOriginRecord( 221 const nsACString& aOriginAttrs, const nsACString& aOriginKey, 222 const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) { 223 // XXX It seems aMakeIfNeeded is always known at compile-time, so this could 224 // be split into two functions. 225 226 if (aMakeIfNeeded) { 227 return mOATable.GetOrInsertNew(aOriginAttrs) 228 ->LookupOrInsertWith( 229 aOriginKey, 230 [&] { 231 auto newOriginRecord = MakeUnique<OriginRecord>(); 232 if (aCloneFrom) { 233 newOriginRecord->mCache = aCloneFrom->Clone(); 234 } else { 235 newOriginRecord->mCache = new SessionStorageCache(); 236 } 237 return newOriginRecord; 238 }) 239 .get(); 240 } 241 242 auto* const table = mOATable.Get(aOriginAttrs); 243 if (!table) return nullptr; 244 245 return table->Get(aOriginKey); 246 } 247 248 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager) 249 NS_INTERFACE_MAP_ENTRY(nsISupports) 250 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager) 251 NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager) 252 NS_INTERFACE_MAP_END 253 254 NS_IMPL_CYCLE_COLLECTION(SessionStorageManager, mBrowsingContext) 255 NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStorageManager) 256 NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStorageManager) 257 258 SessionStorageManager::SessionStorageManager( 259 RefPtr<BrowsingContext> aBrowsingContext) 260 : mBrowsingContext(std::move(aBrowsingContext)), mActor(nullptr) { 261 AssertIsOnMainThread(); 262 263 StorageObserver* observer = StorageObserver::Self(); 264 NS_ASSERTION( 265 observer, 266 "No StorageObserver, cannot observe private data delete notifications!"); 267 268 if (observer) { 269 observer->AddSink(this); 270 } 271 272 if (!XRE_IsParentProcess() && NextGenLocalStorageEnabled()) { 273 // When LSNG is enabled the thread IPC bridge doesn't exist, so we have to 274 // create own protocol to distribute chrome observer notifications to 275 // content processes. 276 mObserver = SessionStorageObserver::Get(); 277 278 if (!mObserver) { 279 ContentChild* contentActor = ContentChild::GetSingleton(); 280 MOZ_ASSERT(contentActor); 281 282 RefPtr<SessionStorageObserver> observer = new SessionStorageObserver(); 283 284 SessionStorageObserverChild* actor = 285 new SessionStorageObserverChild(observer); 286 287 MOZ_ALWAYS_TRUE( 288 contentActor->SendPSessionStorageObserverConstructor(actor)); 289 290 observer->SetActor(actor); 291 292 mObserver = std::move(observer); 293 } 294 } 295 } 296 297 SessionStorageManager::~SessionStorageManager() { 298 StorageObserver* observer = StorageObserver::Self(); 299 if (observer) { 300 observer->RemoveSink(this); 301 } 302 303 if (mActor) { 304 mActor->SendDeleteMeInternal(); 305 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!"); 306 } 307 } 308 309 bool SessionStorageManager::CanLoadData() { 310 AssertIsOnMainThread(); 311 312 return mBrowsingContext && !mBrowsingContext->IsDiscarded(); 313 } 314 315 void SessionStorageManager::SetActor(SessionStorageManagerChild* aActor) { 316 AssertIsOnMainThread(); 317 MOZ_ASSERT(aActor); 318 MOZ_ASSERT(!mActor); 319 320 mActor = aActor; 321 } 322 323 bool SessionStorageManager::ActorExists() const { 324 AssertIsOnMainThread(); 325 326 return mActor; 327 } 328 329 void SessionStorageManager::ClearActor() { 330 AssertIsOnMainThread(); 331 MOZ_ASSERT(mActor); 332 333 mActor = nullptr; 334 } 335 336 nsresult SessionStorageManager::EnsureManager() { 337 AssertIsOnMainThread(); 338 MOZ_ASSERT(CanLoadData()); 339 340 if (ActorExists()) { 341 return NS_OK; 342 } 343 344 ::mozilla::ipc::PBackgroundChild* backgroundActor = 345 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 346 if (NS_WARN_IF(!backgroundActor)) { 347 return NS_ERROR_FAILURE; 348 } 349 350 RefPtr<SessionStorageManagerChild> actor = 351 new SessionStorageManagerChild(this); 352 353 if (!backgroundActor->SendPBackgroundSessionStorageManagerConstructor( 354 actor, mBrowsingContext->Top()->Id())) { 355 return NS_ERROR_FAILURE; 356 } 357 358 SetActor(actor); 359 360 return NS_OK; 361 } 362 363 SessionStorageCacheChild* SessionStorageManager::EnsureCache( 364 nsIPrincipal& aPrincipal, const nsACString& aOriginKey, 365 SessionStorageCache& aCache) { 366 AssertIsOnMainThread(); 367 MOZ_ASSERT(CanLoadData()); 368 MOZ_ASSERT(ActorExists()); 369 370 if (aCache.Actor()) { 371 return aCache.Actor(); 372 } 373 374 mozilla::ipc::PrincipalInfo info; 375 nsresult rv = PrincipalToPrincipalInfo(&aPrincipal, &info); 376 377 if (NS_FAILED(rv)) { 378 return nullptr; 379 } 380 381 RefPtr<SessionStorageCacheChild> actor = 382 new SessionStorageCacheChild(&aCache); 383 if (!mActor->SendPBackgroundSessionStorageCacheConstructor(actor, info, 384 aOriginKey)) { 385 return nullptr; 386 } 387 388 aCache.SetActor(actor); 389 390 return actor; 391 } 392 393 nsresult SessionStorageManager::LoadData(nsIPrincipal& aPrincipal, 394 SessionStorageCache& aCache) { 395 AssertIsOnMainThread(); 396 MOZ_ASSERT(mActor); 397 398 nsAutoCString originKey; 399 nsresult rv = aPrincipal.GetStorageOriginKey(originKey); 400 if (NS_WARN_IF(NS_FAILED(rv))) { 401 return rv; 402 } 403 404 nsAutoCString originAttributes; 405 aPrincipal.OriginAttributesRef().CreateSuffix(originAttributes); 406 407 auto* const originRecord = 408 GetOriginRecord(originAttributes, originKey, true, nullptr); 409 MOZ_ASSERT(originRecord); 410 411 if (originRecord->mLoaded) { 412 return NS_OK; 413 } 414 415 RefPtr<SessionStorageCacheChild> cacheActor = 416 EnsureCache(aPrincipal, originKey, aCache); 417 418 if (!cacheActor) { 419 return NS_ERROR_FAILURE; 420 } 421 422 nsTArray<SSSetItemInfo> data; 423 if (!cacheActor->SendLoad(&data)) { 424 return NS_ERROR_FAILURE; 425 } 426 427 originRecord->mCache->DeserializeData(data); 428 429 originRecord->mLoaded.Flip(); 430 aCache.SetLoadedOrCloned(); 431 432 return NS_OK; 433 } 434 435 void SessionStorageManager::CheckpointData(nsIPrincipal& aPrincipal, 436 SessionStorageCache& aCache) { 437 AssertIsOnMainThread(); 438 MOZ_ASSERT(mActor); 439 440 nsAutoCString originKey; 441 nsresult rv = aPrincipal.GetStorageOriginKey(originKey); 442 if (NS_WARN_IF(NS_FAILED(rv))) { 443 return; 444 } 445 446 return CheckpointDataInternal(aPrincipal, originKey, aCache); 447 } 448 449 void SessionStorageManager::CheckpointDataInternal( 450 nsIPrincipal& aPrincipal, const nsACString& aOriginKey, 451 SessionStorageCache& aCache) { 452 AssertIsOnMainThread(); 453 MOZ_ASSERT(mActor); 454 455 nsTArray<SSWriteInfo> writeInfos = aCache.SerializeWriteInfos(); 456 457 if (writeInfos.IsEmpty()) { 458 return; 459 } 460 461 RefPtr<SessionStorageCacheChild> cacheActor = 462 EnsureCache(aPrincipal, aOriginKey, aCache); 463 464 if (!cacheActor) { 465 return; 466 } 467 468 (void)cacheActor->SendCheckpoint(writeInfos); 469 470 aCache.ResetWriteInfos(); 471 } 472 473 nsresult SessionStorageManager::ClearStoragesForOrigin( 474 const nsACString& aOriginAttrs, const nsACString& aOriginKey) { 475 AssertIsOnMainThread(); 476 477 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey); 478 479 return NS_OK; 480 } 481 482 NS_IMETHODIMP 483 SessionStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal, 484 nsIPrincipal* aStoragePrincipal, 485 Storage** aRetval) { 486 // Nothing to preload. 487 return NS_OK; 488 } 489 490 NS_IMETHODIMP 491 SessionStorageManager::GetSessionStorageCache( 492 nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal, 493 RefPtr<SessionStorageCache>* aRetVal) { 494 return GetSessionStorageCacheHelper(aStoragePrincipal, true, nullptr, 495 aRetVal); 496 } 497 498 nsresult SessionStorageManager::GetSessionStorageCacheHelper( 499 nsIPrincipal* aPrincipal, bool aMakeIfNeeded, 500 SessionStorageCache* aCloneFrom, RefPtr<SessionStorageCache>* aRetVal) { 501 nsAutoCString originKey; 502 nsAutoCString originAttributes; 503 nsresult rv = aPrincipal->GetStorageOriginKey(originKey); 504 aPrincipal->OriginAttributesRef().CreateSuffix(originAttributes); 505 if (NS_FAILED(rv)) { 506 return NS_ERROR_NOT_AVAILABLE; 507 } 508 509 return GetSessionStorageCacheHelper(originAttributes, originKey, 510 aMakeIfNeeded, aCloneFrom, aRetVal); 511 } 512 513 nsresult SessionStorageManager::GetSessionStorageCacheHelper( 514 const nsACString& aOriginAttrs, const nsACString& aOriginKey, 515 bool aMakeIfNeeded, SessionStorageCache* aCloneFrom, 516 RefPtr<SessionStorageCache>* aRetVal) { 517 if (OriginRecord* const originRecord = GetOriginRecord( 518 aOriginAttrs, aOriginKey, aMakeIfNeeded, aCloneFrom)) { 519 *aRetVal = originRecord->mCache; 520 } else { 521 *aRetVal = nullptr; 522 } 523 return NS_OK; 524 } 525 526 NS_IMETHODIMP 527 SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow, 528 nsIPrincipal* aPrincipal, 529 nsIPrincipal* aStoragePrincipal, 530 const nsAString& aDocumentURI, 531 bool aPrivate, Storage** aRetval) { 532 RefPtr<SessionStorageCache> cache; 533 nsresult rv = GetSessionStorageCache(aPrincipal, aStoragePrincipal, &cache); 534 if (NS_FAILED(rv)) { 535 return rv; 536 } 537 538 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow); 539 540 RefPtr<SessionStorage> storage = 541 new SessionStorage(inner, aPrincipal, aStoragePrincipal, cache, this, 542 aDocumentURI, aPrivate); 543 544 storage.forget(aRetval); 545 return NS_OK; 546 } 547 548 NS_IMETHODIMP 549 SessionStorageManager::GetStorage(mozIDOMWindow* aWindow, 550 nsIPrincipal* aPrincipal, 551 nsIPrincipal* aStoragePrincipal, 552 bool aPrivate, Storage** aRetval) { 553 *aRetval = nullptr; 554 555 RefPtr<SessionStorageCache> cache; 556 nsresult rv = 557 GetSessionStorageCacheHelper(aStoragePrincipal, false, nullptr, &cache); 558 if (NS_FAILED(rv) || !cache) { 559 return rv; 560 } 561 562 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow); 563 564 RefPtr<SessionStorage> storage = new SessionStorage( 565 inner, aPrincipal, aStoragePrincipal, cache, this, u""_ns, aPrivate); 566 567 storage.forget(aRetval); 568 return NS_OK; 569 } 570 571 NS_IMETHODIMP 572 SessionStorageManager::CloneStorage(Storage* aStorage) { 573 if (NS_WARN_IF(!aStorage)) { 574 return NS_ERROR_UNEXPECTED; 575 } 576 577 if (aStorage->Type() != Storage::eSessionStorage) { 578 return NS_ERROR_UNEXPECTED; 579 } 580 581 // ToDo: At the moment, we clone the cache on the child process and then 582 // send the checkpoint. It would be nicer if we either serailizing all the 583 // data and sync to the parent process directly or clonig storage on the 584 // parnet process and sync it to the child process on demand. 585 586 RefPtr<SessionStorageCache> cache; 587 nsresult rv = GetSessionStorageCacheHelper( 588 aStorage->StoragePrincipal(), true, 589 static_cast<SessionStorage*>(aStorage)->Cache(), &cache); 590 if (NS_WARN_IF(NS_FAILED(rv))) { 591 return rv; 592 } 593 594 // If cache was cloned from other storage, then we shouldn't load the cache 595 // at the first access. 596 cache->SetLoadedOrCloned(); 597 598 if (CanLoadData()) { 599 rv = EnsureManager(); 600 if (NS_WARN_IF(NS_FAILED(rv))) { 601 return rv; 602 } 603 604 CheckpointData(*aStorage->StoragePrincipal(), *cache); 605 } 606 607 return rv; 608 } 609 610 NS_IMETHODIMP 611 SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage, 612 bool* aRetval) { 613 if (NS_WARN_IF(!aStorage)) { 614 return NS_ERROR_UNEXPECTED; 615 } 616 617 if (!aPrincipal) { 618 return NS_ERROR_NOT_AVAILABLE; 619 } 620 621 *aRetval = false; 622 623 RefPtr<SessionStorageCache> cache; 624 nsresult rv = 625 GetSessionStorageCacheHelper(aPrincipal, false, nullptr, &cache); 626 if (NS_FAILED(rv) || !cache) { 627 return rv; 628 } 629 630 if (aStorage->Type() != Storage::eSessionStorage) { 631 return NS_OK; 632 } 633 634 RefPtr<SessionStorage> sessionStorage = 635 static_cast<SessionStorage*>(aStorage); 636 if (sessionStorage->Cache() != cache) { 637 return NS_OK; 638 } 639 640 if (!StorageUtils::PrincipalsEqual(aStorage->StoragePrincipal(), 641 aPrincipal)) { 642 return NS_OK; 643 } 644 645 *aRetval = true; 646 return NS_OK; 647 } 648 649 void SessionStorageManager::ClearStorages( 650 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, 651 DomainMatchingMode aMode) { 652 if (CanLoadData()) { 653 nsresult rv = EnsureManager(); 654 if (NS_WARN_IF(NS_FAILED(rv))) { 655 return; 656 } 657 658 mActor->SendClearStorages(aPattern, nsCString(aOriginScope), 659 static_cast<uint32_t>(aMode)); 660 } 661 662 ClearStoragesInternal(aPattern, aOriginScope, aMode); 663 } 664 665 nsresult SessionStorageManager::Observe( 666 const char* aTopic, const nsAString& aOriginAttributesPattern, 667 const nsACString& aOriginScope) { 668 OriginAttributesPattern pattern; 669 if (!pattern.Init(aOriginAttributesPattern)) { 670 NS_ERROR("Cannot parse origin attributes pattern"); 671 return NS_ERROR_FAILURE; 672 } 673 674 // Clear everything, caches + database 675 if (!strcmp(aTopic, "cookie-cleared")) { 676 ClearStorages(pattern, ""_ns); 677 return NS_OK; 678 } 679 680 // Clear from caches everything that has been stored 681 // while in session-only mode 682 if (!strcmp(aTopic, "session-only-cleared")) { 683 ClearStorages(pattern, aOriginScope); 684 return NS_OK; 685 } 686 687 // Clear everything (including so and pb data) from caches and database 688 // for the given domain and subdomains. 689 if (!strcmp(aTopic, "browser:purge-sessionStorage")) { 690 ClearStorages(pattern, aOriginScope); 691 return NS_OK; 692 } 693 694 // Clear everything (including so and pb data) from caches and database 695 // for the given domain. 696 if (!strcmp(aTopic, "extension:purge-sessionStorage")) { 697 ClearStorages(pattern, aOriginScope, DomainMatchingMode::EXACT_MATCH); 698 return NS_OK; 699 } 700 701 // Clear entries which match an OriginAttributesPattern. 702 if (!strcmp(aTopic, "dom-storage:clear-origin-attributes-data") || 703 !strcmp(aTopic, "session-storage:clear-origin-attributes-data")) { 704 ClearStorages(pattern, aOriginScope); 705 return NS_OK; 706 } 707 708 if (!strcmp(aTopic, "profile-change")) { 709 // For case caches are still referenced - clear them completely 710 ClearStorages(pattern, ""_ns); 711 mOATable.Clear(); 712 return NS_OK; 713 } 714 715 return NS_OK; 716 } 717 718 SessionStorageManager::OriginRecord::~OriginRecord() = default; 719 720 // static 721 void BackgroundSessionStorageManager::RemoveManager(uint64_t aTopContextId) { 722 MOZ_ASSERT(XRE_IsParentProcess()); 723 AssertIsOnMainThread(); 724 725 ::mozilla::ipc::PBackgroundChild* backgroundActor = 726 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 727 if (NS_WARN_IF(!backgroundActor)) { 728 return; 729 } 730 731 if (NS_WARN_IF(!backgroundActor->SendRemoveBackgroundSessionStorageManager( 732 aTopContextId))) { 733 return; 734 } 735 } 736 737 // static 738 void BackgroundSessionStorageManager::PropagateManager( 739 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) { 740 MOZ_ASSERT(XRE_IsParentProcess()); 741 AssertIsOnMainThread(); 742 MOZ_ASSERT(aCurrentTopContextId != aTargetTopContextId); 743 744 ::mozilla::ipc::PBackgroundChild* backgroundActor = 745 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 746 if (NS_WARN_IF(!backgroundActor)) { 747 return; 748 } 749 750 if (NS_WARN_IF(!backgroundActor->SendPropagateBackgroundSessionStorageManager( 751 aCurrentTopContextId, aTargetTopContextId))) { 752 return; 753 } 754 } 755 756 // static 757 void BackgroundSessionStorageManager::LoadData( 758 uint64_t aTopContextId, 759 const nsTArray<mozilla::dom::SSCacheCopy>& aCacheCopyList) { 760 MOZ_ASSERT(XRE_IsParentProcess()); 761 AssertIsOnMainThread(); 762 763 ::mozilla::ipc::PBackgroundChild* backgroundActor = 764 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 765 if (NS_WARN_IF(!backgroundActor)) { 766 return; 767 } 768 769 if (NS_WARN_IF(!backgroundActor->SendLoadSessionStorageManagerData( 770 aTopContextId, aCacheCopyList))) { 771 return; 772 } 773 } 774 775 // static 776 BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate( 777 uint64_t aTopContextId) { 778 MOZ_ASSERT(XRE_IsParentProcess()); 779 ::mozilla::ipc::AssertIsOnBackgroundThread(); 780 781 if (!sManagers) { 782 sManagers = new nsRefPtrHashtable<nsUint64HashKey, 783 BackgroundSessionStorageManager>(); 784 NS_DispatchToMainThread(NS_NewRunnableFunction( 785 "dom::BackgroundSessionStorageManager::GetOrCreate", [] { 786 RunOnShutdown( 787 [] { 788 ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla:: 789 ipc::BackgroundChild::GetOrCreateForCurrentThread(); 790 if (NS_WARN_IF(!backgroundActor)) { 791 return; 792 } 793 794 if (NS_WARN_IF( 795 !backgroundActor 796 ->SendShutdownBackgroundSessionStorageManagers())) { 797 return; 798 } 799 }, 800 ShutdownPhase::XPCOMShutdown); 801 })); 802 } 803 804 return sManagers 805 ->LookupOrInsertWith( 806 aTopContextId, 807 [aTopContextId] { 808 return new BackgroundSessionStorageManager(aTopContextId); 809 }) 810 .get(); 811 } 812 813 BackgroundSessionStorageManager::BackgroundSessionStorageManager( 814 uint64_t aBrowsingContextId) 815 : mCurrentBrowsingContextId(aBrowsingContextId) { 816 MOZ_ASSERT(XRE_IsParentProcess()); 817 ::mozilla::ipc::AssertIsOnBackgroundThread(); 818 } 819 820 BackgroundSessionStorageManager::~BackgroundSessionStorageManager() = default; 821 822 void BackgroundSessionStorageManager::CopyDataToContentProcess( 823 const nsACString& aOriginAttrs, const nsACString& aOriginKey, 824 nsTArray<SSSetItemInfo>& aData) { 825 MOZ_ASSERT(XRE_IsParentProcess()); 826 ::mozilla::ipc::AssertIsOnBackgroundThread(); 827 828 auto* const originRecord = 829 GetOriginRecord(aOriginAttrs, aOriginKey, false, nullptr); 830 if (!originRecord) { 831 return; 832 } 833 834 aData = originRecord->mCache->SerializeData(); 835 } 836 837 /* static */ 838 RefPtr<BackgroundSessionStorageManager::DataPromise> 839 BackgroundSessionStorageManager::GetData(BrowsingContext* aContext, 840 uint32_t aSizeLimit, 841 bool aClearSessionStoreTimer) { 842 MOZ_ASSERT(XRE_IsParentProcess()); 843 MOZ_ASSERT(aContext->IsTop()); 844 845 AssertIsOnMainThread(); 846 847 ::mozilla::ipc::PBackgroundChild* backgroundActor = 848 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 849 if (NS_WARN_IF(!backgroundActor)) { 850 return DataPromise::CreateAndReject( 851 ::mozilla::ipc::ResponseRejectReason::SendError, __func__); 852 } 853 854 return backgroundActor->SendGetSessionStorageManagerData( 855 aContext->Id(), aSizeLimit, aClearSessionStoreTimer); 856 } 857 858 void BackgroundSessionStorageManager::GetData( 859 uint32_t aSizeLimit, nsTArray<SSCacheCopy>& aCacheCopyList) { 860 for (auto& managerActor : mParticipatingActors) { 861 for (auto* cacheActor : 862 managerActor->ManagedPBackgroundSessionStorageCacheParent()) { 863 auto* cache = static_cast<SessionStorageCacheParent*>(cacheActor); 864 ::mozilla::ipc::PrincipalInfo info = cache->PrincipalInfo(); 865 866 OriginAttributes attributes; 867 StoragePrincipalHelper::GetOriginAttributes(cache->PrincipalInfo(), 868 attributes); 869 870 nsAutoCString originAttrs; 871 attributes.CreateSuffix(originAttrs); 872 873 auto* record = 874 GetOriginRecord(originAttrs, cache->OriginKey(), false, nullptr); 875 876 if (!record) { 877 continue; 878 } 879 880 if (record->mCache->GetOriginQuotaUsage() > aSizeLimit) { 881 continue; 882 } 883 884 nsTArray<SSSetItemInfo> data = record->mCache->SerializeData(); 885 if (data.IsEmpty()) { 886 continue; 887 } 888 889 SSCacheCopy& cacheCopy = *aCacheCopyList.AppendElement(); 890 cacheCopy.originKey() = cache->OriginKey(); 891 cacheCopy.principalInfo() = info; 892 cacheCopy.data().SwapElements(data); 893 } 894 } 895 } 896 897 void BackgroundSessionStorageManager::UpdateData( 898 const nsACString& aOriginAttrs, const nsACString& aOriginKey, 899 const nsTArray<SSWriteInfo>& aWriteInfos) { 900 MOZ_ASSERT(XRE_IsParentProcess()); 901 ::mozilla::ipc::AssertIsOnBackgroundThread(); 902 903 auto* const originRecord = 904 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr); 905 MOZ_ASSERT(originRecord); 906 907 MaybeScheduleSessionStoreUpdate(); 908 909 originRecord->mCache->DeserializeWriteInfos(aWriteInfos); 910 } 911 912 void BackgroundSessionStorageManager::UpdateData( 913 const nsACString& aOriginAttrs, const nsACString& aOriginKey, 914 const nsTArray<SSSetItemInfo>& aData) { 915 MOZ_ASSERT(XRE_IsParentProcess()); 916 ::mozilla::ipc::AssertIsOnBackgroundThread(); 917 918 auto* const originRecord = 919 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr); 920 MOZ_ASSERT(originRecord); 921 922 originRecord->mCache->DeserializeData(aData); 923 } 924 925 void BackgroundSessionStorageManager::ClearStorages( 926 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, 927 DomainMatchingMode aMode) { 928 MOZ_ASSERT(XRE_IsParentProcess()); 929 ::mozilla::ipc::AssertIsOnBackgroundThread(); 930 ClearStoragesInternal(aPattern, aOriginScope, aMode); 931 } 932 933 void BackgroundSessionStorageManager::ClearStoragesForOrigin( 934 const nsACString& aOriginAttrs, const nsACString& aOriginKey) { 935 ::mozilla::ipc::AssertIsInMainProcess(); 936 ::mozilla::ipc::AssertIsOnBackgroundThread(); 937 938 for (auto& managerActor : mParticipatingActors) { 939 QM_WARNONLY_TRY(OkIf(managerActor->SendClearStoragesForOrigin( 940 nsCString(aOriginAttrs), nsCString(aOriginKey)))); 941 } 942 943 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey); 944 } 945 946 void BackgroundSessionStorageManager::SetCurrentBrowsingContextId( 947 uint64_t aBrowsingContextId) { 948 MOZ_DIAGNOSTIC_ASSERT(aBrowsingContextId != mCurrentBrowsingContextId); 949 mCurrentBrowsingContextId = aBrowsingContextId; 950 } 951 952 void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() { 953 if (!SessionStorePlatformCollection()) { 954 return; 955 } 956 957 if (mSessionStoreCallbackTimer) { 958 return; 959 } 960 961 if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) { 962 DispatchSessionStoreUpdate(); 963 return; 964 } 965 966 auto result = NS_NewTimerWithFuncCallback( 967 [](nsITimer*, void* aClosure) { 968 auto* mgr = static_cast<BackgroundSessionStorageManager*>(aClosure); 969 mgr->DispatchSessionStoreUpdate(); 970 }, 971 this, StaticPrefs::browser_sessionstore_interval(), 972 nsITimer::TYPE_ONE_SHOT, 973 "BackgroundSessionStorageManager::DispatchSessionStoreUpdate"_ns); 974 975 if (result.isErr()) { 976 return; 977 } 978 979 mSessionStoreCallbackTimer = result.unwrap(); 980 } 981 982 void BackgroundSessionStorageManager::MaybeDispatchSessionStoreUpdate() { 983 if (mSessionStoreCallbackTimer) { 984 BackgroundSessionStorageManager::DispatchSessionStoreUpdate(); 985 } 986 } 987 988 void BackgroundSessionStorageManager::DispatchSessionStoreUpdate() { 989 ::mozilla::ipc::AssertIsOnBackgroundThread(); 990 NS_DispatchToMainThread(NS_NewRunnableFunction( 991 "CanonicalBrowsingContext::UpdateSessionStore", 992 [targetBrowsingContextId = mCurrentBrowsingContextId]() { 993 CanonicalBrowsingContext::UpdateSessionStoreForStorage( 994 targetBrowsingContextId); 995 })); 996 997 CancelSessionStoreUpdate(); 998 } 999 1000 void BackgroundSessionStorageManager::CancelSessionStoreUpdate() { 1001 if (mSessionStoreCallbackTimer) { 1002 mSessionStoreCallbackTimer->Cancel(); 1003 mSessionStoreCallbackTimer = nullptr; 1004 } 1005 } 1006 1007 void BackgroundSessionStorageManager::AddParticipatingActor( 1008 SessionStorageManagerParent* aActor) { 1009 ::mozilla::ipc::AssertIsOnBackgroundThread(); 1010 mParticipatingActors.AppendElement(aActor); 1011 } 1012 1013 void BackgroundSessionStorageManager::RemoveParticipatingActor( 1014 SessionStorageManagerParent* aActor) { 1015 ::mozilla::ipc::AssertIsOnBackgroundThread(); 1016 mParticipatingActors.RemoveElement(aActor); 1017 } 1018 1019 } // namespace mozilla::dom