Bug 1787199 [wpt PR 35620] - Add tests for `VisibilityStateEntry`, a=testonly
[gecko.git] / dom / storage / SessionStorageManager.cpp
blob020083730ab5f3adcf986980aab4ccc710e92a31
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 "nsTHashMap.h"
30 #include "nsThreadUtils.h"
32 namespace mozilla::dom {
34 using namespace StorageUtils;
36 // Parent process, background thread hashmap that stores top context id and
37 // manager pair.
38 static StaticAutoPtr<
39 nsRefPtrHashtable<nsUint64HashKey, BackgroundSessionStorageManager>>
40 sManagers;
42 bool RecvShutdownBackgroundSessionStorageManagers() {
43 ::mozilla::ipc::AssertIsOnBackgroundThread();
45 sManagers = nullptr;
46 return true;
49 void RecvPropagateBackgroundSessionStorageManager(
50 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
51 ::mozilla::ipc::AssertIsOnBackgroundThread();
53 if (sManagers) {
54 if (RefPtr<BackgroundSessionStorageManager> mgr =
55 sManagers->Get(aCurrentTopContextId)) {
56 mgr->MaybeDispatchSessionStoreUpdate();
57 mgr->SetCurrentBrowsingContextId(aTargetTopContextId);
58 // Because of bfcache, we may re-register aTargetTopContextId in
59 // CanonicalBrowsingContext::ReplacedBy.
60 // XXXBFCache do we want to tweak this behavior and ensure this is
61 // called only once?
62 sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr));
67 bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) {
68 ::mozilla::ipc::AssertIsOnBackgroundThread();
70 if (sManagers) {
71 RefPtr<BackgroundSessionStorageManager> mgr;
72 sManagers->Remove(aTopContextId, getter_AddRefs(mgr));
74 if (mgr) {
75 mgr->CancelSessionStoreUpdate();
79 return true;
82 bool RecvLoadSessionStorageData(
83 uint64_t aTopContextId,
84 nsTArray<mozilla::dom::SSCacheCopy>&& aCacheCopyList) {
85 if (aCacheCopyList.IsEmpty()) {
86 return true;
89 RefPtr<BackgroundSessionStorageManager> manager =
90 BackgroundSessionStorageManager::GetOrCreate(aTopContextId);
92 if (!manager) {
93 return true;
96 for (const auto& cacheInit : aCacheCopyList) {
97 OriginAttributes attrs;
98 StoragePrincipalHelper::GetOriginAttributes(cacheInit.principalInfo(),
99 attrs);
101 nsAutoCString originAttrs;
102 attrs.CreateSuffix(originAttrs);
104 manager->UpdateData(originAttrs, cacheInit.originKey(), cacheInit.data());
107 return true;
110 bool RecvGetSessionStorageData(
111 uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer,
112 ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&&
113 aResolver) {
114 nsTArray<mozilla::dom::SSCacheCopy> data;
115 auto resolve = MakeScopeExit([&]() { aResolver(std::move(data)); });
117 if (!sManagers) {
118 return true;
121 RefPtr<BackgroundSessionStorageManager> manager =
122 sManagers->Get(aTopContextId);
123 if (!manager) {
124 return true;
127 if (aCancelSessionStoreTimer) {
128 manager->CancelSessionStoreUpdate();
131 manager->GetData(aSizeLimit, data);
133 return true;
136 bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs,
137 const nsACString& aOriginKey) {
138 mozilla::ipc::AssertIsInMainProcess();
139 mozilla::ipc::AssertIsOnBackgroundThread();
141 if (!sManagers) {
142 return true;
145 for (auto& entry : *sManagers) {
146 entry.GetData()->ClearStoragesForOrigin(aOriginAttrs, aOriginKey);
149 return true;
152 void SessionStorageManagerBase::ClearStoragesInternal(
153 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
154 for (const auto& oaEntry : mOATable) {
155 OriginAttributes oa;
156 DebugOnly<bool> ok = oa.PopulateFromSuffix(oaEntry.GetKey());
157 MOZ_ASSERT(ok);
158 if (!aPattern.Matches(oa)) {
159 // This table doesn't match the given origin attributes pattern
160 continue;
163 OriginKeyHashTable* table = oaEntry.GetWeak();
164 for (const auto& originKeyEntry : *table) {
165 if (aOriginScope.IsEmpty() ||
166 StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) {
167 const auto cache = originKeyEntry.GetData()->mCache;
168 cache->Clear(false);
169 cache->ResetWriteInfos();
175 void SessionStorageManagerBase::ClearStoragesForOriginInternal(
176 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
177 for (const auto& oaEntry : mOATable) {
178 // Filter tables which match the given origin attrs.
179 if (oaEntry.GetKey() != aOriginAttrs) {
180 continue;
183 OriginKeyHashTable* table = oaEntry.GetWeak();
184 for (const auto& originKeyEntry : *table) {
185 // Match exact origin (without origin attrs).
186 if (originKeyEntry.GetKey() != aOriginKey) {
187 continue;
190 const auto cache = originKeyEntry.GetData()->mCache;
191 cache->Clear(false);
192 cache->ResetWriteInfos();
197 SessionStorageManagerBase::OriginRecord*
198 SessionStorageManagerBase::GetOriginRecord(
199 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
200 const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) {
201 // XXX It seems aMakeIfNeeded is always known at compile-time, so this could
202 // be split into two functions.
204 if (aMakeIfNeeded) {
205 return mOATable.GetOrInsertNew(aOriginAttrs)
206 ->LookupOrInsertWith(
207 aOriginKey,
208 [&] {
209 auto newOriginRecord = MakeUnique<OriginRecord>();
210 if (aCloneFrom) {
211 newOriginRecord->mCache = aCloneFrom->Clone();
212 } else {
213 newOriginRecord->mCache = new SessionStorageCache();
215 return newOriginRecord;
217 .get();
220 auto* const table = mOATable.Get(aOriginAttrs);
221 if (!table) return nullptr;
223 return table->Get(aOriginKey);
226 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager)
227 NS_INTERFACE_MAP_ENTRY(nsISupports)
228 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
229 NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager)
230 NS_INTERFACE_MAP_END
232 NS_IMPL_CYCLE_COLLECTION(SessionStorageManager, mBrowsingContext)
233 NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStorageManager)
234 NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStorageManager)
236 SessionStorageManager::SessionStorageManager(
237 RefPtr<BrowsingContext> aBrowsingContext)
238 : mBrowsingContext(std::move(aBrowsingContext)), mActor(nullptr) {
239 AssertIsOnMainThread();
241 StorageObserver* observer = StorageObserver::Self();
242 NS_ASSERTION(
243 observer,
244 "No StorageObserver, cannot observe private data delete notifications!");
246 if (observer) {
247 observer->AddSink(this);
250 if (!XRE_IsParentProcess() && NextGenLocalStorageEnabled()) {
251 // When LSNG is enabled the thread IPC bridge doesn't exist, so we have to
252 // create own protocol to distribute chrome observer notifications to
253 // content processes.
254 mObserver = SessionStorageObserver::Get();
256 if (!mObserver) {
257 ContentChild* contentActor = ContentChild::GetSingleton();
258 MOZ_ASSERT(contentActor);
260 RefPtr<SessionStorageObserver> observer = new SessionStorageObserver();
262 SessionStorageObserverChild* actor =
263 new SessionStorageObserverChild(observer);
265 MOZ_ALWAYS_TRUE(
266 contentActor->SendPSessionStorageObserverConstructor(actor));
268 observer->SetActor(actor);
270 mObserver = std::move(observer);
275 SessionStorageManager::~SessionStorageManager() {
276 StorageObserver* observer = StorageObserver::Self();
277 if (observer) {
278 observer->RemoveSink(this);
281 if (mActor) {
282 mActor->SendDeleteMeInternal();
283 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
287 bool SessionStorageManager::CanLoadData() {
288 AssertIsOnMainThread();
290 return mBrowsingContext && !mBrowsingContext->IsDiscarded();
293 void SessionStorageManager::SetActor(SessionStorageManagerChild* aActor) {
294 AssertIsOnMainThread();
295 MOZ_ASSERT(aActor);
296 MOZ_ASSERT(!mActor);
298 mActor = aActor;
301 bool SessionStorageManager::ActorExists() const {
302 AssertIsOnMainThread();
304 return mActor;
307 void SessionStorageManager::ClearActor() {
308 AssertIsOnMainThread();
309 MOZ_ASSERT(mActor);
311 mActor = nullptr;
314 nsresult SessionStorageManager::EnsureManager() {
315 AssertIsOnMainThread();
316 MOZ_ASSERT(CanLoadData());
318 if (ActorExists()) {
319 return NS_OK;
322 ::mozilla::ipc::PBackgroundChild* backgroundActor =
323 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
324 if (NS_WARN_IF(!backgroundActor)) {
325 return NS_ERROR_FAILURE;
328 RefPtr<SessionStorageManagerChild> actor =
329 new SessionStorageManagerChild(this);
331 if (!backgroundActor->SendPBackgroundSessionStorageManagerConstructor(
332 actor, mBrowsingContext->Top()->Id())) {
333 return NS_ERROR_FAILURE;
336 SetActor(actor);
338 return NS_OK;
341 SessionStorageCacheChild* SessionStorageManager::EnsureCache(
342 nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
343 SessionStorageCache& aCache) {
344 AssertIsOnMainThread();
345 MOZ_ASSERT(CanLoadData());
346 MOZ_ASSERT(ActorExists());
348 if (aCache.Actor()) {
349 return aCache.Actor();
352 mozilla::ipc::PrincipalInfo info;
353 nsresult rv = PrincipalToPrincipalInfo(&aPrincipal, &info);
355 if (NS_FAILED(rv)) {
356 return nullptr;
359 RefPtr<SessionStorageCacheChild> actor =
360 new SessionStorageCacheChild(&aCache);
361 if (!mActor->SendPBackgroundSessionStorageCacheConstructor(actor, info,
362 aOriginKey)) {
363 return nullptr;
366 aCache.SetActor(actor);
368 return actor;
371 nsresult SessionStorageManager::LoadData(nsIPrincipal& aPrincipal,
372 SessionStorageCache& aCache) {
373 AssertIsOnMainThread();
374 MOZ_ASSERT(mActor);
376 nsAutoCString originKey;
377 nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
378 if (NS_WARN_IF(NS_FAILED(rv))) {
379 return rv;
382 nsAutoCString originAttributes;
383 aPrincipal.OriginAttributesRef().CreateSuffix(originAttributes);
385 auto* const originRecord =
386 GetOriginRecord(originAttributes, originKey, true, nullptr);
387 MOZ_ASSERT(originRecord);
389 if (originRecord->mLoaded) {
390 return NS_OK;
393 RefPtr<SessionStorageCacheChild> cacheActor =
394 EnsureCache(aPrincipal, originKey, aCache);
396 if (!cacheActor) {
397 return NS_ERROR_FAILURE;
400 nsTArray<SSSetItemInfo> data;
401 if (!cacheActor->SendLoad(&data)) {
402 return NS_ERROR_FAILURE;
405 originRecord->mCache->DeserializeData(data);
407 originRecord->mLoaded.Flip();
408 aCache.SetLoadedOrCloned();
410 return NS_OK;
413 void SessionStorageManager::CheckpointData(nsIPrincipal& aPrincipal,
414 SessionStorageCache& aCache) {
415 AssertIsOnMainThread();
416 MOZ_ASSERT(mActor);
418 nsAutoCString originKey;
419 nsresult rv = aPrincipal.GetStorageOriginKey(originKey);
420 if (NS_WARN_IF(NS_FAILED(rv))) {
421 return;
424 return CheckpointDataInternal(aPrincipal, originKey, aCache);
427 void SessionStorageManager::CheckpointDataInternal(
428 nsIPrincipal& aPrincipal, const nsACString& aOriginKey,
429 SessionStorageCache& aCache) {
430 AssertIsOnMainThread();
431 MOZ_ASSERT(mActor);
433 nsTArray<SSWriteInfo> writeInfos = aCache.SerializeWriteInfos();
435 if (writeInfos.IsEmpty()) {
436 return;
439 RefPtr<SessionStorageCacheChild> cacheActor =
440 EnsureCache(aPrincipal, aOriginKey, aCache);
442 if (!cacheActor) {
443 return;
446 Unused << cacheActor->SendCheckpoint(writeInfos);
448 aCache.ResetWriteInfos();
451 nsresult SessionStorageManager::ClearStoragesForOrigin(
452 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
453 AssertIsOnMainThread();
455 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
457 return NS_OK;
460 NS_IMETHODIMP
461 SessionStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
462 nsIPrincipal* aStoragePrincipal,
463 Storage** aRetval) {
464 // Nothing to preload.
465 return NS_OK;
468 NS_IMETHODIMP
469 SessionStorageManager::GetSessionStorageCache(
470 nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
471 RefPtr<SessionStorageCache>* aRetVal) {
472 return GetSessionStorageCacheHelper(aStoragePrincipal, true, nullptr,
473 aRetVal);
476 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
477 nsIPrincipal* aPrincipal, bool aMakeIfNeeded,
478 SessionStorageCache* aCloneFrom, RefPtr<SessionStorageCache>* aRetVal) {
479 nsAutoCString originKey;
480 nsAutoCString originAttributes;
481 nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
482 aPrincipal->OriginAttributesRef().CreateSuffix(originAttributes);
483 if (NS_FAILED(rv)) {
484 return NS_ERROR_NOT_AVAILABLE;
487 return GetSessionStorageCacheHelper(originAttributes, originKey,
488 aMakeIfNeeded, aCloneFrom, aRetVal);
491 nsresult SessionStorageManager::GetSessionStorageCacheHelper(
492 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
493 bool aMakeIfNeeded, SessionStorageCache* aCloneFrom,
494 RefPtr<SessionStorageCache>* aRetVal) {
495 if (OriginRecord* const originRecord = GetOriginRecord(
496 aOriginAttrs, aOriginKey, aMakeIfNeeded, aCloneFrom)) {
497 *aRetVal = originRecord->mCache;
498 } else {
499 *aRetVal = nullptr;
501 return NS_OK;
504 NS_IMETHODIMP
505 SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow,
506 nsIPrincipal* aPrincipal,
507 nsIPrincipal* aStoragePrincipal,
508 const nsAString& aDocumentURI,
509 bool aPrivate, Storage** aRetval) {
510 RefPtr<SessionStorageCache> cache;
511 nsresult rv = GetSessionStorageCache(aPrincipal, aStoragePrincipal, &cache);
512 if (NS_FAILED(rv)) {
513 return rv;
516 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
518 RefPtr<SessionStorage> storage =
519 new SessionStorage(inner, aPrincipal, aStoragePrincipal, cache, this,
520 aDocumentURI, aPrivate);
522 storage.forget(aRetval);
523 return NS_OK;
526 NS_IMETHODIMP
527 SessionStorageManager::GetStorage(mozIDOMWindow* aWindow,
528 nsIPrincipal* aPrincipal,
529 nsIPrincipal* aStoragePrincipal,
530 bool aPrivate, Storage** aRetval) {
531 *aRetval = nullptr;
533 RefPtr<SessionStorageCache> cache;
534 nsresult rv =
535 GetSessionStorageCacheHelper(aStoragePrincipal, false, nullptr, &cache);
536 if (NS_FAILED(rv) || !cache) {
537 return rv;
540 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
542 RefPtr<SessionStorage> storage = new SessionStorage(
543 inner, aPrincipal, aStoragePrincipal, cache, this, u""_ns, aPrivate);
545 storage.forget(aRetval);
546 return NS_OK;
549 NS_IMETHODIMP
550 SessionStorageManager::CloneStorage(Storage* aStorage) {
551 if (NS_WARN_IF(!aStorage)) {
552 return NS_ERROR_UNEXPECTED;
555 if (aStorage->Type() != Storage::eSessionStorage) {
556 return NS_ERROR_UNEXPECTED;
559 // ToDo: At the moment, we clone the cache on the child process and then
560 // send the checkpoint. It would be nicer if we either serailizing all the
561 // data and sync to the parent process directly or clonig storage on the
562 // parnet process and sync it to the child process on demand.
564 RefPtr<SessionStorageCache> cache;
565 nsresult rv = GetSessionStorageCacheHelper(
566 aStorage->StoragePrincipal(), true,
567 static_cast<SessionStorage*>(aStorage)->Cache(), &cache);
568 if (NS_WARN_IF(NS_FAILED(rv))) {
569 return rv;
572 // If cache was cloned from other storage, then we shouldn't load the cache
573 // at the first access.
574 cache->SetLoadedOrCloned();
576 if (CanLoadData()) {
577 rv = EnsureManager();
578 if (NS_WARN_IF(NS_FAILED(rv))) {
579 return rv;
582 CheckpointData(*aStorage->StoragePrincipal(), *cache);
585 return rv;
588 NS_IMETHODIMP
589 SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
590 bool* aRetval) {
591 if (NS_WARN_IF(!aStorage)) {
592 return NS_ERROR_UNEXPECTED;
595 if (!aPrincipal) {
596 return NS_ERROR_NOT_AVAILABLE;
599 *aRetval = false;
601 RefPtr<SessionStorageCache> cache;
602 nsresult rv =
603 GetSessionStorageCacheHelper(aPrincipal, false, nullptr, &cache);
604 if (NS_FAILED(rv) || !cache) {
605 return rv;
608 if (aStorage->Type() != Storage::eSessionStorage) {
609 return NS_OK;
612 RefPtr<SessionStorage> sessionStorage =
613 static_cast<SessionStorage*>(aStorage);
614 if (sessionStorage->Cache() != cache) {
615 return NS_OK;
618 if (!StorageUtils::PrincipalsEqual(aStorage->StoragePrincipal(),
619 aPrincipal)) {
620 return NS_OK;
623 *aRetval = true;
624 return NS_OK;
627 void SessionStorageManager::ClearStorages(
628 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
629 if (CanLoadData()) {
630 nsresult rv = EnsureManager();
631 if (NS_WARN_IF(NS_FAILED(rv))) {
632 return;
635 mActor->SendClearStorages(aPattern, nsCString(aOriginScope));
638 ClearStoragesInternal(aPattern, aOriginScope);
641 nsresult SessionStorageManager::Observe(
642 const char* aTopic, const nsAString& aOriginAttributesPattern,
643 const nsACString& aOriginScope) {
644 OriginAttributesPattern pattern;
645 if (!pattern.Init(aOriginAttributesPattern)) {
646 NS_ERROR("Cannot parse origin attributes pattern");
647 return NS_ERROR_FAILURE;
650 // Clear everything, caches + database
651 if (!strcmp(aTopic, "cookie-cleared")) {
652 ClearStorages(pattern, ""_ns);
653 return NS_OK;
656 // Clear from caches everything that has been stored
657 // while in session-only mode
658 if (!strcmp(aTopic, "session-only-cleared")) {
659 ClearStorages(pattern, aOriginScope);
660 return NS_OK;
663 // Clear everything (including so and pb data) from caches and database
664 // for the given domain and subdomains.
665 if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
666 ClearStorages(pattern, aOriginScope);
667 return NS_OK;
670 // Clear entries which match an OriginAttributesPattern.
671 if (!strcmp(aTopic, "dom-storage:clear-origin-attributes-data") ||
672 !strcmp(aTopic, "session-storage:clear-origin-attributes-data")) {
673 ClearStorages(pattern, aOriginScope);
674 return NS_OK;
677 if (!strcmp(aTopic, "profile-change")) {
678 // For case caches are still referenced - clear them completely
679 ClearStorages(pattern, ""_ns);
680 mOATable.Clear();
681 return NS_OK;
684 return NS_OK;
687 SessionStorageManager::OriginRecord::~OriginRecord() = default;
689 // static
690 void BackgroundSessionStorageManager::RemoveManager(uint64_t aTopContextId) {
691 MOZ_ASSERT(XRE_IsParentProcess());
692 AssertIsOnMainThread();
694 ::mozilla::ipc::PBackgroundChild* backgroundActor =
695 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
696 if (NS_WARN_IF(!backgroundActor)) {
697 return;
700 if (NS_WARN_IF(!backgroundActor->SendRemoveBackgroundSessionStorageManager(
701 aTopContextId))) {
702 return;
706 // static
707 void BackgroundSessionStorageManager::PropagateManager(
708 uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) {
709 MOZ_ASSERT(XRE_IsParentProcess());
710 AssertIsOnMainThread();
711 MOZ_ASSERT(aCurrentTopContextId != aTargetTopContextId);
713 ::mozilla::ipc::PBackgroundChild* backgroundActor =
714 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
715 if (NS_WARN_IF(!backgroundActor)) {
716 return;
719 if (NS_WARN_IF(!backgroundActor->SendPropagateBackgroundSessionStorageManager(
720 aCurrentTopContextId, aTargetTopContextId))) {
721 return;
725 // static
726 void BackgroundSessionStorageManager::LoadData(
727 uint64_t aTopContextId,
728 const nsTArray<mozilla::dom::SSCacheCopy>& aCacheCopyList) {
729 MOZ_ASSERT(XRE_IsParentProcess());
730 AssertIsOnMainThread();
732 ::mozilla::ipc::PBackgroundChild* backgroundActor =
733 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
734 if (NS_WARN_IF(!backgroundActor)) {
735 return;
738 if (NS_WARN_IF(!backgroundActor->SendLoadSessionStorageManagerData(
739 aTopContextId, aCacheCopyList))) {
740 return;
744 // static
745 BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate(
746 uint64_t aTopContextId) {
747 MOZ_ASSERT(XRE_IsParentProcess());
748 ::mozilla::ipc::AssertIsOnBackgroundThread();
750 if (!sManagers) {
751 sManagers = new nsRefPtrHashtable<nsUint64HashKey,
752 BackgroundSessionStorageManager>();
753 NS_DispatchToMainThread(NS_NewRunnableFunction(
754 "dom::BackgroundSessionStorageManager::GetOrCreate", [] {
755 RunOnShutdown(
756 [] {
757 ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::
758 ipc::BackgroundChild::GetOrCreateForCurrentThread();
759 if (NS_WARN_IF(!backgroundActor)) {
760 return;
763 if (NS_WARN_IF(
764 !backgroundActor
765 ->SendShutdownBackgroundSessionStorageManagers())) {
766 return;
769 ShutdownPhase::XPCOMShutdown);
770 }));
773 return sManagers
774 ->LookupOrInsertWith(
775 aTopContextId,
776 [aTopContextId] {
777 return new BackgroundSessionStorageManager(aTopContextId);
779 .get();
782 BackgroundSessionStorageManager::BackgroundSessionStorageManager(
783 uint64_t aBrowsingContextId)
784 : mCurrentBrowsingContextId(aBrowsingContextId) {
785 MOZ_ASSERT(XRE_IsParentProcess());
786 ::mozilla::ipc::AssertIsOnBackgroundThread();
789 BackgroundSessionStorageManager::~BackgroundSessionStorageManager() = default;
791 void BackgroundSessionStorageManager::CopyDataToContentProcess(
792 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
793 nsTArray<SSSetItemInfo>& aData) {
794 MOZ_ASSERT(XRE_IsParentProcess());
795 ::mozilla::ipc::AssertIsOnBackgroundThread();
797 auto* const originRecord =
798 GetOriginRecord(aOriginAttrs, aOriginKey, false, nullptr);
799 if (!originRecord) {
800 return;
803 aData = originRecord->mCache->SerializeData();
806 /* static */
807 RefPtr<BackgroundSessionStorageManager::DataPromise>
808 BackgroundSessionStorageManager::GetData(BrowsingContext* aContext,
809 uint32_t aSizeLimit,
810 bool aClearSessionStoreTimer) {
811 MOZ_ASSERT(XRE_IsParentProcess());
812 MOZ_ASSERT(aContext->IsTop());
814 AssertIsOnMainThread();
816 ::mozilla::ipc::PBackgroundChild* backgroundActor =
817 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
818 if (NS_WARN_IF(!backgroundActor)) {
819 return DataPromise::CreateAndReject(
820 ::mozilla::ipc::ResponseRejectReason::SendError, __func__);
823 return backgroundActor->SendGetSessionStorageManagerData(
824 aContext->Id(), aSizeLimit, aClearSessionStoreTimer);
827 void BackgroundSessionStorageManager::GetData(
828 uint32_t aSizeLimit, nsTArray<SSCacheCopy>& aCacheCopyList) {
829 for (auto& managerActor : mParticipatingActors) {
830 for (auto* cacheActor :
831 managerActor->ManagedPBackgroundSessionStorageCacheParent()) {
832 auto* cache = static_cast<SessionStorageCacheParent*>(cacheActor);
833 ::mozilla::ipc::PrincipalInfo info = cache->PrincipalInfo();
835 OriginAttributes attributes;
836 StoragePrincipalHelper::GetOriginAttributes(cache->PrincipalInfo(),
837 attributes);
839 nsAutoCString originAttrs;
840 attributes.CreateSuffix(originAttrs);
842 auto* record =
843 GetOriginRecord(originAttrs, cache->OriginKey(), false, nullptr);
845 if (!record) {
846 continue;
849 if (record->mCache->GetOriginQuotaUsage() > aSizeLimit) {
850 continue;
853 nsTArray<SSSetItemInfo> data = record->mCache->SerializeData();
854 if (data.IsEmpty()) {
855 continue;
858 SSCacheCopy& cacheCopy = *aCacheCopyList.AppendElement();
859 cacheCopy.originKey() = cache->OriginKey();
860 cacheCopy.principalInfo() = info;
861 cacheCopy.data().SwapElements(data);
866 void BackgroundSessionStorageManager::UpdateData(
867 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
868 const nsTArray<SSWriteInfo>& aWriteInfos) {
869 MOZ_ASSERT(XRE_IsParentProcess());
870 ::mozilla::ipc::AssertIsOnBackgroundThread();
872 auto* const originRecord =
873 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
874 MOZ_ASSERT(originRecord);
876 MaybeScheduleSessionStoreUpdate();
878 originRecord->mCache->DeserializeWriteInfos(aWriteInfos);
881 void BackgroundSessionStorageManager::UpdateData(
882 const nsACString& aOriginAttrs, const nsACString& aOriginKey,
883 const nsTArray<SSSetItemInfo>& aData) {
884 MOZ_ASSERT(XRE_IsParentProcess());
885 ::mozilla::ipc::AssertIsOnBackgroundThread();
887 auto* const originRecord =
888 GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr);
889 MOZ_ASSERT(originRecord);
891 originRecord->mCache->DeserializeData(aData);
894 void BackgroundSessionStorageManager::ClearStorages(
895 const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) {
896 MOZ_ASSERT(XRE_IsParentProcess());
897 ::mozilla::ipc::AssertIsOnBackgroundThread();
898 ClearStoragesInternal(aPattern, aOriginScope);
901 void BackgroundSessionStorageManager::ClearStoragesForOrigin(
902 const nsACString& aOriginAttrs, const nsACString& aOriginKey) {
903 ::mozilla::ipc::AssertIsInMainProcess();
904 ::mozilla::ipc::AssertIsOnBackgroundThread();
906 for (auto& managerActor : mParticipatingActors) {
907 QM_WARNONLY_TRY(OkIf(managerActor->SendClearStoragesForOrigin(
908 nsCString(aOriginAttrs), nsCString(aOriginKey))));
911 ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey);
914 void BackgroundSessionStorageManager::SetCurrentBrowsingContextId(
915 uint64_t aBrowsingContextId) {
916 MOZ_DIAGNOSTIC_ASSERT(aBrowsingContextId != mCurrentBrowsingContextId);
917 mCurrentBrowsingContextId = aBrowsingContextId;
920 void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() {
921 if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
922 return;
925 if (mSessionStoreCallbackTimer) {
926 return;
929 if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
930 DispatchSessionStoreUpdate();
931 return;
934 auto result = NS_NewTimerWithFuncCallback(
935 [](nsITimer*, void* aClosure) {
936 auto* mgr = static_cast<BackgroundSessionStorageManager*>(aClosure);
937 mgr->DispatchSessionStoreUpdate();
939 this, StaticPrefs::browser_sessionstore_interval(),
940 nsITimer::TYPE_ONE_SHOT,
941 "BackgroundSessionStorageManager::DispatchSessionStoreUpdate");
943 if (result.isErr()) {
944 return;
947 mSessionStoreCallbackTimer = result.unwrap();
950 void BackgroundSessionStorageManager::MaybeDispatchSessionStoreUpdate() {
951 if (mSessionStoreCallbackTimer) {
952 BackgroundSessionStorageManager::DispatchSessionStoreUpdate();
956 void BackgroundSessionStorageManager::DispatchSessionStoreUpdate() {
957 ::mozilla::ipc::AssertIsOnBackgroundThread();
958 NS_DispatchToMainThread(NS_NewRunnableFunction(
959 "CanonicalBrowsingContext::UpdateSessionStore",
960 [targetBrowsingContextId = mCurrentBrowsingContextId]() {
961 CanonicalBrowsingContext::UpdateSessionStoreForStorage(
962 targetBrowsingContextId);
963 }));
965 CancelSessionStoreUpdate();
968 void BackgroundSessionStorageManager::CancelSessionStoreUpdate() {
969 if (mSessionStoreCallbackTimer) {
970 mSessionStoreCallbackTimer->Cancel();
971 mSessionStoreCallbackTimer = nullptr;
975 void BackgroundSessionStorageManager::AddParticipatingActor(
976 SessionStorageManagerParent* aActor) {
977 ::mozilla::ipc::AssertIsOnBackgroundThread();
978 mParticipatingActors.AppendElement(aActor);
981 void BackgroundSessionStorageManager::RemoveParticipatingActor(
982 SessionStorageManagerParent* aActor) {
983 ::mozilla::ipc::AssertIsOnBackgroundThread();
984 mParticipatingActors.RemoveElement(aActor);
987 } // namespace mozilla::dom