diff options
Diffstat (limited to 'dom/storage/StorageIPC.cpp')
-rw-r--r-- | dom/storage/StorageIPC.cpp | 1598 |
1 files changed, 1598 insertions, 0 deletions
diff --git a/dom/storage/StorageIPC.cpp b/dom/storage/StorageIPC.cpp new file mode 100644 index 0000000000..811575f471 --- /dev/null +++ b/dom/storage/StorageIPC.cpp @@ -0,0 +1,1598 @@ +/* -*- 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 "StorageIPC.h" + +#include "StorageCommon.h" +#include "StorageUtils.h" +#include "LocalStorageManager.h" +#include "SessionStorageObserver.h" +#include "SessionStorageManager.h" +#include "SessionStorageCache.h" + +#include "mozilla/dom/LocalStorageCommon.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/PBackgroundParent.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/StoragePrincipalHelper.h" +#include "mozilla/Unused.h" +#include "nsCOMPtr.h" +#include "nsIPrincipal.h" +#include "nsThreadUtils.h" + +namespace mozilla::dom { + +namespace { + +using LocalStorageCacheParentHashtable = + nsClassHashtable<nsCStringHashKey, nsTArray<LocalStorageCacheParent*>>; + +StaticAutoPtr<LocalStorageCacheParentHashtable> gLocalStorageCacheParents; + +StorageDBChild* sStorageChild[kPrivateBrowsingIdCount] = {nullptr, nullptr}; + +// False until we shut the storage child down. +bool sStorageChildDown[kPrivateBrowsingIdCount] = {false, false}; + +} // namespace + +LocalStorageCacheChild::LocalStorageCacheChild(LocalStorageCache* aCache) + : mCache(aCache) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aCache); + aCache->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(LocalStorageCacheChild); +} + +LocalStorageCacheChild::~LocalStorageCacheChild() { + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(LocalStorageCacheChild); +} + +void LocalStorageCacheChild::SendDeleteMeInternal() { + AssertIsOnOwningThread(); + + if (mCache) { + mCache->ClearActor(); + mCache = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundLocalStorageCacheChild::SendDeleteMe()); + } +} + +void LocalStorageCacheChild::ActorDestroy(ActorDestroyReason aWhy) { + AssertIsOnOwningThread(); + + if (mCache) { + mCache->ClearActor(); + mCache = nullptr; + } +} + +mozilla::ipc::IPCResult LocalStorageCacheChild::RecvObserve( + const PrincipalInfo& aPrincipalInfo, + const PrincipalInfo& aCachePrincipalInfo, + const uint32_t& aPrivateBrowsingId, const nsAString& aDocumentURI, + const nsAString& aKey, const nsAString& aOldValue, + const nsAString& aNewValue) { + AssertIsOnOwningThread(); + + auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo); + if (NS_WARN_IF(principalOrErr.isErr())) { + return IPC_FAIL_NO_REASON(this); + } + + auto cachePrincipalOrErr = PrincipalInfoToPrincipal(aCachePrincipalInfo); + if (NS_WARN_IF(cachePrincipalOrErr.isErr())) { + return IPC_FAIL_NO_REASON(this); + } + + nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); + nsCOMPtr<nsIPrincipal> cachePrincipal = cachePrincipalOrErr.unwrap(); + + if (StorageUtils::PrincipalsEqual(principal, cachePrincipal)) { + Storage::NotifyChange(/* aStorage */ nullptr, principal, aKey, aOldValue, + aNewValue, + /* aStorageType */ u"localStorage", aDocumentURI, + /* aIsPrivate */ !!aPrivateBrowsingId, + /* aImmediateDispatch */ true); + } + + return IPC_OK(); +} + +// ---------------------------------------------------------------------------- +// Child +// ---------------------------------------------------------------------------- + +class StorageDBChild::ShutdownObserver final : public nsIObserver { + // Expected to be only 0 or 1. + const uint32_t mPrivateBrowsingId; + + public: + explicit ShutdownObserver(const uint32_t aPrivateBrowsingId) + : mPrivateBrowsingId(aPrivateBrowsingId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount); + } + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + private: + ~ShutdownObserver() { MOZ_ASSERT(NS_IsMainThread()); } +}; + +void StorageDBChild::AddIPDLReference() { + MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references"); + mIPCOpen = true; + AddRef(); +} + +void StorageDBChild::ReleaseIPDLReference() { + MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference"); + mIPCOpen = false; + Release(); +} + +StorageDBChild::StorageDBChild(LocalStorageManager* aManager, + const uint32_t aPrivateBrowsingId) + : mManager(aManager), + mPrivateBrowsingId(aPrivateBrowsingId), + mStatus(NS_OK), + mIPCOpen(false) { + MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); +} + +StorageDBChild::~StorageDBChild() = default; + +// static +StorageDBChild* StorageDBChild::Get(const uint32_t aPrivateBrowsingId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + + return sStorageChild[aPrivateBrowsingId]; +} + +// static +StorageDBChild* StorageDBChild::GetOrCreate(const uint32_t aPrivateBrowsingId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + + StorageDBChild*& storageChild = sStorageChild[aPrivateBrowsingId]; + if (storageChild || sStorageChildDown[aPrivateBrowsingId]) { + // When sStorageChildDown is at true, sStorageChild is null. + // Checking sStorageChildDown flag here prevents reinitialization of + // the storage child after shutdown. + return storageChild; + } + + // Use LocalStorageManager::Ensure in case we're called from + // DOMSessionStorageManager's initializer and we haven't yet initialized the + // local storage manager. + RefPtr<StorageDBChild> newStorageChild = + new StorageDBChild(LocalStorageManager::Ensure(), aPrivateBrowsingId); + + nsresult rv = newStorageChild->Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + newStorageChild.forget(&storageChild); + + return storageChild; +} + +nsTHashSet<nsCString>& StorageDBChild::OriginsHavingData() { + if (!mOriginsHavingData) { + mOriginsHavingData = MakeUnique<nsTHashSet<nsCString>>(); + } + + return *mOriginsHavingData; +} + +nsresult StorageDBChild::Init() { + MOZ_ASSERT(NS_IsMainThread()); + + ::mozilla::ipc::PBackgroundChild* actor = + ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!actor)) { + return NS_ERROR_FAILURE; + } + + nsString profilePath; + if (XRE_IsParentProcess() && mPrivateBrowsingId == 0) { + nsresult rv = StorageDBThread::GetProfilePath(profilePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + AddIPDLReference(); + + actor->SendPBackgroundStorageConstructor(this, profilePath, + mPrivateBrowsingId); + + nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); + MOZ_ASSERT(observerService); + + nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mPrivateBrowsingId); + + MOZ_ALWAYS_SUCCEEDS( + observerService->AddObserver(observer, "xpcom-shutdown", false)); + + return NS_OK; +} + +nsresult StorageDBChild::Shutdown() { + // There is nothing to do here, IPC will release automatically and + // the actual thread running on the parent process will also stop + // automatically in profile-before-change topic observer. + return NS_OK; +} + +void StorageDBChild::AsyncPreload(LocalStorageCacheBridge* aCache, + bool aPriority) { + if (mIPCOpen) { + // Adding ref to cache for the time of preload. This ensures a reference to + // to the cache and that all keys will load into this cache object. + mLoadingCaches.Insert(aCache); + SendAsyncPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), + aPriority); + } else { + // No IPC, no love. But the LoadDone call is expected. + aCache->LoadDone(NS_ERROR_UNEXPECTED); + } +} + +void StorageDBChild::AsyncGetUsage(StorageUsageBridge* aUsage) { + if (mIPCOpen) { + SendAsyncGetUsage(aUsage->OriginScope()); + } +} + +void StorageDBChild::SyncPreload(LocalStorageCacheBridge* aCache, + bool aForceSync) { + if (NS_FAILED(mStatus)) { + aCache->LoadDone(mStatus); + return; + } + + if (!mIPCOpen) { + aCache->LoadDone(NS_ERROR_UNEXPECTED); + return; + } + + // There is no way to put the child process to a wait state to receive all + // incoming async responses from the parent, hence we have to do a sync + // preload instead. We are smart though, we only demand keys that are left to + // load in case the async preload has already loaded some keys. + nsTArray<nsString> keys, values; + nsresult rv; + SendPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), + aCache->LoadedCount(), &keys, &values, &rv); + + for (uint32_t i = 0; i < keys.Length(); ++i) { + aCache->LoadItem(keys[i], values[i]); + } + + aCache->LoadDone(rv); +} + +nsresult StorageDBChild::AsyncAddItem(LocalStorageCacheBridge* aCache, + const nsAString& aKey, + const nsAString& aValue) { + if (NS_FAILED(mStatus) || !mIPCOpen) { + return mStatus; + } + + SendAsyncAddItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(), + nsString(aKey), nsString(aValue)); + OriginsHavingData().Insert(aCache->Origin()); + return NS_OK; +} + +nsresult StorageDBChild::AsyncUpdateItem(LocalStorageCacheBridge* aCache, + const nsAString& aKey, + const nsAString& aValue) { + if (NS_FAILED(mStatus) || !mIPCOpen) { + return mStatus; + } + + SendAsyncUpdateItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(), + nsString(aKey), nsString(aValue)); + OriginsHavingData().Insert(aCache->Origin()); + return NS_OK; +} + +nsresult StorageDBChild::AsyncRemoveItem(LocalStorageCacheBridge* aCache, + const nsAString& aKey) { + if (NS_FAILED(mStatus) || !mIPCOpen) { + return mStatus; + } + + SendAsyncRemoveItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(), + nsString(aKey)); + return NS_OK; +} + +nsresult StorageDBChild::AsyncClear(LocalStorageCacheBridge* aCache) { + if (NS_FAILED(mStatus) || !mIPCOpen) { + return mStatus; + } + + SendAsyncClear(aCache->OriginSuffix(), aCache->OriginNoSuffix()); + OriginsHavingData().Remove(aCache->Origin()); + return NS_OK; +} + +bool StorageDBChild::ShouldPreloadOrigin(const nsACString& aOrigin) { + // Return true if we didn't receive the origins list yet. + // I tend to rather preserve a bit of early-after-start performance + // than a bit of memory here. + return !mOriginsHavingData || mOriginsHavingData->Contains(aOrigin); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvObserve( + const nsACString& aTopic, const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + MOZ_ASSERT(!XRE_IsParentProcess()); + + if (StorageObserver* obs = StorageObserver::Self()) { + obs->Notify(PromiseFlatCString(aTopic).get(), aOriginAttributesPattern, + aOriginScope); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvOriginsHavingData( + nsTArray<nsCString>&& aOrigins) { + // Force population of mOriginsHavingData even if there are no origins so that + // ShouldPreloadOrigin does not generate false positives for all origins. + if (!aOrigins.Length()) { + Unused << OriginsHavingData(); + } + + for (uint32_t i = 0; i < aOrigins.Length(); ++i) { + OriginsHavingData().Insert(aOrigins[i]); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvLoadItem( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const nsAString& aKey, const nsAString& aValue) { + LocalStorageCache* aCache = + mManager->GetCache(aOriginSuffix, aOriginNoSuffix); + if (aCache) { + aCache->LoadItem(aKey, aValue); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvLoadDone( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const nsresult& aRv) { + LocalStorageCache* aCache = + mManager->GetCache(aOriginSuffix, aOriginNoSuffix); + if (aCache) { + aCache->LoadDone(aRv); + + // Just drop reference to this cache now since the load is done. + mLoadingCaches.Remove(static_cast<LocalStorageCacheBridge*>(aCache)); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvLoadUsage( + const nsACString& aOriginNoSuffix, const int64_t& aUsage) { + RefPtr<StorageUsageBridge> scopeUsage = + mManager->GetOriginUsage(aOriginNoSuffix, mPrivateBrowsingId); + scopeUsage->LoadUsage(aUsage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBChild::RecvError(const nsresult& aRv) { + mStatus = aRv; + return IPC_OK(); +} + +NS_IMPL_ISUPPORTS(StorageDBChild::ShutdownObserver, nsIObserver) + +NS_IMETHODIMP +StorageDBChild::ShutdownObserver::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown")); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; + } + + Unused << observerService->RemoveObserver(this, "xpcom-shutdown"); + + StorageDBChild*& storageChild = sStorageChild[mPrivateBrowsingId]; + if (storageChild) { + sStorageChildDown[mPrivateBrowsingId] = true; + + MOZ_ALWAYS_TRUE(storageChild->PBackgroundStorageChild::SendDeleteMe()); + + NS_RELEASE(storageChild); + storageChild = nullptr; + } + + return NS_OK; +} + +SessionStorageObserverChild::SessionStorageObserverChild( + SessionStorageObserver* aObserver) + : mObserver(aObserver) { + AssertIsOnOwningThread(); + MOZ_ASSERT(NextGenLocalStorageEnabled()); + MOZ_ASSERT(aObserver); + aObserver->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(SessionStorageObserverChild); +} + +SessionStorageObserverChild::~SessionStorageObserverChild() { + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(SessionStorageObserverChild); +} + +void SessionStorageObserverChild::SendDeleteMeInternal() { + AssertIsOnOwningThread(); + + if (mObserver) { + mObserver->ClearActor(); + mObserver = nullptr; + + // Don't check result here since IPC may no longer be available due to + // SessionStorageManager (which holds a strong reference to + // SessionStorageObserver) being destroyed very late in the game. + PSessionStorageObserverChild::SendDeleteMe(); + } +} + +void SessionStorageObserverChild::ActorDestroy(ActorDestroyReason aWhy) { + AssertIsOnOwningThread(); + + if (mObserver) { + mObserver->ClearActor(); + mObserver = nullptr; + } +} + +mozilla::ipc::IPCResult SessionStorageObserverChild::RecvObserve( + const nsACString& aTopic, const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + AssertIsOnOwningThread(); + + if (StorageObserver* obs = StorageObserver::Self()) { + obs->Notify(PromiseFlatCString(aTopic).get(), aOriginAttributesPattern, + aOriginScope); + } + + return IPC_OK(); +} + +SessionStorageCacheChild::SessionStorageCacheChild(SessionStorageCache* aCache) + : mCache(aCache) { + AssertIsOnOwningThread(); + MOZ_ASSERT(mCache); + + MOZ_COUNT_CTOR(SessionStorageCacheChild); +} + +SessionStorageCacheChild::~SessionStorageCacheChild() { + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(SessionStorageCacheChild); +} + +void SessionStorageCacheChild::SendDeleteMeInternal() { + AssertIsOnOwningThread(); + + if (mCache) { + mCache->ClearActor(); + mCache = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundSessionStorageCacheChild::SendDeleteMe()); + } +} + +void SessionStorageCacheChild::ActorDestroy(ActorDestroyReason aWhy) { + AssertIsOnOwningThread(); + + if (mCache) { + mCache->ClearActor(); + mCache = nullptr; + } +} + +SessionStorageManagerChild::SessionStorageManagerChild( + SessionStorageManager* aSSManager) + : mSSManager(aSSManager) { + AssertIsOnOwningThread(); + MOZ_ASSERT(mSSManager); + + MOZ_COUNT_CTOR(SessionStorageManagerChild); +} + +SessionStorageManagerChild::~SessionStorageManagerChild() { + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(SessionStorageManagerChild); +} + +void SessionStorageManagerChild::SendDeleteMeInternal() { + AssertIsOnOwningThread(); + + if (mSSManager) { + mSSManager->ClearActor(); + mSSManager = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundSessionStorageManagerChild::SendDeleteMe()); + } +} + +void SessionStorageManagerChild::ActorDestroy(ActorDestroyReason aWhy) { + AssertIsOnOwningThread(); + + if (mSSManager) { + mSSManager->ClearActor(); + mSSManager = nullptr; + } +} + +mozilla::ipc::IPCResult SessionStorageManagerChild::RecvClearStoragesForOrigin( + const nsACString& aOriginAttrs, const nsACString& aOriginKey) { + AssertIsOnOwningThread(); + + if (mSSManager) { + mSSManager->ClearStoragesForOrigin(aOriginAttrs, aOriginKey); + } + + return IPC_OK(); +} + +LocalStorageCacheParent::LocalStorageCacheParent( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + const nsACString& aOriginKey, uint32_t aPrivateBrowsingId) + : mPrincipalInfo(aPrincipalInfo), + mOriginKey(aOriginKey), + mPrivateBrowsingId(aPrivateBrowsingId), + mActorDestroyed(false) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +LocalStorageCacheParent::~LocalStorageCacheParent() { + MOZ_ASSERT(mActorDestroyed); +} + +void LocalStorageCacheParent::ActorDestroy(ActorDestroyReason aWhy) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + MOZ_ASSERT(gLocalStorageCacheParents); + + nsTArray<LocalStorageCacheParent*>* array; + gLocalStorageCacheParents->Get(mOriginKey, &array); + MOZ_ASSERT(array); + + array->RemoveElement(this); + + if (array->IsEmpty()) { + gLocalStorageCacheParents->Remove(mOriginKey); + } + + if (!gLocalStorageCacheParents->Count()) { + gLocalStorageCacheParents = nullptr; + } +} + +mozilla::ipc::IPCResult LocalStorageCacheParent::RecvDeleteMe() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + IProtocol* mgr = Manager(); + if (!PBackgroundLocalStorageCacheParent::Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LocalStorageCacheParent::RecvNotify( + const nsAString& aDocumentURI, const nsAString& aKey, + const nsAString& aOldValue, const nsAString& aNewValue) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(gLocalStorageCacheParents); + + nsTArray<LocalStorageCacheParent*>* array; + gLocalStorageCacheParents->Get(mOriginKey, &array); + MOZ_ASSERT(array); + + for (LocalStorageCacheParent* localStorageCacheParent : *array) { + if (localStorageCacheParent != this) { + // When bug 1443925 is fixed, we can compare mPrincipalInfo against + // localStorageCacheParent->PrincipalInfo() here on the background thread + // instead of posting it to the main thread. The advantage of doing so is + // that it would save an IPC message in the case where the principals do + // not match. + Unused << localStorageCacheParent->SendObserve( + mPrincipalInfo, localStorageCacheParent->PrincipalInfo(), + mPrivateBrowsingId, aDocumentURI, aKey, aOldValue, aNewValue); + } + } + + return IPC_OK(); +} + +// ---------------------------------------------------------------------------- +// Parent +// ---------------------------------------------------------------------------- + +class StorageDBParent::ObserverSink : public StorageObserverSink { + nsCOMPtr<nsIEventTarget> mOwningEventTarget; + + // Only touched on the PBackground thread. + StorageDBParent* MOZ_NON_OWNING_REF mActor; + + public: + explicit ObserverSink(StorageDBParent* aActor) + : mOwningEventTarget(GetCurrentSerialEventTarget()), mActor(aActor) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageDBParent::ObserverSink); + + void Start(); + + void Stop(); + + private: + ~ObserverSink() = default; + + void AddSink(); + + void RemoveSink(); + + void Notify(const nsACString& aTopic, + const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope); + + // StorageObserverSink + nsresult Observe(const char* aTopic, const nsAString& aOriginAttrPattern, + const nsACString& aOriginScope) override; +}; + +NS_IMPL_ADDREF(StorageDBParent) +NS_IMPL_RELEASE(StorageDBParent) + +void StorageDBParent::AddIPDLReference() { + MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references"); + mIPCOpen = true; + AddRef(); +} + +void StorageDBParent::ReleaseIPDLReference() { + MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference"); + mIPCOpen = false; + Release(); +} + +namespace {} // namespace + +StorageDBParent::StorageDBParent(const nsAString& aProfilePath, + const uint32_t aPrivateBrowsingId) + : mProfilePath(aProfilePath), + mPrivateBrowsingId(aPrivateBrowsingId), + mIPCOpen(false) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + // We are always open by IPC only + AddIPDLReference(); +} + +StorageDBParent::~StorageDBParent() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + if (mObserverSink) { + mObserverSink->Stop(); + mObserverSink = nullptr; + } +} + +void StorageDBParent::Init() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + PBackgroundParent* actor = Manager(); + MOZ_ASSERT(actor); + + if (::mozilla::ipc::BackgroundParent::IsOtherProcessActor(actor)) { + mObserverSink = new ObserverSink(this); + mObserverSink->Start(); + } + + StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId); + if (storageThread) { + nsTArray<nsCString> scopes; + storageThread->GetOriginsHavingData(&scopes); + mozilla::Unused << SendOriginsHavingData(scopes); + } +} + +StorageDBParent::CacheParentBridge* StorageDBParent::NewCache( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) { + return new CacheParentBridge(this, aOriginSuffix, aOriginNoSuffix); +} + +void StorageDBParent::ActorDestroy(ActorDestroyReason aWhy) { + // Implement me! Bug 1005169 +} + +mozilla::ipc::IPCResult StorageDBParent::RecvDeleteMe() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + IProtocol* mgr = Manager(); + if (!PBackgroundStorageParent::Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncPreload( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const bool& aPriority) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + storageThread->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix), + aPriority); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncGetUsage( + const nsACString& aOriginNoSuffix) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + // The object releases it self in LoadUsage method + RefPtr<UsageParentBridge> usage = + new UsageParentBridge(this, aOriginNoSuffix); + + storageThread->AsyncGetUsage(usage); + + return IPC_OK(); +} + +namespace { + +// We need another implementation of LocalStorageCacheBridge to do +// synchronous IPC preload. This class just receives Load* notifications +// and fills the returning arguments of RecvPreload with the database +// values for us. +class SyncLoadCacheHelper : public LocalStorageCacheBridge { + public: + SyncLoadCacheHelper(const nsACString& aOriginSuffix, + const nsACString& aOriginNoSuffix, + uint32_t aAlreadyLoadedCount, nsTArray<nsString>* aKeys, + nsTArray<nsString>* aValues, nsresult* rv) + : mMonitor("DOM Storage SyncLoad IPC"), + mSuffix(aOriginSuffix), + mOrigin(aOriginNoSuffix), + mKeys(aKeys), + mValues(aValues), + mRv(rv), + mLoaded(false), + mLoadedCount(aAlreadyLoadedCount) { + // Precaution + *mRv = NS_ERROR_UNEXPECTED; + } + + virtual const nsCString Origin() const override { + return LocalStorageManager::CreateOrigin(mSuffix, mOrigin); + } + virtual const nsCString& OriginNoSuffix() const override { return mOrigin; } + virtual const nsCString& OriginSuffix() const override { return mSuffix; } + virtual bool Loaded() override { return mLoaded; } + virtual uint32_t LoadedCount() override { return mLoadedCount; } + virtual bool LoadItem(const nsAString& aKey, + const nsAString& aValue) override { + // Called on the aCache background thread + MOZ_ASSERT(!mLoaded); + if (mLoaded) { + return false; + } + + ++mLoadedCount; + mKeys->AppendElement(aKey); + mValues->AppendElement(aValue); + return true; + } + + virtual void LoadDone(nsresult aRv) override { + // Called on the aCache background thread + MonitorAutoLock monitor(mMonitor); + MOZ_ASSERT(!mLoaded && mRv); + mLoaded = true; + if (mRv) { + *mRv = aRv; + mRv = nullptr; + } + monitor.Notify(); + } + + virtual void LoadWait() override { + // Called on the main thread, exits after LoadDone() call + MonitorAutoLock monitor(mMonitor); + while (!mLoaded) { + monitor.Wait(); + } + } + + private: + Monitor mMonitor MOZ_UNANNOTATED; + nsCString mSuffix, mOrigin; + nsTArray<nsString>* mKeys; + nsTArray<nsString>* mValues; + nsresult* mRv; + bool mLoaded; + uint32_t mLoadedCount; +}; + +} // namespace + +mozilla::ipc::IPCResult StorageDBParent::RecvPreload( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const uint32_t& aAlreadyLoadedCount, nsTArray<nsString>* aKeys, + nsTArray<nsString>* aValues, nsresult* aRv) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + RefPtr<SyncLoadCacheHelper> cache( + new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix, + aAlreadyLoadedCount, aKeys, aValues, aRv)); + + storageThread->SyncPreload(cache, true); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncAddItem( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const nsAString& aKey, const nsAString& aValue) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + nsresult rv = storageThread->AsyncAddItem( + NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue); + if (NS_FAILED(rv) && mIPCOpen) { + mozilla::Unused << SendError(rv); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncUpdateItem( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const nsAString& aKey, const nsAString& aValue) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + nsresult rv = storageThread->AsyncUpdateItem( + NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue); + if (NS_FAILED(rv) && mIPCOpen) { + mozilla::Unused << SendError(rv); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncRemoveItem( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix, + const nsAString& aKey) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + nsresult rv = storageThread->AsyncRemoveItem( + NewCache(aOriginSuffix, aOriginNoSuffix), aKey); + if (NS_FAILED(rv) && mIPCOpen) { + mozilla::Unused << SendError(rv); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncClear( + const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + nsresult rv = + storageThread->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix)); + if (NS_FAILED(rv) && mIPCOpen) { + mozilla::Unused << SendError(rv); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvAsyncFlush() { + StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + storageThread->AsyncFlush(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvStartup() { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvClearAll() { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + storageThread->AsyncClearAll(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOrigin( + const nsACString& aOriginNoSuffix) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + storageThread->AsyncClearMatchingOrigin(aOriginNoSuffix); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOriginAttributes( + const OriginAttributesPattern& aPattern) { + StorageDBThread* storageThread = + StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId); + if (!storageThread) { + return IPC_FAIL_NO_REASON(this); + } + + storageThread->AsyncClearMatchingOriginAttributes(aPattern); + + return IPC_OK(); +} + +void StorageDBParent::Observe(const nsACString& aTopic, + const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + if (mIPCOpen) { + mozilla::Unused << SendObserve(aTopic, aOriginAttributesPattern, + aOriginScope); + } +} + +namespace { + +// Results must be sent back on the main thread +class LoadRunnable : public Runnable { + public: + enum TaskType { loadItem, loadDone }; + + LoadRunnable(StorageDBParent* aParent, TaskType aType, + const nsACString& aOriginSuffix, + const nsACString& aOriginNoSuffix, + const nsAString& aKey = u""_ns, const nsAString& aValue = u""_ns) + : Runnable("dom::LoadRunnable"), + mParent(aParent), + mType(aType), + mSuffix(aOriginSuffix), + mOrigin(aOriginNoSuffix), + mKey(aKey), + mValue(aValue), + mRv(NS_ERROR_NOT_INITIALIZED) {} + + LoadRunnable(StorageDBParent* aParent, TaskType aType, + const nsACString& aOriginSuffix, + const nsACString& aOriginNoSuffix, nsresult aRv) + : Runnable("dom::LoadRunnable"), + mParent(aParent), + mType(aType), + mSuffix(aOriginSuffix), + mOrigin(aOriginNoSuffix), + mRv(aRv) {} + + private: + RefPtr<StorageDBParent> mParent; + TaskType mType; + nsCString mSuffix, mOrigin; + nsString mKey; + nsString mValue; + nsresult mRv; + + NS_IMETHOD Run() override { + if (!mParent->IPCOpen()) { + return NS_OK; + } + + switch (mType) { + case loadItem: + mozilla::Unused << mParent->SendLoadItem(mSuffix, mOrigin, mKey, + mValue); + break; + case loadDone: + mozilla::Unused << mParent->SendLoadDone(mSuffix, mOrigin, mRv); + break; + } + + mParent = nullptr; + + return NS_OK; + } +}; + +} // namespace + +// StorageDBParent::CacheParentBridge + +const nsCString StorageDBParent::CacheParentBridge::Origin() const { + return LocalStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix); +} + +bool StorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, + const nsAString& aValue) { + if (mLoaded) { + return false; + } + + ++mLoadedCount; + + RefPtr<LoadRunnable> r = + new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix, + mOriginNoSuffix, aKey, aValue); + + MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL)); + + return true; +} + +void StorageDBParent::CacheParentBridge::LoadDone(nsresult aRv) { + // Prevent send of duplicate LoadDone. + if (mLoaded) { + return; + } + + mLoaded = true; + + RefPtr<LoadRunnable> r = new LoadRunnable( + mParent, LoadRunnable::loadDone, mOriginSuffix, mOriginNoSuffix, aRv); + + MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL)); +} + +void StorageDBParent::CacheParentBridge::LoadWait() { + // Should never be called on this implementation + MOZ_ASSERT(false); +} + +// XXX Fix me! +// This should be just: +// NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::CacheParentBridge, Destroy) +// But due to different strings used for refcount logging and different return +// types, this is done manually for now. +NS_IMETHODIMP_(void) +StorageDBParent::CacheParentBridge::Release(void) { + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + nsrefcnt count = --mRefCnt; + NS_LOG_RELEASE(this, count, "LocalStorageCacheBridge"); + if (0 == count) { + mRefCnt = 1; /* stabilize */ + /* enable this to find non-threadsafe destructors: */ + /* NS_ASSERT_OWNINGTHREAD(_class); */ + Destroy(); + } +} + +void StorageDBParent::CacheParentBridge::Destroy() { + if (mOwningEventTarget->IsOnCurrentThread()) { + delete this; + return; + } + + RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod( + "CacheParentBridge::Destroy", this, &CacheParentBridge::Destroy); + + MOZ_ALWAYS_SUCCEEDS( + mOwningEventTarget->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL)); +} + +// StorageDBParent::UsageParentBridge + +namespace { + +class UsageRunnable : public Runnable { + public: + UsageRunnable(StorageDBParent* aParent, const nsACString& aOriginScope, + const int64_t& aUsage) + : Runnable("dom::UsageRunnable"), + mParent(aParent), + mOriginScope(aOriginScope), + mUsage(aUsage) {} + + private: + NS_IMETHOD Run() override { + if (!mParent->IPCOpen()) { + return NS_OK; + } + + mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage); + + mParent = nullptr; + + return NS_OK; + } + + RefPtr<StorageDBParent> mParent; + nsCString mOriginScope; + int64_t mUsage; +}; + +} // namespace + +void StorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage) { + RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage); + + MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL)); +} + +// XXX Fix me! +// This should be just: +// NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::UsageParentBridge, Destroy) +// But due to different strings used for refcount logging, this is done manually +// for now. +NS_IMETHODIMP_(MozExternalRefCountType) +StorageDBParent::UsageParentBridge::Release(void) { + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + nsrefcnt count = --mRefCnt; + NS_LOG_RELEASE(this, count, "StorageUsageBridge"); + if (count == 0) { + Destroy(); + return 0; + } + return count; +} + +void StorageDBParent::UsageParentBridge::Destroy() { + if (mOwningEventTarget->IsOnCurrentThread()) { + delete this; + return; + } + + RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod( + "UsageParentBridge::Destroy", this, &UsageParentBridge::Destroy); + + MOZ_ALWAYS_SUCCEEDS( + mOwningEventTarget->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL)); +} + +void StorageDBParent::ObserverSink::Start() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + RefPtr<Runnable> runnable = + NewRunnableMethod("StorageDBParent::ObserverSink::AddSink", this, + &StorageDBParent::ObserverSink::AddSink); + + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); +} + +void StorageDBParent::ObserverSink::Stop() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + mActor = nullptr; + + RefPtr<Runnable> runnable = + NewRunnableMethod("StorageDBParent::ObserverSink::RemoveSink", this, + &StorageDBParent::ObserverSink::RemoveSink); + + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); +} + +void StorageDBParent::ObserverSink::AddSink() { + MOZ_ASSERT(NS_IsMainThread()); + + StorageObserver* observer = StorageObserver::Self(); + if (observer) { + observer->AddSink(this); + } +} + +void StorageDBParent::ObserverSink::RemoveSink() { + MOZ_ASSERT(NS_IsMainThread()); + + StorageObserver* observer = StorageObserver::Self(); + if (observer) { + observer->RemoveSink(this); + } +} + +void StorageDBParent::ObserverSink::Notify( + const nsACString& aTopic, const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + if (mActor) { + mActor->Observe(aTopic, aOriginAttributesPattern, aOriginScope); + } +} + +nsresult StorageDBParent::ObserverSink::Observe( + const char* aTopic, const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<Runnable> runnable = NewRunnableMethod<nsCString, nsString, nsCString>( + "StorageDBParent::ObserverSink::Observe2", this, + &StorageDBParent::ObserverSink::Notify, aTopic, aOriginAttributesPattern, + aOriginScope); + + MOZ_ALWAYS_SUCCEEDS( + mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL)); + + return NS_OK; +} + +SessionStorageObserverParent::SessionStorageObserverParent() + : mActorDestroyed(false) { + MOZ_ASSERT(NS_IsMainThread()); + + StorageObserver* observer = StorageObserver::Self(); + if (observer) { + observer->AddSink(this); + } +} + +SessionStorageObserverParent::~SessionStorageObserverParent() { + MOZ_ASSERT(mActorDestroyed); + + StorageObserver* observer = StorageObserver::Self(); + if (observer) { + observer->RemoveSink(this); + } +} + +void SessionStorageObserverParent::ActorDestroy(ActorDestroyReason aWhy) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; +} + +mozilla::ipc::IPCResult SessionStorageObserverParent::RecvDeleteMe() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mActorDestroyed); + + IProtocol* mgr = Manager(); + if (!PSessionStorageObserverParent::Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +nsresult SessionStorageObserverParent::Observe( + const char* aTopic, const nsAString& aOriginAttributesPattern, + const nsACString& aOriginScope) { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mActorDestroyed) { + mozilla::Unused << SendObserve(nsDependentCString(aTopic), + aOriginAttributesPattern, aOriginScope); + } + return NS_OK; +} + +SessionStorageCacheParent::SessionStorageCacheParent( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + const nsACString& aOriginKey, SessionStorageManagerParent* aActor) + : mPrincipalInfo(aPrincipalInfo), + mOriginKey(aOriginKey), + mManagerActor(aActor) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mManagerActor); +} + +SessionStorageCacheParent::~SessionStorageCacheParent() = default; + +void SessionStorageCacheParent::ActorDestroy(ActorDestroyReason aWhy) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + mManagerActor = nullptr; +} + +mozilla::ipc::IPCResult SessionStorageCacheParent::RecvLoad( + nsTArray<SSSetItemInfo>* aData) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mManagerActor); + + mLoadReceived.Flip(); + + RefPtr<BackgroundSessionStorageManager> manager = mManagerActor->GetManager(); + MOZ_ASSERT(manager); + + OriginAttributes attrs; + MOZ_ALWAYS_TRUE( + StoragePrincipalHelper::GetOriginAttributes(mPrincipalInfo, attrs)); + + nsAutoCString originAttrs; + attrs.CreateSuffix(originAttrs); + + manager->CopyDataToContentProcess(originAttrs, mOriginKey, *aData); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult SessionStorageCacheParent::RecvCheckpoint( + nsTArray<SSWriteInfo>&& aWriteInfos) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mManagerActor); + + RefPtr<BackgroundSessionStorageManager> manager = mManagerActor->GetManager(); + MOZ_ASSERT(manager); + + OriginAttributes attrs; + StoragePrincipalHelper::GetOriginAttributes(mPrincipalInfo, attrs); + + nsAutoCString originAttrs; + attrs.CreateSuffix(originAttrs); + + manager->UpdateData(originAttrs, mOriginKey, aWriteInfos); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult SessionStorageCacheParent::RecvDeleteMe() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mManagerActor); + + mManagerActor = nullptr; + + IProtocol* mgr = Manager(); + if (!PBackgroundSessionStorageCacheParent::Send__delete__(this)) { + return IPC_FAIL( + mgr, "Failed to delete PBackgroundSessionStorageCacheParent actor"); + } + return IPC_OK(); +} + +SessionStorageManagerParent::SessionStorageManagerParent(uint64_t aTopContextId) + : mBackgroundManager( + BackgroundSessionStorageManager::GetOrCreate(aTopContextId)) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mBackgroundManager); + mBackgroundManager->AddParticipatingActor(this); +} + +SessionStorageManagerParent::~SessionStorageManagerParent() = default; + +void SessionStorageManagerParent::ActorDestroy(ActorDestroyReason aWhy) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + if (mBackgroundManager) { + mBackgroundManager->RemoveParticipatingActor(this); + } + + mBackgroundManager = nullptr; +} + +already_AddRefed<PBackgroundSessionStorageCacheParent> +SessionStorageManagerParent::AllocPBackgroundSessionStorageCacheParent( + const PrincipalInfo& aPrincipalInfo, const nsACString& aOriginKey) { + return MakeAndAddRef<SessionStorageCacheParent>(aPrincipalInfo, aOriginKey, + this); +} + +BackgroundSessionStorageManager* SessionStorageManagerParent::GetManager() + const { + return mBackgroundManager; +} + +mozilla::ipc::IPCResult SessionStorageManagerParent::RecvClearStorages( + const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + mBackgroundManager->ClearStorages(aPattern, aOriginScope); + return IPC_OK(); +} + +mozilla::ipc::IPCResult SessionStorageManagerParent::RecvDeleteMe() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(mBackgroundManager); + + mBackgroundManager->RemoveParticipatingActor(this); + + mBackgroundManager = nullptr; + + IProtocol* mgr = Manager(); + if (!PBackgroundSessionStorageManagerParent::Send__delete__(this)) { + return IPC_FAIL( + mgr, "Failed to delete PBackgroundSessionStorageManagerParent actor"); + } + return IPC_OK(); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ + +PBackgroundLocalStorageCacheParent* AllocPBackgroundLocalStorageCacheParent( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + const nsACString& aOriginKey, const uint32_t& aPrivateBrowsingId) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + RefPtr<LocalStorageCacheParent> actor = new LocalStorageCacheParent( + aPrincipalInfo, aOriginKey, aPrivateBrowsingId); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +mozilla::ipc::IPCResult RecvPBackgroundLocalStorageCacheConstructor( + mozilla::ipc::PBackgroundParent* aBackgroundActor, + PBackgroundLocalStorageCacheParent* aActor, + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + const nsACString& aOriginKey, const uint32_t& aPrivateBrowsingId) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + auto* actor = static_cast<LocalStorageCacheParent*>(aActor); + + if (!gLocalStorageCacheParents) { + gLocalStorageCacheParents = new LocalStorageCacheParentHashtable(); + } + + gLocalStorageCacheParents->GetOrInsertNew(aOriginKey)->AppendElement(actor); + + // We are currently trusting the content process not to lie to us. It is + // future work to consult the ClientManager to determine whether this is a + // legitimate origin for the content process. + + return IPC_OK(); +} + +bool DeallocPBackgroundLocalStorageCacheParent( + PBackgroundLocalStorageCacheParent* aActor) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + RefPtr<LocalStorageCacheParent> actor = + dont_AddRef(static_cast<LocalStorageCacheParent*>(aActor)); + + return true; +} + +PBackgroundStorageParent* AllocPBackgroundStorageParent( + const nsAString& aProfilePath, const uint32_t& aPrivateBrowsingId) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(NextGenLocalStorageEnabled()) || + NS_WARN_IF(aPrivateBrowsingId >= kPrivateBrowsingIdCount)) { + return nullptr; + } + + return new StorageDBParent(aProfilePath, aPrivateBrowsingId); +} + +mozilla::ipc::IPCResult RecvPBackgroundStorageConstructor( + PBackgroundStorageParent* aActor, const nsAString& aProfilePath, + const uint32_t& aPrivateBrowsingId) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + + auto* actor = static_cast<StorageDBParent*>(aActor); + actor->Init(); + return IPC_OK(); +} + +bool DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + StorageDBParent* actor = static_cast<StorageDBParent*>(aActor); + actor->ReleaseIPDLReference(); + return true; +} + +PSessionStorageObserverParent* AllocPSessionStorageObserverParent() { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<SessionStorageObserverParent> actor = + new SessionStorageObserverParent(); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool RecvPSessionStorageObserverConstructor( + PSessionStorageObserverParent* aActor) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + return true; +} + +bool DeallocPSessionStorageObserverParent( + PSessionStorageObserverParent* aActor) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + RefPtr<SessionStorageObserverParent> actor = + dont_AddRef(static_cast<SessionStorageObserverParent*>(aActor)); + + return true; +} + +already_AddRefed<PBackgroundSessionStorageManagerParent> +AllocPBackgroundSessionStorageManagerParent(const uint64_t& aTopContextId) { + return MakeAndAddRef<SessionStorageManagerParent>(aTopContextId); +} + +} // namespace mozilla::dom |