Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / storage / SessionStorageManager.cpp
bloba64d5784267d9ae884b0d41e5638ad5ea6d08dd4
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/. */
7 #include "SessionStorageManager.h"
9 #include "StorageIPC.h"
10 #include "SessionStorage.h"
11 #include "SessionStorageCache.h"
12 #include "SessionStorageObserver.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"
33 namespace mozilla::dom {
35 using namespace StorageUtils;
37 // Parent process, background thread hashmap that stores top context id and
38 // manager pair.
39 static StaticAutoPtr<
40 nsRefPtrHashtable<nsUint64HashKey, BackgroundSessionStorageManager>>
41 sManagers;
43 bool RecvShutdownBackgroundSessionStorageManagers() {
44 ::mozilla::ipc::AssertIsOnBackgroundThread();
46 sManagers = nullptr;
47 return true;
50 void RecvPropagateBackgroundSessionStorageManager(
51 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
52 ::mozilla::ipc::AssertIsOnBackgroundThread();
54 if (sManagers) {
55 if (RefPtr<BackgroundSessionStorageManager> mgr =
56 sManagers->Get(aCurrentTopContextId)) {
57 mgr->MaybeDispatchSessionStoreUpdate();
58 mgr->SetCurrentBrowsingContextId(aTargetTopContextId);
59 // Because of bfcache, we may re-register aTargetTopContextId in
60 // CanonicalBrowsingContext::ReplacedBy.
61 // XXXBFCache do we want to tweak this behavior and ensure this is
62 // called only once?
63 sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr));
68 bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) {
69 ::mozilla::ipc::AssertIsOnBackgroundThread();
71 if (sManagers) {
72 RefPtr<BackgroundSessionStorageManager> mgr;
73 sManagers->Remove(aTopContextId, getter_AddRefs(mgr));
75 if (mgr) {
76 mgr->CancelSessionStoreUpdate();
80 return true;
83 bool RecvLoadSessionStorageData(
84 uint64_t aTopContextId,
85 nsTArray<mozilla::dom::SSCacheCopy>&& aCacheCopyList) {
86 if (aCacheCopyList.IsEmpty()) {
87 return true;
90 RefPtr<BackgroundSessionStorageManager> manager =
91 BackgroundSessionStorageManager::GetOrCreate(aTopContextId);
93 if (!manager) {
94 return true;
97 for (const auto& cacheInit : aCacheCopyList) {
98 OriginAttributes attrs;
99 StoragePrincipalHelper::GetOriginAttributes(cacheInit.principalInfo(),
100 attrs);
102 nsAutoCString originAttrs;
103 attrs.CreateSuffix(originAttrs);
105 manager->UpdateData(originAttrs, cacheInit.originKey(), cacheInit.data());
108 return true;
111 bool RecvGetSessionStorageData(
112 uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer,
113 ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&&
114 aResolver) {
115 nsTArray<mozilla::dom::SSCacheCopy> data;
116 auto resolve = MakeScopeExit([&]() { aResolver(std::move(data)); });
118 if (!sManagers) {
119 return true;
122 RefPtr<BackgroundSessionStorageManager> manager =
123 sManagers->Get(aTopContextId);
124 if (!manager) {
125 return true;
128 if (aCancelSessionStoreTimer) {
129 manager->CancelSessionStoreUpdate();
132 manager->GetData(aSizeLimit, data);
134 return true;
137 bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs,
138 const nsACString& aOriginKey) {
139 mozilla::ipc::AssertIsInMainProcess();
140 mozilla::ipc::AssertIsOnBackgroundThread();
142 if (!sManagers) {
143 return true;
146 for (auto& entry : *sManagers) {
147 entry.GetData()->ClearStoragesForOrigin(aOriginAttrs, aOriginKey);
150 return true;
153 void SessionStorageManagerBase::ClearStoragesInternal(
154 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
155 for (const auto& oaEntry : mOATable) {
156 OriginAttributes oa;
157 DebugOnly<bool> ok = oa.PopulateFromSuffix(oaEntry.GetKey());
158 MOZ_ASSERT(ok);
159 if (!aPattern.Matches(oa)) {
160 // This table doesn't match the given origin attributes pattern
161 continue;
164 OriginKeyHashTable* table = oaEntry.GetWeak();
165 for (const auto& originKeyEntry : *table) {
166 if (aOriginScope.IsEmpty() ||
167 StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) {
168 const auto cache = originKeyEntry.GetData()->mCache;
169 cache->Clear(false);
170 cache->ResetWriteInfos();
176 void SessionStorageManagerBase::ClearStoragesForOriginInternal(
177 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
178 for (const auto& oaEntry : mOATable) {
179 // Filter tables which match the given origin attrs.
180 if (oaEntry.GetKey() != aOriginAttrs) {
181 continue;
184 OriginKeyHashTable* table = oaEntry.GetWeak();
185 for (const auto& originKeyEntry : *table) {
186 // Match exact origin (without origin attrs).
187 if (originKeyEntry.GetKey() != aOriginKey) {
188 continue;
191 const auto cache = originKeyEntry.GetData()->mCache;
192 cache->Clear(false);
193 cache->ResetWriteInfos();
198 SessionStorageManagerBase::OriginRecord*
199 SessionStorageManagerBase::GetOriginRecord(
200 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
201 const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) {
202 // XXX It seems aMakeIfNeeded is always known at compile-time, so this could
203 // be split into two functions.
205 if (aMakeIfNeeded) {
206 return mOATable.GetOrInsertNew(aOriginAttrs)
207 ->LookupOrInsertWith(
208 aOriginKey,
209 [&] {
210 auto newOriginRecord = MakeUnique<OriginRecord>();
211 if (aCloneFrom) {
212 newOriginRecord->mCache = aCloneFrom->Clone();
213 } else {
214 newOriginRecord->mCache = new SessionStorageCache();
216 return newOriginRecord;
218 .get();
221 auto* const table = mOATable.Get(aOriginAttrs);
222 if (!table) return nullptr;
224 return table->Get(aOriginKey);
227 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager)
228 NS_INTERFACE_MAP_ENTRY(nsISupports)
229 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
230 NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager)
231 NS_INTERFACE_MAP_END
233 NS_IMPL_CYCLE_COLLECTION(SessionStorageManager, mBrowsingContext)
234 NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStorageManager)
235 NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStorageManager)
237 SessionStorageManager::SessionStorageManager(
238 RefPtr<BrowsingContext> aBrowsingContext)
239 : mBrowsingContext(std::move(aBrowsingContext)), mActor(nullptr) {
240 AssertIsOnMainThread();
242 StorageObserver* observer = StorageObserver::Self();
243 NS_ASSERTION(
244 observer,
245 "No StorageObserver, cannot observe private data delete notifications!");
247 if (observer) {
248 observer->AddSink(this);
251 if (!XRE_IsParentProcess() && NextGenLocalStorageEnabled()) {
252 // When LSNG is enabled the thread IPC bridge doesn't exist, so we have to
253 // create own protocol to distribute chrome observer notifications to
254 // content processes.
255 mObserver = SessionStorageObserver::Get();
257 if (!mObserver) {
258 ContentChild* contentActor = ContentChild::GetSingleton();
259 MOZ_ASSERT(contentActor);
261 RefPtr<SessionStorageObserver> observer = new SessionStorageObserver();
263 SessionStorageObserverChild* actor =
264 new SessionStorageObserverChild(observer);
266 MOZ_ALWAYS_TRUE(
267 contentActor->SendPSessionStorageObserverConstructor(actor));
269 observer->SetActor(actor);
271 mObserver = std::move(observer);
276 SessionStorageManager::~SessionStorageManager() {
277 StorageObserver* observer = StorageObserver::Self();
278 if (observer) {
279 observer->RemoveSink(this);
282 if (mActor) {
283 mActor->SendDeleteMeInternal();
284 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
288 bool SessionStorageManager::CanLoadData() {
289 AssertIsOnMainThread();
291 return mBrowsingContext && !mBrowsingContext->IsDiscarded();
294 void SessionStorageManager::SetActor(SessionStorageManagerChild* aActor) {
295 AssertIsOnMainThread();
296 MOZ_ASSERT(aActor);
297 MOZ_ASSERT(!mActor);
299 mActor = aActor;
302 bool SessionStorageManager::ActorExists() const {
303 AssertIsOnMainThread();
305 return mActor;
308 void SessionStorageManager::ClearActor() {
309 AssertIsOnMainThread();
310 MOZ_ASSERT(mActor);
312 mActor = nullptr;
315 nsresult SessionStorageManager::EnsureManager() {
316 AssertIsOnMainThread();
317 MOZ_ASSERT(CanLoadData());
319 if (ActorExists()) {
320 return NS_OK;
323 ::mozilla::ipc::PBackgroundChild* backgroundActor =
324 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
325 if (NS_WARN_IF(!backgroundActor)) {
326 return NS_ERROR_FAILURE;
329 RefPtr<SessionStorageManagerChild> actor =
330 new SessionStorageManagerChild(this);
332 if (!backgroundActor->SendPBackgroundSessionStorageManagerConstructor(
333 actor, mBrowsingContext->Top()->Id())) {
334 return NS_ERROR_FAILURE;
337 SetActor(actor);
339 return NS_OK;
342 SessionStorageCacheChild* SessionStorageManager::EnsureCache(
343 nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
344 SessionStorageCache& aCache) {
345 AssertIsOnMainThread();
346 MOZ_ASSERT(CanLoadData());
347 MOZ_ASSERT(ActorExists());
349 if (aCache.Actor()) {
350 return aCache.Actor();
353 mozilla::ipc::PrincipalInfo info;
354 nsresult rv = PrincipalToPrincipalInfo(&aPrincipal, &info);
356 if (NS_FAILED(rv)) {
357 return nullptr;
360 RefPtr<SessionStorageCacheChild> actor =
361 new SessionStorageCacheChild(&aCache);
362 if (!mActor->SendPBackgroundSessionStorageCacheConstructor(actor, info,
363 aOriginKey)) {
364 return nullptr;
367 aCache.SetActor(actor);
369 return actor;
372 nsresult SessionStorageManager::LoadData(nsIPrincipal& aPrincipal,
373 SessionStorageCache& aCache) {
374 AssertIsOnMainThread();
375 MOZ_ASSERT(mActor);
377 nsAutoCString originKey;
378 nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
379 if (NS_WARN_IF(NS_FAILED(rv))) {
380 return rv;
383 nsAutoCString originAttributes;
384 aPrincipal.OriginAttributesRef().CreateSuffix(originAttributes);
386 auto* const originRecord =
387 GetOriginRecord(originAttributes, originKey, true, nullptr);
388 MOZ_ASSERT(originRecord);
390 if (originRecord->mLoaded) {
391 return NS_OK;
394 RefPtr<SessionStorageCacheChild> cacheActor =
395 EnsureCache(aPrincipal, originKey, aCache);
397 if (!cacheActor) {
398 return NS_ERROR_FAILURE;
401 nsTArray<SSSetItemInfo> data;
402 if (!cacheActor->SendLoad(&data)) {
403 return NS_ERROR_FAILURE;
406 originRecord->mCache->DeserializeData(data);
408 originRecord->mLoaded.Flip();
409 aCache.SetLoadedOrCloned();
411 return NS_OK;
414 void SessionStorageManager::CheckpointData(nsIPrincipal& aPrincipal,
415 SessionStorageCache& aCache) {
416 AssertIsOnMainThread();
417 MOZ_ASSERT(mActor);
419 nsAutoCString originKey;
420 nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
421 if (NS_WARN_IF(NS_FAILED(rv))) {
422 return;
425 return CheckpointDataInternal(aPrincipal, originKey, aCache);
428 void SessionStorageManager::CheckpointDataInternal(
429 nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
430 SessionStorageCache& aCache) {
431 AssertIsOnMainThread();
432 MOZ_ASSERT(mActor);
434 nsTArray<SSWriteInfo> writeInfos = aCache.SerializeWriteInfos();
436 if (writeInfos.IsEmpty()) {
437 return;
440 RefPtr<SessionStorageCacheChild> cacheActor =
441 EnsureCache(aPrincipal, aOriginKey, aCache);
443 if (!cacheActor) {
444 return;
447 Unused << cacheActor->SendCheckpoint(writeInfos);
449 aCache.ResetWriteInfos();
452 nsresult SessionStorageManager::ClearStoragesForOrigin(
453 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
454 AssertIsOnMainThread();
456 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
458 return NS_OK;
461 NS_IMETHODIMP
462 SessionStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
463 nsIPrincipal* aStoragePrincipal,
464 Storage** aRetval) {
465 // Nothing to preload.
466 return NS_OK;
469 NS_IMETHODIMP
470 SessionStorageManager::GetSessionStorageCache(
471 nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
472 RefPtr<SessionStorageCache>* aRetVal) {
473 return GetSessionStorageCacheHelper(aStoragePrincipal, true, nullptr,
474 aRetVal);
477 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
478 nsIPrincipal* aPrincipal, bool aMakeIfNeeded,
479 SessionStorageCache* aCloneFrom, RefPtr<SessionStorageCache>* aRetVal) {
480 nsAutoCString originKey;
481 nsAutoCString originAttributes;
482 nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
483 aPrincipal->OriginAttributesRef().CreateSuffix(originAttributes);
484 if (NS_FAILED(rv)) {
485 return NS_ERROR_NOT_AVAILABLE;
488 return GetSessionStorageCacheHelper(originAttributes, originKey,
489 aMakeIfNeeded, aCloneFrom, aRetVal);
492 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
493 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
494 bool aMakeIfNeeded, SessionStorageCache* aCloneFrom,
495 RefPtr<SessionStorageCache>* aRetVal) {
496 if (OriginRecord* const originRecord = GetOriginRecord(
497 aOriginAttrs, aOriginKey, aMakeIfNeeded, aCloneFrom)) {
498 *aRetVal = originRecord->mCache;
499 } else {
500 *aRetVal = nullptr;
502 return NS_OK;
505 NS_IMETHODIMP
506 SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow,
507 nsIPrincipal* aPrincipal,
508 nsIPrincipal* aStoragePrincipal,
509 const nsAString& aDocumentURI,
510 bool aPrivate, Storage** aRetval) {
511 RefPtr<SessionStorageCache> cache;
512 nsresult rv = GetSessionStorageCache(aPrincipal, aStoragePrincipal, &cache);
513 if (NS_FAILED(rv)) {
514 return rv;
517 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
519 RefPtr<SessionStorage> storage =
520 new SessionStorage(inner, aPrincipal, aStoragePrincipal, cache, this,
521 aDocumentURI, aPrivate);
523 storage.forget(aRetval);
524 return NS_OK;
527 NS_IMETHODIMP
528 SessionStorageManager::GetStorage(mozIDOMWindow* aWindow,
529 nsIPrincipal* aPrincipal,
530 nsIPrincipal* aStoragePrincipal,
531 bool aPrivate, Storage** aRetval) {
532 *aRetval = nullptr;
534 RefPtr<SessionStorageCache> cache;
535 nsresult rv =
536 GetSessionStorageCacheHelper(aStoragePrincipal, false, nullptr, &cache);
537 if (NS_FAILED(rv) || !cache) {
538 return rv;
541 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
543 RefPtr<SessionStorage> storage = new SessionStorage(
544 inner, aPrincipal, aStoragePrincipal, cache, this, u""_ns, aPrivate);
546 storage.forget(aRetval);
547 return NS_OK;
550 NS_IMETHODIMP
551 SessionStorageManager::CloneStorage(Storage* aStorage) {
552 if (NS_WARN_IF(!aStorage)) {
553 return NS_ERROR_UNEXPECTED;
556 if (aStorage->Type() != Storage::eSessionStorage) {
557 return NS_ERROR_UNEXPECTED;
560 // ToDo: At the moment, we clone the cache on the child process and then
561 // send the checkpoint. It would be nicer if we either serailizing all the
562 // data and sync to the parent process directly or clonig storage on the
563 // parnet process and sync it to the child process on demand.
565 RefPtr<SessionStorageCache> cache;
566 nsresult rv = GetSessionStorageCacheHelper(
567 aStorage->StoragePrincipal(), true,
568 static_cast<SessionStorage*>(aStorage)->Cache(), &cache);
569 if (NS_WARN_IF(NS_FAILED(rv))) {
570 return rv;
573 // If cache was cloned from other storage, then we shouldn't load the cache
574 // at the first access.
575 cache->SetLoadedOrCloned();
577 if (CanLoadData()) {
578 rv = EnsureManager();
579 if (NS_WARN_IF(NS_FAILED(rv))) {
580 return rv;
583 CheckpointData(*aStorage->StoragePrincipal(), *cache);
586 return rv;
589 NS_IMETHODIMP
590 SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
591 bool* aRetval) {
592 if (NS_WARN_IF(!aStorage)) {
593 return NS_ERROR_UNEXPECTED;
596 if (!aPrincipal) {
597 return NS_ERROR_NOT_AVAILABLE;
600 *aRetval = false;
602 RefPtr<SessionStorageCache> cache;
603 nsresult rv =
604 GetSessionStorageCacheHelper(aPrincipal, false, nullptr, &cache);
605 if (NS_FAILED(rv) || !cache) {
606 return rv;
609 if (aStorage->Type() != Storage::eSessionStorage) {
610 return NS_OK;
613 RefPtr<SessionStorage> sessionStorage =
614 static_cast<SessionStorage*>(aStorage);
615 if (sessionStorage->Cache() != cache) {
616 return NS_OK;
619 if (!StorageUtils::PrincipalsEqual(aStorage->StoragePrincipal(),
620 aPrincipal)) {
621 return NS_OK;
624 *aRetval = true;
625 return NS_OK;
628 void SessionStorageManager::ClearStorages(
629 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
630 if (CanLoadData()) {
631 nsresult rv = EnsureManager();
632 if (NS_WARN_IF(NS_FAILED(rv))) {
633 return;
636 mActor->SendClearStorages(aPattern, nsCString(aOriginScope));
639 ClearStoragesInternal(aPattern, aOriginScope);
642 nsresult SessionStorageManager::Observe(
643 const char* aTopic, const nsAString& aOriginAttributesPattern,
644 const nsACString& aOriginScope) {
645 OriginAttributesPattern pattern;
646 if (!pattern.Init(aOriginAttributesPattern)) {
647 NS_ERROR("Cannot parse origin attributes pattern");
648 return NS_ERROR_FAILURE;
651 // Clear everything, caches + database
652 if (!strcmp(aTopic, "cookie-cleared")) {
653 ClearStorages(pattern, ""_ns);
654 return NS_OK;
657 // Clear from caches everything that has been stored
658 // while in session-only mode
659 if (!strcmp(aTopic, "session-only-cleared")) {
660 ClearStorages(pattern, aOriginScope);
661 return NS_OK;
664 // Clear everything (including so and pb data) from caches and database
665 // for the given domain and subdomains.
666 if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
667 ClearStorages(pattern, aOriginScope);
668 return NS_OK;
671 // Clear entries which match an OriginAttributesPattern.
672 if (!strcmp(aTopic, "dom-storage:clear-origin-attributes-data") ||
673 !strcmp(aTopic, "session-storage:clear-origin-attributes-data")) {
674 ClearStorages(pattern, aOriginScope);
675 return NS_OK;
678 if (!strcmp(aTopic, "profile-change")) {
679 // For case caches are still referenced - clear them completely
680 ClearStorages(pattern, ""_ns);
681 mOATable.Clear();
682 return NS_OK;
685 return NS_OK;
688 SessionStorageManager::OriginRecord::~OriginRecord() = default;
690 // static
691 void BackgroundSessionStorageManager::RemoveManager(uint64_t aTopContextId) {
692 MOZ_ASSERT(XRE_IsParentProcess());
693 AssertIsOnMainThread();
695 ::mozilla::ipc::PBackgroundChild* backgroundActor =
696 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
697 if (NS_WARN_IF(!backgroundActor)) {
698 return;
701 if (NS_WARN_IF(!backgroundActor->SendRemoveBackgroundSessionStorageManager(
702 aTopContextId))) {
703 return;
707 // static
708 void BackgroundSessionStorageManager::PropagateManager(
709 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
710 MOZ_ASSERT(XRE_IsParentProcess());
711 AssertIsOnMainThread();
712 MOZ_ASSERT(aCurrentTopContextId != aTargetTopContextId);
714 ::mozilla::ipc::PBackgroundChild* backgroundActor =
715 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
716 if (NS_WARN_IF(!backgroundActor)) {
717 return;
720 if (NS_WARN_IF(!backgroundActor->SendPropagateBackgroundSessionStorageManager(
721 aCurrentTopContextId, aTargetTopContextId))) {
722 return;
726 // static
727 void BackgroundSessionStorageManager::LoadData(
728 uint64_t aTopContextId,
729 const nsTArray<mozilla::dom::SSCacheCopy>& aCacheCopyList) {
730 MOZ_ASSERT(XRE_IsParentProcess());
731 AssertIsOnMainThread();
733 ::mozilla::ipc::PBackgroundChild* backgroundActor =
734 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
735 if (NS_WARN_IF(!backgroundActor)) {
736 return;
739 if (NS_WARN_IF(!backgroundActor->SendLoadSessionStorageManagerData(
740 aTopContextId, aCacheCopyList))) {
741 return;
745 // static
746 BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate(
747 uint64_t aTopContextId) {
748 MOZ_ASSERT(XRE_IsParentProcess());
749 ::mozilla::ipc::AssertIsOnBackgroundThread();
751 if (!sManagers) {
752 sManagers = new nsRefPtrHashtable<nsUint64HashKey,
753 BackgroundSessionStorageManager>();
754 NS_DispatchToMainThread(NS_NewRunnableFunction(
755 "dom::BackgroundSessionStorageManager::GetOrCreate", [] {
756 RunOnShutdown(
757 [] {
758 ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::
759 ipc::BackgroundChild::GetOrCreateForCurrentThread();
760 if (NS_WARN_IF(!backgroundActor)) {
761 return;
764 if (NS_WARN_IF(
765 !backgroundActor
766 ->SendShutdownBackgroundSessionStorageManagers())) {
767 return;
770 ShutdownPhase::XPCOMShutdown);
771 }));
774 return sManagers
775 ->LookupOrInsertWith(
776 aTopContextId,
777 [aTopContextId] {
778 return new BackgroundSessionStorageManager(aTopContextId);
780 .get();
783 BackgroundSessionStorageManager::BackgroundSessionStorageManager(
784 uint64_t aBrowsingContextId)
785 : mCurrentBrowsingContextId(aBrowsingContextId) {
786 MOZ_ASSERT(XRE_IsParentProcess());
787 ::mozilla::ipc::AssertIsOnBackgroundThread();
790 BackgroundSessionStorageManager::~BackgroundSessionStorageManager() = default;
792 void BackgroundSessionStorageManager::CopyDataToContentProcess(
793 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
794 nsTArray<SSSetItemInfo>& aData) {
795 MOZ_ASSERT(XRE_IsParentProcess());
796 ::mozilla::ipc::AssertIsOnBackgroundThread();
798 auto* const originRecord =
799 GetOriginRecord(aOriginAttrs, aOriginKey, false, nullptr);
800 if (!originRecord) {
801 return;
804 aData = originRecord->mCache->SerializeData();
807 /* static */
808 RefPtr<BackgroundSessionStorageManager::DataPromise>
809 BackgroundSessionStorageManager::GetData(BrowsingContext* aContext,
810 uint32_t aSizeLimit,
811 bool aClearSessionStoreTimer) {
812 MOZ_ASSERT(XRE_IsParentProcess());
813 MOZ_ASSERT(aContext->IsTop());
815 AssertIsOnMainThread();
817 ::mozilla::ipc::PBackgroundChild* backgroundActor =
818 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
819 if (NS_WARN_IF(!backgroundActor)) {
820 return DataPromise::CreateAndReject(
821 ::mozilla::ipc::ResponseRejectReason::SendError, __func__);
824 return backgroundActor->SendGetSessionStorageManagerData(
825 aContext->Id(), aSizeLimit, aClearSessionStoreTimer);
828 void BackgroundSessionStorageManager::GetData(
829 uint32_t aSizeLimit, nsTArray<SSCacheCopy>& aCacheCopyList) {
830 for (auto& managerActor : mParticipatingActors) {
831 for (auto* cacheActor :
832 managerActor->ManagedPBackgroundSessionStorageCacheParent()) {
833 auto* cache = static_cast<SessionStorageCacheParent*>(cacheActor);
834 ::mozilla::ipc::PrincipalInfo info = cache->PrincipalInfo();
836 OriginAttributes attributes;
837 StoragePrincipalHelper::GetOriginAttributes(cache->PrincipalInfo(),
838 attributes);
840 nsAutoCString originAttrs;
841 attributes.CreateSuffix(originAttrs);
843 auto* record =
844 GetOriginRecord(originAttrs, cache->OriginKey(), false, nullptr);
846 if (!record) {
847 continue;
850 if (record->mCache->GetOriginQuotaUsage() > aSizeLimit) {
851 continue;
854 nsTArray<SSSetItemInfo> data = record->mCache->SerializeData();
855 if (data.IsEmpty()) {
856 continue;
859 SSCacheCopy& cacheCopy = *aCacheCopyList.AppendElement();
860 cacheCopy.originKey() = cache->OriginKey();
861 cacheCopy.principalInfo() = info;
862 cacheCopy.data().SwapElements(data);
867 void BackgroundSessionStorageManager::UpdateData(
868 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
869 const nsTArray<SSWriteInfo>& aWriteInfos) {
870 MOZ_ASSERT(XRE_IsParentProcess());
871 ::mozilla::ipc::AssertIsOnBackgroundThread();
873 auto* const originRecord =
874 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
875 MOZ_ASSERT(originRecord);
877 MaybeScheduleSessionStoreUpdate();
879 originRecord->mCache->DeserializeWriteInfos(aWriteInfos);
882 void BackgroundSessionStorageManager::UpdateData(
883 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
884 const nsTArray<SSSetItemInfo>& aData) {
885 MOZ_ASSERT(XRE_IsParentProcess());
886 ::mozilla::ipc::AssertIsOnBackgroundThread();
888 auto* const originRecord =
889 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
890 MOZ_ASSERT(originRecord);
892 originRecord->mCache->DeserializeData(aData);
895 void BackgroundSessionStorageManager::ClearStorages(
896 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
897 MOZ_ASSERT(XRE_IsParentProcess());
898 ::mozilla::ipc::AssertIsOnBackgroundThread();
899 ClearStoragesInternal(aPattern, aOriginScope);
902 void BackgroundSessionStorageManager::ClearStoragesForOrigin(
903 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
904 ::mozilla::ipc::AssertIsInMainProcess();
905 ::mozilla::ipc::AssertIsOnBackgroundThread();
907 for (auto& managerActor : mParticipatingActors) {
908 QM_WARNONLY_TRY(OkIf(managerActor->SendClearStoragesForOrigin(
909 nsCString(aOriginAttrs), nsCString(aOriginKey))));
912 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
915 void BackgroundSessionStorageManager::SetCurrentBrowsingContextId(
916 uint64_t aBrowsingContextId) {
917 MOZ_DIAGNOSTIC_ASSERT(aBrowsingContextId != mCurrentBrowsingContextId);
918 mCurrentBrowsingContextId = aBrowsingContextId;
921 void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() {
922 if (!SessionStorePlatformCollection()) {
923 return;
926 if (mSessionStoreCallbackTimer) {
927 return;
930 if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
931 DispatchSessionStoreUpdate();
932 return;
935 auto result = NS_NewTimerWithFuncCallback(
936 [](nsITimer*, void* aClosure) {
937 auto* mgr = static_cast<BackgroundSessionStorageManager*>(aClosure);
938 mgr->DispatchSessionStoreUpdate();
940 this, StaticPrefs::browser_sessionstore_interval(),
941 nsITimer::TYPE_ONE_SHOT,
942 "BackgroundSessionStorageManager::DispatchSessionStoreUpdate");
944 if (result.isErr()) {
945 return;
948 mSessionStoreCallbackTimer = result.unwrap();
951 void BackgroundSessionStorageManager::MaybeDispatchSessionStoreUpdate() {
952 if (mSessionStoreCallbackTimer) {
953 BackgroundSessionStorageManager::DispatchSessionStoreUpdate();
957 void BackgroundSessionStorageManager::DispatchSessionStoreUpdate() {
958 ::mozilla::ipc::AssertIsOnBackgroundThread();
959 NS_DispatchToMainThread(NS_NewRunnableFunction(
960 "CanonicalBrowsingContext::UpdateSessionStore",
961 [targetBrowsingContextId = mCurrentBrowsingContextId]() {
962 CanonicalBrowsingContext::UpdateSessionStoreForStorage(
963 targetBrowsingContextId);
964 }));
966 CancelSessionStoreUpdate();
969 void BackgroundSessionStorageManager::CancelSessionStoreUpdate() {
970 if (mSessionStoreCallbackTimer) {
971 mSessionStoreCallbackTimer->Cancel();
972 mSessionStoreCallbackTimer = nullptr;
976 void BackgroundSessionStorageManager::AddParticipatingActor(
977 SessionStorageManagerParent* aActor) {
978 ::mozilla::ipc::AssertIsOnBackgroundThread();
979 mParticipatingActors.AppendElement(aActor);
982 void BackgroundSessionStorageManager::RemoveParticipatingActor(
983 SessionStorageManagerParent* aActor) {
984 ::mozilla::ipc::AssertIsOnBackgroundThread();
985 mParticipatingActors.RemoveElement(aActor);
988 } // namespace mozilla::dom