/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SessionStorageManager.h" #include "StorageIPC.h" #include "SessionStorage.h" #include "SessionStorageCache.h" #include "SessionStorageObserver.h" #include "StorageUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/OriginAttributes.h" #include "mozilla/PrincipalHashKey.h" #include "mozilla/ScopeExit.h" #include "mozilla/StoragePrincipalHelper.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/LocalStorageCommon.h" #include "mozilla/dom/PBackgroundSessionStorageCache.h" #include "mozilla/dom/PBackgroundSessionStorageManager.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/PBackgroundChild.h" #include "nsTHashMap.h" #include "nsThreadUtils.h" namespace mozilla::dom { using namespace StorageUtils; // Parent process, background thread hashmap that stores top context id and // manager pair. static StaticAutoPtr< nsRefPtrHashtable> sManagers; bool RecvShutdownBackgroundSessionStorageManagers() { ::mozilla::ipc::AssertIsOnBackgroundThread(); sManagers = nullptr; return true; } void RecvPropagateBackgroundSessionStorageManager( uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) { ::mozilla::ipc::AssertIsOnBackgroundThread(); if (sManagers) { if (RefPtr mgr = sManagers->Get(aCurrentTopContextId)) { mgr->MaybeDispatchSessionStoreUpdate(); mgr->SetCurrentBrowsingContextId(aTargetTopContextId); // Because of bfcache, we may re-register aTargetTopContextId in // CanonicalBrowsingContext::ReplacedBy. // XXXBFCache do we want to tweak this behavior and ensure this is // called only once? sManagers->InsertOrUpdate(aTargetTopContextId, std::move(mgr)); } } } bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId) { ::mozilla::ipc::AssertIsOnBackgroundThread(); if (sManagers) { RefPtr mgr; sManagers->Remove(aTopContextId, getter_AddRefs(mgr)); if (mgr) { mgr->CancelSessionStoreUpdate(); } } return true; } bool RecvLoadSessionStorageData( uint64_t aTopContextId, nsTArray&& aCacheCopyList) { if (aCacheCopyList.IsEmpty()) { return true; } RefPtr manager = BackgroundSessionStorageManager::GetOrCreate(aTopContextId); if (!manager) { return true; } for (const auto& cacheInit : aCacheCopyList) { OriginAttributes attrs; StoragePrincipalHelper::GetOriginAttributes(cacheInit.principalInfo(), attrs); nsAutoCString originAttrs; attrs.CreateSuffix(originAttrs); manager->UpdateData(originAttrs, cacheInit.originKey(), cacheInit.data()); } return true; } bool RecvGetSessionStorageData( uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer, ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&& aResolver) { nsTArray data; auto resolve = MakeScopeExit([&]() { aResolver(std::move(data)); }); if (!sManagers) { return true; } RefPtr manager = sManagers->Get(aTopContextId); if (!manager) { return true; } if (aCancelSessionStoreTimer) { manager->CancelSessionStoreUpdate(); } manager->GetData(aSizeLimit, data); return true; } bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs, const nsACString& aOriginKey) { mozilla::ipc::AssertIsInMainProcess(); mozilla::ipc::AssertIsOnBackgroundThread(); if (!sManagers) { return true; } for (auto& entry : *sManagers) { entry.GetData()->ClearStoragesForOrigin(aOriginAttrs, aOriginKey); } return true; } void SessionStorageManagerBase::ClearStoragesInternal( const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { for (const auto& oaEntry : mOATable) { OriginAttributes oa; DebugOnly ok = oa.PopulateFromSuffix(oaEntry.GetKey()); MOZ_ASSERT(ok); if (!aPattern.Matches(oa)) { // This table doesn't match the given origin attributes pattern continue; } OriginKeyHashTable* table = oaEntry.GetWeak(); for (const auto& originKeyEntry : *table) { if (aOriginScope.IsEmpty() || StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) { const auto cache = originKeyEntry.GetData()->mCache; cache->Clear(false); cache->ResetWriteInfos(); } } } } void SessionStorageManagerBase::ClearStoragesForOriginInternal( const nsACString& aOriginAttrs, const nsACString& aOriginKey) { for (const auto& oaEntry : mOATable) { // Filter tables which match the given origin attrs. if (oaEntry.GetKey() != aOriginAttrs) { continue; } OriginKeyHashTable* table = oaEntry.GetWeak(); for (const auto& originKeyEntry : *table) { // Match exact origin (without origin attrs). if (originKeyEntry.GetKey() != aOriginKey) { continue; } const auto cache = originKeyEntry.GetData()->mCache; cache->Clear(false); cache->ResetWriteInfos(); } } } SessionStorageManagerBase::OriginRecord* SessionStorageManagerBase::GetOriginRecord( const nsACString& aOriginAttrs, const nsACString& aOriginKey, const bool aMakeIfNeeded, SessionStorageCache* const aCloneFrom) { // XXX It seems aMakeIfNeeded is always known at compile-time, so this could // be split into two functions. if (aMakeIfNeeded) { return mOATable.GetOrInsertNew(aOriginAttrs) ->LookupOrInsertWith( aOriginKey, [&] { auto newOriginRecord = MakeUnique(); if (aCloneFrom) { newOriginRecord->mCache = aCloneFrom->Clone(); } else { newOriginRecord->mCache = new SessionStorageCache(); } return newOriginRecord; }) .get(); } auto* const table = mOATable.Get(aOriginAttrs); if (!table) return nullptr; return table->Get(aOriginKey); } NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorageManager) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager) NS_INTERFACE_MAP_ENTRY(nsIDOMSessionStorageManager) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION(SessionStorageManager, mBrowsingContext) NS_IMPL_CYCLE_COLLECTING_ADDREF(SessionStorageManager) NS_IMPL_CYCLE_COLLECTING_RELEASE(SessionStorageManager) SessionStorageManager::SessionStorageManager( RefPtr aBrowsingContext) : mBrowsingContext(std::move(aBrowsingContext)), mActor(nullptr) { AssertIsOnMainThread(); StorageObserver* observer = StorageObserver::Self(); NS_ASSERTION( observer, "No StorageObserver, cannot observe private data delete notifications!"); if (observer) { observer->AddSink(this); } if (!XRE_IsParentProcess() && NextGenLocalStorageEnabled()) { // When LSNG is enabled the thread IPC bridge doesn't exist, so we have to // create own protocol to distribute chrome observer notifications to // content processes. mObserver = SessionStorageObserver::Get(); if (!mObserver) { ContentChild* contentActor = ContentChild::GetSingleton(); MOZ_ASSERT(contentActor); RefPtr observer = new SessionStorageObserver(); SessionStorageObserverChild* actor = new SessionStorageObserverChild(observer); MOZ_ALWAYS_TRUE( contentActor->SendPSessionStorageObserverConstructor(actor)); observer->SetActor(actor); mObserver = std::move(observer); } } } SessionStorageManager::~SessionStorageManager() { StorageObserver* observer = StorageObserver::Self(); if (observer) { observer->RemoveSink(this); } if (mActor) { mActor->SendDeleteMeInternal(); MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!"); } } bool SessionStorageManager::CanLoadData() { AssertIsOnMainThread(); return mBrowsingContext && !mBrowsingContext->IsDiscarded(); } void SessionStorageManager::SetActor(SessionStorageManagerChild* aActor) { AssertIsOnMainThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(!mActor); mActor = aActor; } bool SessionStorageManager::ActorExists() const { AssertIsOnMainThread(); return mActor; } void SessionStorageManager::ClearActor() { AssertIsOnMainThread(); MOZ_ASSERT(mActor); mActor = nullptr; } nsresult SessionStorageManager::EnsureManager() { AssertIsOnMainThread(); MOZ_ASSERT(CanLoadData()); if (ActorExists()) { return NS_OK; } ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return NS_ERROR_FAILURE; } RefPtr actor = new SessionStorageManagerChild(this); if (!backgroundActor->SendPBackgroundSessionStorageManagerConstructor( actor, mBrowsingContext->Top()->Id())) { return NS_ERROR_FAILURE; } SetActor(actor); return NS_OK; } SessionStorageCacheChild* SessionStorageManager::EnsureCache( nsIPrincipal& aPrincipal, const nsACString& aOriginKey, SessionStorageCache& aCache) { AssertIsOnMainThread(); MOZ_ASSERT(CanLoadData()); MOZ_ASSERT(ActorExists()); if (aCache.Actor()) { return aCache.Actor(); } mozilla::ipc::PrincipalInfo info; nsresult rv = PrincipalToPrincipalInfo(&aPrincipal, &info); if (NS_FAILED(rv)) { return nullptr; } RefPtr actor = new SessionStorageCacheChild(&aCache); if (!mActor->SendPBackgroundSessionStorageCacheConstructor(actor, info, aOriginKey)) { return nullptr; } aCache.SetActor(actor); return actor; } nsresult SessionStorageManager::LoadData(nsIPrincipal& aPrincipal, SessionStorageCache& aCache) { AssertIsOnMainThread(); MOZ_ASSERT(mActor); nsAutoCString originKey; nsresult rv = aPrincipal.GetStorageOriginKey(originKey); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsAutoCString originAttributes; aPrincipal.OriginAttributesRef().CreateSuffix(originAttributes); auto* const originRecord = GetOriginRecord(originAttributes, originKey, true, nullptr); MOZ_ASSERT(originRecord); if (originRecord->mLoaded) { return NS_OK; } RefPtr cacheActor = EnsureCache(aPrincipal, originKey, aCache); if (!cacheActor) { return NS_ERROR_FAILURE; } nsTArray data; if (!cacheActor->SendLoad(&data)) { return NS_ERROR_FAILURE; } originRecord->mCache->DeserializeData(data); originRecord->mLoaded.Flip(); aCache.SetLoadedOrCloned(); return NS_OK; } void SessionStorageManager::CheckpointData(nsIPrincipal& aPrincipal, SessionStorageCache& aCache) { AssertIsOnMainThread(); MOZ_ASSERT(mActor); nsAutoCString originKey; nsresult rv = aPrincipal.GetStorageOriginKey(originKey); if (NS_WARN_IF(NS_FAILED(rv))) { return; } return CheckpointDataInternal(aPrincipal, originKey, aCache); } void SessionStorageManager::CheckpointDataInternal( nsIPrincipal& aPrincipal, const nsACString& aOriginKey, SessionStorageCache& aCache) { AssertIsOnMainThread(); MOZ_ASSERT(mActor); nsTArray writeInfos = aCache.SerializeWriteInfos(); if (writeInfos.IsEmpty()) { return; } RefPtr cacheActor = EnsureCache(aPrincipal, aOriginKey, aCache); if (!cacheActor) { return; } Unused << cacheActor->SendCheckpoint(writeInfos); aCache.ResetWriteInfos(); } nsresult SessionStorageManager::ClearStoragesForOrigin( const nsACString& aOriginAttrs, const nsACString& aOriginKey) { AssertIsOnMainThread(); ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey); return NS_OK; } NS_IMETHODIMP SessionStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal, Storage** aRetval) { // Nothing to preload. return NS_OK; } NS_IMETHODIMP SessionStorageManager::GetSessionStorageCache( nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal, RefPtr* aRetVal) { return GetSessionStorageCacheHelper(aStoragePrincipal, true, nullptr, aRetVal); } nsresult SessionStorageManager::GetSessionStorageCacheHelper( nsIPrincipal* aPrincipal, bool aMakeIfNeeded, SessionStorageCache* aCloneFrom, RefPtr* aRetVal) { nsAutoCString originKey; nsAutoCString originAttributes; nsresult rv = aPrincipal->GetStorageOriginKey(originKey); aPrincipal->OriginAttributesRef().CreateSuffix(originAttributes); if (NS_FAILED(rv)) { return NS_ERROR_NOT_AVAILABLE; } return GetSessionStorageCacheHelper(originAttributes, originKey, aMakeIfNeeded, aCloneFrom, aRetVal); } nsresult SessionStorageManager::GetSessionStorageCacheHelper( const nsACString& aOriginAttrs, const nsACString& aOriginKey, bool aMakeIfNeeded, SessionStorageCache* aCloneFrom, RefPtr* aRetVal) { if (OriginRecord* const originRecord = GetOriginRecord( aOriginAttrs, aOriginKey, aMakeIfNeeded, aCloneFrom)) { *aRetVal = originRecord->mCache; } else { *aRetVal = nullptr; } return NS_OK; } NS_IMETHODIMP SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal, const nsAString& aDocumentURI, bool aPrivate, Storage** aRetval) { RefPtr cache; nsresult rv = GetSessionStorageCache(aPrincipal, aStoragePrincipal, &cache); if (NS_FAILED(rv)) { return rv; } nsCOMPtr inner = nsPIDOMWindowInner::From(aWindow); RefPtr storage = new SessionStorage(inner, aPrincipal, aStoragePrincipal, cache, this, aDocumentURI, aPrivate); storage.forget(aRetval); return NS_OK; } NS_IMETHODIMP SessionStorageManager::GetStorage(mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal, bool aPrivate, Storage** aRetval) { *aRetval = nullptr; RefPtr cache; nsresult rv = GetSessionStorageCacheHelper(aStoragePrincipal, false, nullptr, &cache); if (NS_FAILED(rv) || !cache) { return rv; } nsCOMPtr inner = nsPIDOMWindowInner::From(aWindow); RefPtr storage = new SessionStorage( inner, aPrincipal, aStoragePrincipal, cache, this, u""_ns, aPrivate); storage.forget(aRetval); return NS_OK; } NS_IMETHODIMP SessionStorageManager::CloneStorage(Storage* aStorage) { if (NS_WARN_IF(!aStorage)) { return NS_ERROR_UNEXPECTED; } if (aStorage->Type() != Storage::eSessionStorage) { return NS_ERROR_UNEXPECTED; } // ToDo: At the moment, we clone the cache on the child process and then // send the checkpoint. It would be nicer if we either serailizing all the // data and sync to the parent process directly or clonig storage on the // parnet process and sync it to the child process on demand. RefPtr cache; nsresult rv = GetSessionStorageCacheHelper( aStorage->StoragePrincipal(), true, static_cast(aStorage)->Cache(), &cache); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // If cache was cloned from other storage, then we shouldn't load the cache // at the first access. cache->SetLoadedOrCloned(); if (CanLoadData()) { rv = EnsureManager(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } CheckpointData(*aStorage->StoragePrincipal(), *cache); } return rv; } NS_IMETHODIMP SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage, bool* aRetval) { if (NS_WARN_IF(!aStorage)) { return NS_ERROR_UNEXPECTED; } if (!aPrincipal) { return NS_ERROR_NOT_AVAILABLE; } *aRetval = false; RefPtr cache; nsresult rv = GetSessionStorageCacheHelper(aPrincipal, false, nullptr, &cache); if (NS_FAILED(rv) || !cache) { return rv; } if (aStorage->Type() != Storage::eSessionStorage) { return NS_OK; } RefPtr sessionStorage = static_cast(aStorage); if (sessionStorage->Cache() != cache) { return NS_OK; } if (!StorageUtils::PrincipalsEqual(aStorage->StoragePrincipal(), aPrincipal)) { return NS_OK; } *aRetval = true; return NS_OK; } void SessionStorageManager::ClearStorages( const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { if (CanLoadData()) { nsresult rv = EnsureManager(); if (NS_WARN_IF(NS_FAILED(rv))) { return; } mActor->SendClearStorages(aPattern, nsCString(aOriginScope)); } ClearStoragesInternal(aPattern, aOriginScope); } nsresult SessionStorageManager::Observe( const char* aTopic, const nsAString& aOriginAttributesPattern, const nsACString& aOriginScope) { OriginAttributesPattern pattern; if (!pattern.Init(aOriginAttributesPattern)) { NS_ERROR("Cannot parse origin attributes pattern"); return NS_ERROR_FAILURE; } // Clear everything, caches + database if (!strcmp(aTopic, "cookie-cleared")) { ClearStorages(pattern, ""_ns); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "session-only-cleared")) { ClearStorages(pattern, aOriginScope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the given domain and subdomains. if (!strcmp(aTopic, "browser:purge-sessionStorage")) { ClearStorages(pattern, aOriginScope); return NS_OK; } // Clear entries which match an OriginAttributesPattern. if (!strcmp(aTopic, "dom-storage:clear-origin-attributes-data") || !strcmp(aTopic, "session-storage:clear-origin-attributes-data")) { ClearStorages(pattern, aOriginScope); return NS_OK; } if (!strcmp(aTopic, "profile-change")) { // For case caches are still referenced - clear them completely ClearStorages(pattern, ""_ns); mOATable.Clear(); return NS_OK; } return NS_OK; } SessionStorageManager::OriginRecord::~OriginRecord() = default; // static void BackgroundSessionStorageManager::RemoveManager(uint64_t aTopContextId) { MOZ_ASSERT(XRE_IsParentProcess()); AssertIsOnMainThread(); ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return; } if (NS_WARN_IF(!backgroundActor->SendRemoveBackgroundSessionStorageManager( aTopContextId))) { return; } } // static void BackgroundSessionStorageManager::PropagateManager( uint64_t aCurrentTopContextId, uint64_t aTargetTopContextId) { MOZ_ASSERT(XRE_IsParentProcess()); AssertIsOnMainThread(); MOZ_ASSERT(aCurrentTopContextId != aTargetTopContextId); ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return; } if (NS_WARN_IF(!backgroundActor->SendPropagateBackgroundSessionStorageManager( aCurrentTopContextId, aTargetTopContextId))) { return; } } // static void BackgroundSessionStorageManager::LoadData( uint64_t aTopContextId, const nsTArray& aCacheCopyList) { MOZ_ASSERT(XRE_IsParentProcess()); AssertIsOnMainThread(); ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return; } if (NS_WARN_IF(!backgroundActor->SendLoadSessionStorageManagerData( aTopContextId, aCacheCopyList))) { return; } } // static BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate( uint64_t aTopContextId) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); if (!sManagers) { sManagers = new nsRefPtrHashtable(); NS_DispatchToMainThread(NS_NewRunnableFunction( "dom::BackgroundSessionStorageManager::GetOrCreate", [] { RunOnShutdown( [] { ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla:: ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return; } if (NS_WARN_IF( !backgroundActor ->SendShutdownBackgroundSessionStorageManagers())) { return; } }, ShutdownPhase::XPCOMShutdown); })); } return sManagers ->LookupOrInsertWith( aTopContextId, [aTopContextId] { return new BackgroundSessionStorageManager(aTopContextId); }) .get(); } BackgroundSessionStorageManager::BackgroundSessionStorageManager( uint64_t aBrowsingContextId) : mCurrentBrowsingContextId(aBrowsingContextId) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); } BackgroundSessionStorageManager::~BackgroundSessionStorageManager() = default; void BackgroundSessionStorageManager::CopyDataToContentProcess( const nsACString& aOriginAttrs, const nsACString& aOriginKey, nsTArray& aData) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); auto* const originRecord = GetOriginRecord(aOriginAttrs, aOriginKey, false, nullptr); if (!originRecord) { return; } aData = originRecord->mCache->SerializeData(); } /* static */ RefPtr BackgroundSessionStorageManager::GetData(BrowsingContext* aContext, uint32_t aSizeLimit, bool aClearSessionStoreTimer) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(aContext->IsTop()); AssertIsOnMainThread(); ::mozilla::ipc::PBackgroundChild* backgroundActor = ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!backgroundActor)) { return DataPromise::CreateAndReject( ::mozilla::ipc::ResponseRejectReason::SendError, __func__); } return backgroundActor->SendGetSessionStorageManagerData( aContext->Id(), aSizeLimit, aClearSessionStoreTimer); } void BackgroundSessionStorageManager::GetData( uint32_t aSizeLimit, nsTArray& aCacheCopyList) { for (auto& managerActor : mParticipatingActors) { for (auto* cacheActor : managerActor->ManagedPBackgroundSessionStorageCacheParent()) { auto* cache = static_cast(cacheActor); ::mozilla::ipc::PrincipalInfo info = cache->PrincipalInfo(); OriginAttributes attributes; StoragePrincipalHelper::GetOriginAttributes(cache->PrincipalInfo(), attributes); nsAutoCString originAttrs; attributes.CreateSuffix(originAttrs); auto* record = GetOriginRecord(originAttrs, cache->OriginKey(), false, nullptr); if (!record) { continue; } if (record->mCache->GetOriginQuotaUsage() > aSizeLimit) { continue; } nsTArray data = record->mCache->SerializeData(); if (data.IsEmpty()) { continue; } SSCacheCopy& cacheCopy = *aCacheCopyList.AppendElement(); cacheCopy.originKey() = cache->OriginKey(); cacheCopy.principalInfo() = info; cacheCopy.data().SwapElements(data); } } } void BackgroundSessionStorageManager::UpdateData( const nsACString& aOriginAttrs, const nsACString& aOriginKey, const nsTArray& aWriteInfos) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); auto* const originRecord = GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr); MOZ_ASSERT(originRecord); MaybeScheduleSessionStoreUpdate(); originRecord->mCache->DeserializeWriteInfos(aWriteInfos); } void BackgroundSessionStorageManager::UpdateData( const nsACString& aOriginAttrs, const nsACString& aOriginKey, const nsTArray& aData) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); auto* const originRecord = GetOriginRecord(aOriginAttrs, aOriginKey, true, nullptr); MOZ_ASSERT(originRecord); originRecord->mCache->DeserializeData(aData); } void BackgroundSessionStorageManager::ClearStorages( const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); ClearStoragesInternal(aPattern, aOriginScope); } void BackgroundSessionStorageManager::ClearStoragesForOrigin( const nsACString& aOriginAttrs, const nsACString& aOriginKey) { ::mozilla::ipc::AssertIsInMainProcess(); ::mozilla::ipc::AssertIsOnBackgroundThread(); for (auto& managerActor : mParticipatingActors) { QM_WARNONLY_TRY(OkIf(managerActor->SendClearStoragesForOrigin( nsCString(aOriginAttrs), nsCString(aOriginKey)))); } ClearStoragesForOriginInternal(aOriginAttrs, aOriginKey); } void BackgroundSessionStorageManager::SetCurrentBrowsingContextId( uint64_t aBrowsingContextId) { MOZ_DIAGNOSTIC_ASSERT(aBrowsingContextId != mCurrentBrowsingContextId); mCurrentBrowsingContextId = aBrowsingContextId; } void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() { if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) { return; } if (mSessionStoreCallbackTimer) { return; } if (StaticPrefs::browser_sessionstore_debug_no_auto_updates()) { DispatchSessionStoreUpdate(); return; } auto result = NS_NewTimerWithFuncCallback( [](nsITimer*, void* aClosure) { auto* mgr = static_cast(aClosure); mgr->DispatchSessionStoreUpdate(); }, this, StaticPrefs::browser_sessionstore_interval(), nsITimer::TYPE_ONE_SHOT, "BackgroundSessionStorageManager::DispatchSessionStoreUpdate"); if (result.isErr()) { return; } mSessionStoreCallbackTimer = result.unwrap(); } void BackgroundSessionStorageManager::MaybeDispatchSessionStoreUpdate() { if (mSessionStoreCallbackTimer) { BackgroundSessionStorageManager::DispatchSessionStoreUpdate(); } } void BackgroundSessionStorageManager::DispatchSessionStoreUpdate() { ::mozilla::ipc::AssertIsOnBackgroundThread(); NS_DispatchToMainThread(NS_NewRunnableFunction( "CanonicalBrowsingContext::UpdateSessionStore", [targetBrowsingContextId = mCurrentBrowsingContextId]() { CanonicalBrowsingContext::UpdateSessionStoreForStorage( targetBrowsingContextId); })); CancelSessionStoreUpdate(); } void BackgroundSessionStorageManager::CancelSessionStoreUpdate() { if (mSessionStoreCallbackTimer) { mSessionStoreCallbackTimer->Cancel(); mSessionStoreCallbackTimer = nullptr; } } void BackgroundSessionStorageManager::AddParticipatingActor( SessionStorageManagerParent* aActor) { ::mozilla::ipc::AssertIsOnBackgroundThread(); mParticipatingActors.AppendElement(aActor); } void BackgroundSessionStorageManager::RemoveParticipatingActor( SessionStorageManagerParent* aActor) { ::mozilla::ipc::AssertIsOnBackgroundThread(); mParticipatingActors.RemoveElement(aActor); } } // namespace mozilla::dom