Bug 1350637 - Part 2: Core changes for LocalStorage on PBackground; r=asuth

- stop inheriting StorageDBBridge in StorageDBThread and StorageDBChild
- move StorageDBThread and StorageDBChild initialization out of LocalStorageCache
- use IPC even for the intra-process communication in main process
- rationalize a bit storage observer code
- make StorageDBParent to always be created and destroyed on the background thread
This commit is contained in:
Jan Varga 2017-07-26 12:19:13 +02:00
parent ceff0f7d8f
commit c186d54bb2
10 changed files with 616 additions and 164 deletions

View File

@ -24,10 +24,6 @@ namespace dom {
#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
// static
StorageDBBridge* LocalStorageCache::sDatabase = nullptr;
bool LocalStorageCache::sDatabaseDown = false;
namespace {
const uint32_t kDefaultSet = 0;
@ -243,13 +239,14 @@ LocalStorageCache::Preload()
return;
}
if (!StartDatabase()) {
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (!storageChild) {
mLoaded = true;
mLoadResult = NS_ERROR_FAILURE;
return;
}
sDatabase->AsyncPreload(this);
storageChild->AsyncPreload(this);
}
namespace {
@ -309,7 +306,7 @@ LocalStorageCache::WaitForPreload(Telemetry::HistogramID aTelemetryID)
// No need to check sDatabase for being non-null since preload is either
// done before we've shut the DB down or when the DB could not start,
// preload has not even be started.
sDatabase->SyncPreload(this);
StorageDBChild::Get()->SyncPreload(this);
}
nsresult
@ -429,17 +426,18 @@ LocalStorageCache::SetItem(const LocalStorage* aStorage, const nsAString& aKey,
data.mKeys.Put(aKey, aValue);
if (aSource == ContentMutation && Persist(aStorage)) {
if (!sDatabase) {
StorageDBChild* storageChild = StorageDBChild::Get();
if (!storageChild) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
return NS_ERROR_NOT_INITIALIZED;
}
if (DOMStringIsNull(aOld)) {
return sDatabase->AsyncAddItem(this, aKey, aValue);
return storageChild->AsyncAddItem(this, aKey, aValue);
}
return sDatabase->AsyncUpdateItem(this, aKey, aValue);
return storageChild->AsyncUpdateItem(this, aKey, aValue);
}
return NS_OK;
@ -470,13 +468,14 @@ LocalStorageCache::RemoveItem(const LocalStorage* aStorage,
data.mKeys.Remove(aKey);
if (aSource == ContentMutation && Persist(aStorage)) {
if (!sDatabase) {
StorageDBChild* storageChild = StorageDBChild::Get();
if (!storageChild) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
return NS_ERROR_NOT_INITIALIZED;
}
return sDatabase->AsyncRemoveItem(this, aKey);
return storageChild->AsyncRemoveItem(this, aKey);
}
return NS_OK;
@ -511,13 +510,14 @@ LocalStorageCache::Clear(const LocalStorage* aStorage,
}
if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
if (!sDatabase) {
StorageDBChild* storageChild = StorageDBChild::Get();
if (!storageChild) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
return NS_ERROR_NOT_INITIALIZED;
}
return sDatabase->AsyncClear(this);
return storageChild->AsyncClear(this);
}
return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
@ -672,67 +672,5 @@ StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
return true;
}
// static
StorageDBBridge*
LocalStorageCache::StartDatabase()
{
if (sDatabase || sDatabaseDown) {
// When sDatabaseDown is at true, sDatabase is null.
// Checking sDatabaseDown flag here prevents reinitialization of
// the database after shutdown.
return sDatabase;
}
if (XRE_IsParentProcess()) {
// XXX Fix me!
return nullptr;
} else {
// Use LocalStorageManager::Ensure in case we're called from
// DOMSessionStorageManager's initializer and we haven't yet initialized the
// local storage manager.
RefPtr<StorageDBChild> db = new StorageDBChild(
LocalStorageManager::Ensure());
nsresult rv = db->Init();
if (NS_FAILED(rv)) {
return nullptr;
}
db.forget(&sDatabase);
}
return sDatabase;
}
// static
StorageDBBridge*
LocalStorageCache::GetDatabase()
{
return sDatabase;
}
// static
nsresult
LocalStorageCache::StopDatabase()
{
if (!sDatabase) {
return NS_OK;
}
sDatabaseDown = true;
nsresult rv = sDatabase->Shutdown();
if (XRE_IsParentProcess()) {
delete sDatabase;
} else {
StorageDBChild* child = static_cast<StorageDBChild*>(sDatabase);
NS_RELEASE(child);
}
sDatabase = nullptr;
return rv;
}
} // namespace dom
} // namespace mozilla

View File

@ -131,13 +131,6 @@ public:
void GetKeys(const LocalStorage* aStorage, nsTArray<nsString>& aKeys);
// Starts the database engine thread or the IPC bridge
static StorageDBBridge* StartDatabase();
static StorageDBBridge* GetDatabase();
// Stops the thread and flushes all uncommited data
static nsresult StopDatabase();
// LocalStorageCacheBridge
virtual const nsCString Origin() const;
@ -260,13 +253,6 @@ private:
// Whether we have already captured state of the cache preload on our first
// access.
bool mPreloadTelemetryRecorded : 1;
// StorageDBThread on the parent or single process,
// StorageDBChild on the child process.
static StorageDBBridge* sDatabase;
// False until we shut the database down.
static bool sDatabaseDown;
};
// StorageUsage
@ -274,7 +260,7 @@ private:
class StorageUsageBridge
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageUsageBridge)
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(StorageUsageBridge)
virtual const nsCString& OriginScope() = 0;
virtual void LoadUsage(const int64_t aUsage) = 0;

View File

@ -76,7 +76,7 @@ LocalStorageManager::LocalStorageManager()
// Do this only on the child process. The thread IPC bridge
// is also used to communicate chrome observer notifications.
// Note: must be called after we set sSelf
LocalStorageCache::StartDatabase();
StorageDBChild::GetOrCreate();
}
}
@ -168,9 +168,9 @@ LocalStorageManager::GetOriginUsage(const nsACString& aOriginNoSuffix)
usage = new StorageUsage(aOriginNoSuffix);
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (db) {
db->AsyncGetUsage(usage);
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (storageChild) {
storageChild->AsyncGetUsage(usage);
}
mUsages.Put(aOriginNoSuffix, usage);
@ -234,7 +234,7 @@ LocalStorageManager::GetStorageInternal(CreateMode aCreateMode,
if (aCreateMode == CreateMode::CreateIfShouldPreload) {
// This is a demand to just preload the cache, if the scope has
// no data stored, bypass creation and preload of the cache.
StorageDBBridge* db = LocalStorageCache::GetDatabase();
StorageDBChild* db = StorageDBChild::Get();
if (db) {
if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(originAttrSuffix, originKey))) {
return NS_OK;

View File

@ -131,6 +131,7 @@ private:
nsDataHashtable<nsCStringHashKey, RefPtr<StorageUsage> > mUsages;
friend class LocalStorageCache;
friend class StorageDBChild;
// Releases cache since it is no longer referrered by any Storage object.
virtual void DropCache(LocalStorageCache* aCache);

View File

@ -23,6 +23,7 @@
#include "mozIStorageValueArray.h"
#include "mozIStorageFunction.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsIObserverService.h"
#include "nsVariant.h"
#include "mozilla/IOInterposer.h"
@ -48,6 +49,11 @@ using namespace StorageUtils;
namespace { // anon
StorageDBThread* sStorageThread = nullptr;
// False until we shut the storage thread down.
bool sStorageThreadDown = false;
// This is only a compatibility code for schema version 0. Returns the 'scope'
// key in the schema version 0 format for the scope column.
nsCString
@ -106,11 +112,64 @@ Scheme0Scope(LocalStorageCacheBridge* aCache)
} // anon
// XXX Fix me!
#if 0
StorageDBBridge::StorageDBBridge()
{
}
#endif
class StorageDBThread::InitHelper final
: public Runnable
{
nsCOMPtr<nsIEventTarget> mOwningThread;
mozilla::Mutex mMutex;
mozilla::CondVar mCondVar;
nsString mProfilePath;
nsresult mMainThreadResultCode;
bool mWaiting;
public:
InitHelper()
: Runnable("dom::StorageDBThread::InitHelper")
, mOwningThread(GetCurrentThreadEventTarget())
, mMutex("InitHelper::mMutex")
, mCondVar(mMutex, "InitHelper::mCondVar")
, mMainThreadResultCode(NS_OK)
, mWaiting(true)
{ }
// Because of the `sync Preload` IPC, we need to be able to synchronously
// initialize, which includes consulting and initializing
// some main-thread-only APIs. Bug 1386441 discusses improving this situation.
nsresult
SyncDispatchAndReturnProfilePath(nsAString& aProfilePath);
private:
~InitHelper() override = default;
nsresult
RunOnMainThread();
NS_DECL_NSIRUNNABLE
};
class StorageDBThread::NoteBackgroundThreadRunnable final
: public Runnable
{
nsCOMPtr<nsIEventTarget> mOwningThread;
public:
NoteBackgroundThreadRunnable()
: Runnable("dom::StorageDBThread::NoteBackgroundThreadRunnable")
, mOwningThread(GetCurrentThreadEventTarget())
{ }
private:
~NoteBackgroundThreadRunnable() override = default;
NS_DECL_NSIRUNNABLE
};
StorageDBThread::StorageDBThread()
: mThread(nullptr)
@ -127,26 +186,66 @@ StorageDBThread::StorageDBThread()
{
}
// static
StorageDBThread*
StorageDBThread::Get()
{
AssertIsOnBackgroundThread();
return sStorageThread;
}
// static
StorageDBThread*
StorageDBThread::GetOrCreate()
{
AssertIsOnBackgroundThread();
if (sStorageThread || sStorageThreadDown) {
// When sStorageThreadDown is at true, sStorageThread is null.
// Checking sStorageThreadDown flag here prevents reinitialization of
// the storage thread after shutdown.
return sStorageThread;
}
nsAutoPtr<StorageDBThread> storageThread(new StorageDBThread());
nsresult rv = storageThread->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
sStorageThread = storageThread.forget();
return sStorageThread;
}
nsresult
StorageDBThread::Init()
{
nsresult rv;
AssertIsOnBackgroundThread();
// Need to determine location on the main thread, since
// NS_GetSpecialDirectory access the atom table that can
// be accessed only on the main thread.
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mDatabaseFile));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<InitHelper> helper = new InitHelper();
nsString profilePath;
nsresult rv = helper->SyncDispatchAndReturnProfilePath(profilePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mDatabaseFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mDatabaseFile->InitWithPath(profilePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mDatabaseFile->Append(NS_LITERAL_STRING("webappsstore.sqlite"));
NS_ENSURE_SUCCESS(rv, rv);
// Ensure mozIStorageService init on the main thread first.
nsCOMPtr<mozIStorageService> service =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Need to keep the lock to avoid setting mThread later then
// the thread body executes.
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
@ -158,12 +257,20 @@ StorageDBThread::Init()
return NS_ERROR_OUT_OF_MEMORY;
}
RefPtr<NoteBackgroundThreadRunnable> runnable =
new NoteBackgroundThreadRunnable();
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
return NS_OK;
}
nsresult
StorageDBThread::Shutdown()
{
AssertIsOnBackgroundThread();
sStorageThreadDown = true;
if (!mThread) {
return NS_ERROR_NOT_INITIALIZED;
}
@ -904,7 +1011,7 @@ StorageDBThread::DBOperation::Perform(StorageDBThread* aThread)
}
StatementCache* statements;
if (MOZ_UNLIKELY(NS_IsMainThread())) {
if (MOZ_UNLIKELY(IsOnBackgroundThread())) {
statements = &aThread->mReaderStatements;
} else {
statements = &aThread->mWorkerStatements;
@ -1494,5 +1601,115 @@ StorageDBThread::PendingOperations::IsOriginUpdatePending(const nsACString& aOri
return false;
}
nsresult
StorageDBThread::
InitHelper::SyncDispatchAndReturnProfilePath(nsAString& aProfilePath)
{
AssertIsOnBackgroundThread();
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
mozilla::MutexAutoLock autolock(mMutex);
while (mWaiting) {
mCondVar.Wait();
}
if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
return mMainThreadResultCode;
}
aProfilePath = mProfilePath;
return NS_OK;
}
nsresult
StorageDBThread::
InitHelper::RunOnMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
// Need to determine location on the main thread, since
// NS_GetSpecialDirectory accesses the atom table that can
// only be accessed on the main thread.
nsCOMPtr<nsIFile> profileDir;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(profileDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = profileDir->GetPath(mProfilePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// This service has to be started on the main thread currently.
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
StorageDBThread::
InitHelper::Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = RunOnMainThread();
if (NS_WARN_IF(NS_FAILED(rv))) {
mMainThreadResultCode = rv;
}
mozilla::MutexAutoLock lock(mMutex);
MOZ_ASSERT(mWaiting);
mWaiting = false;
mCondVar.Notify();
return NS_OK;
}
NS_IMETHODIMP
StorageDBThread::
NoteBackgroundThreadRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
StorageObserver* observer = StorageObserver::Self();
MOZ_ASSERT(observer);
observer->NoteBackgroundThread(mOwningThread);
return NS_OK;
}
NS_IMETHODIMP
StorageDBThread::
ShutdownRunnable::Run()
{
if (NS_IsMainThread()) {
mDone = true;
return NS_OK;
}
AssertIsOnBackgroundThread();
if (sStorageThread) {
sStorageThread->Shutdown();
delete sStorageThread;
sStorageThread = nullptr;
}
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -32,6 +32,15 @@ class StorageUsage;
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
// XXX Fix me!
// 1. Move comments to StorageDBThread/StorageDBChild.
// 2. Devirtualize relevant methods in StorageDBThread/StorageDBChild.
// 3. Remove relevant methods in StorageDBThread/StorageDBChild that are
// unused.
// 4. Remove this class completely.
//
// See bug 1387636 for more details.
#if 0
// Interface used by the cache to post operations to the asynchronous
// database thread or process.
class StorageDBBridge
@ -100,17 +109,15 @@ public:
// Check whether the scope has any data stored on disk and is thus allowed to
// preload
virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0;
// Get the complete list of scopes having data
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins) = 0;
};
#endif
// The implementation of the the database engine, this directly works
// with the sqlite or any other db API we are based on
// This class is resposible for collecting and processing asynchronous
// DB operations over caches (LocalStorageCache) communicating though
// LocalStorageCacheBridge interface class
class StorageDBThread final : public StorageDBBridge
class StorageDBThread final
{
public:
class PendingOperations;
@ -287,11 +294,43 @@ public:
Monitor mMonitor;
};
class InitHelper;
class NoteBackgroundThreadRunnable;
class ShutdownRunnable : public Runnable
{
// Only touched on the main thread.
bool& mDone;
public:
explicit ShutdownRunnable(bool& aDone)
: Runnable("dom::StorageDBThread::ShutdownRunnable")
, mDone(aDone)
{
MOZ_ASSERT(NS_IsMainThread());
}
private:
~ShutdownRunnable()
{ }
NS_DECL_NSIRUNNABLE
};
public:
StorageDBThread();
virtual ~StorageDBThread() {}
static StorageDBThread*
Get();
static StorageDBThread*
GetOrCreate();
virtual nsresult Init();
// Flushes all uncommited data and stops the I/O thread.
virtual nsresult Shutdown();
virtual void AsyncPreload(LocalStorageCacheBridge* aCache,
@ -358,7 +397,9 @@ public:
virtual void AsyncFlush();
virtual bool ShouldPreloadOrigin(const nsACString& aOrigin);
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins);
// Get the complete list of scopes having data.
void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins);
private:
nsCOMPtr<nsIFile> mDatabaseFile;

View File

@ -20,10 +20,38 @@
namespace mozilla {
namespace dom {
namespace {
StorageDBChild* sStorageChild = nullptr;
// False until we shut the storage child down.
bool sStorageChildDown = false;
}
// ----------------------------------------------------------------------------
// Child
// ----------------------------------------------------------------------------
class StorageDBChild::ShutdownObserver final
: public nsIObserver
{
public:
ShutdownObserver()
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
private:
~ShutdownObserver()
{
MOZ_ASSERT(NS_IsMainThread());
}
};
NS_IMPL_ADDREF(StorageDBChild)
NS_IMETHODIMP_(MozExternalRefCountType) StorageDBChild::Release(void)
@ -70,6 +98,44 @@ StorageDBChild::~StorageDBChild()
{
}
// static
StorageDBChild*
StorageDBChild::Get()
{
MOZ_ASSERT(NS_IsMainThread());
return sStorageChild;
}
// static
StorageDBChild*
StorageDBChild::GetOrCreate()
{
MOZ_ASSERT(NS_IsMainThread());
if (sStorageChild || sStorageChildDown) {
// When sStorageChildDown is at true, sStorageChild is null.
// Checking sStorageChildDown flag here prevents reinitialization of
// the storage child after shutdown.
return sStorageChild;
}
// Use LocalStorageManager::Ensure in case we're called from
// DOMSessionStorageManager's initializer and we haven't yet initialized the
// local storage manager.
RefPtr<StorageDBChild> storageChild =
new StorageDBChild(LocalStorageManager::Ensure());
nsresult rv = storageChild->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
storageChild.forget(&sStorageChild);
return sStorageChild;
}
nsTHashtable<nsCStringHashKey>&
StorageDBChild::OriginsHavingData()
{
@ -83,6 +149,8 @@ StorageDBChild::OriginsHavingData()
nsresult
StorageDBChild::Init()
{
MOZ_ASSERT(NS_IsMainThread());
PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!actor)) {
return NS_ERROR_FAILURE;
@ -92,6 +160,16 @@ StorageDBChild::Init()
actor->SendPBackgroundStorageConstructor(this);
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
MOZ_ASSERT(observerService);
nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
MOZ_ALWAYS_SUCCEEDS(
observerService->AddObserver(observer,
"xpcom-shutdown",
false));
return NS_OK;
}
@ -295,6 +373,35 @@ StorageDBChild::RecvError(const nsresult& 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");
if (sStorageChild) {
sStorageChildDown = true;
NS_RELEASE(sStorageChild);
sStorageChild = nullptr;
}
return NS_OK;
}
// ----------------------------------------------------------------------------
// Parent
// ----------------------------------------------------------------------------
@ -335,10 +442,10 @@ private:
return NS_OK;
}
StorageDBBridge* db = LocalStorageCache::GetDatabase();
if (db) {
StorageDBThread* storageThread = StorageDBThread::Get();
if (storageThread) {
InfallibleTArray<nsCString> scopes;
db->GetOriginsHavingData(&scopes);
storageThread->GetOriginsHavingData(&scopes);
mozilla::Unused << mParent->SendOriginsHavingData(scopes);
}
@ -413,27 +520,31 @@ StorageDBParent::RecvAsyncPreload(const nsCString& aOriginSuffix,
const nsCString& aOriginNoSuffix,
const bool& aPriority)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
db->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix), aPriority);
storageThread->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix),
aPriority);
return IPC_OK();
}
mozilla::ipc::IPCResult
StorageDBParent::RecvAsyncGetUsage(const nsCString& aOriginNoSuffix)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
// The object releases it self in LoadUsage method
RefPtr<UsageParentBridge> usage =
new UsageParentBridge(this, aOriginNoSuffix);
db->AsyncGetUsage(usage);
storageThread->AsyncGetUsage(usage);
return IPC_OK();
}
@ -524,8 +635,8 @@ StorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
InfallibleTArray<nsString>* aValues,
nsresult* aRv)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
@ -533,7 +644,8 @@ StorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix, aAlreadyLoadedCount,
aKeys, aValues, aRv));
db->SyncPreload(cache, true);
storageThread->SyncPreload(cache, true);
return IPC_OK();
}
@ -543,13 +655,15 @@ StorageDBParent::RecvAsyncAddItem(const nsCString& aOriginSuffix,
const nsString& aKey,
const nsString& aValue)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = db->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey,
aValue);
nsresult rv =
storageThread->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix),
aKey,
aValue);
if (NS_FAILED(rv) && mIPCOpen) {
mozilla::Unused << SendError(rv);
}
@ -563,13 +677,15 @@ StorageDBParent::RecvAsyncUpdateItem(const nsCString& aOriginSuffix,
const nsString& aKey,
const nsString& aValue)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = db->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix),
aKey, aValue);
nsresult rv =
storageThread->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix),
aKey,
aValue);
if (NS_FAILED(rv) && mIPCOpen) {
mozilla::Unused << SendError(rv);
}
@ -582,13 +698,14 @@ StorageDBParent::RecvAsyncRemoveItem(const nsCString& aOriginSuffix,
const nsCString& aOriginNoSuffix,
const nsString& aKey)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = db->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix),
aKey);
nsresult rv =
storageThread->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix),
aKey);
if (NS_FAILED(rv) && mIPCOpen) {
mozilla::Unused << SendError(rv);
}
@ -600,12 +717,13 @@ mozilla::ipc::IPCResult
StorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
const nsCString& aOriginNoSuffix)
{
StorageDBBridge* db = LocalStorageCache::StartDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = db->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
nsresult rv =
storageThread->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
if (NS_FAILED(rv) && mIPCOpen) {
mozilla::Unused << SendError(rv);
}
@ -616,12 +734,13 @@ StorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
mozilla::ipc::IPCResult
StorageDBParent::RecvAsyncFlush()
{
StorageDBBridge* db = LocalStorageCache::GetDatabase();
if (!db) {
StorageDBThread* storageThread = StorageDBThread::Get();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
db->AsyncFlush();
storageThread->AsyncFlush();
return IPC_OK();
}
@ -704,6 +823,8 @@ private:
break;
}
mParent = nullptr;
return NS_OK;
}
};
@ -731,7 +852,10 @@ StorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey,
RefPtr<LoadRunnable> r =
new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix,
mOriginNoSuffix, aKey, aValue);
NS_DispatchToMainThread(r);
MOZ_ALWAYS_SUCCEEDS(
mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
return true;
}
@ -748,7 +872,9 @@ StorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
RefPtr<LoadRunnable> r =
new LoadRunnable(mParent, LoadRunnable::loadDone, mOriginSuffix,
mOriginNoSuffix, aRv);
NS_DispatchToMainThread(r);
MOZ_ALWAYS_SUCCEEDS(
mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
}
void
@ -758,6 +884,42 @@ StorageDBParent::CacheParentBridge::LoadWait()
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 {
@ -782,6 +944,9 @@ private:
}
mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage);
mParent = nullptr;
return NS_OK;
}
@ -796,7 +961,43 @@ void
StorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
{
RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage);
NS_DispatchToMainThread(r);
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));
}
/*******************************************************************************

View File

@ -28,14 +28,22 @@ class PBackgroundStorageParent;
// is responsible to send all requests to the parent process
// and expects asynchronous answers. Those are then transparently
// forwarded back to consumers on the child process.
class StorageDBChild final : public StorageDBBridge
, public PBackgroundStorageChild
class StorageDBChild final
: public PBackgroundStorageChild
{
class ShutdownObserver;
virtual ~StorageDBChild();
public:
explicit StorageDBChild(LocalStorageManager* aManager);
static StorageDBChild*
Get();
static StorageDBChild*
GetOrCreate();
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
NS_IMETHOD_(MozExternalRefCountType) Release(void);
@ -69,17 +77,21 @@ public:
}
virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix)
{ /* NO-OP on the child process */ }
{
MOZ_CRASH("Shouldn't be called!");
}
virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern)
{ /* NO-OP on the child process */ }
{
MOZ_CRASH("Shouldn't be called!");
}
virtual void AsyncFlush()
{ SendAsyncFlush(); }
{
MOZ_CRASH("Shouldn't be called!");
}
virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix);
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins)
{ NS_NOTREACHED("Not implemented for child process"); }
private:
mozilla::ipc::IPCResult RecvObserve(const nsCString& aTopic,
@ -148,7 +160,8 @@ public:
CacheParentBridge(StorageDBParent* aParentDB,
const nsACString& aOriginSuffix,
const nsACString& aOriginNoSuffix)
: mParent(aParentDB)
: mOwningEventTarget(GetCurrentThreadSerialEventTarget())
, mParent(aParentDB)
, mOriginSuffix(aOriginSuffix), mOriginNoSuffix(aOriginNoSuffix)
, mLoaded(false), mLoadedCount(0) {}
virtual ~CacheParentBridge() {}
@ -168,7 +181,14 @@ public:
virtual void LoadDone(nsresult aRv);
virtual void LoadWait();
NS_IMETHOD_(void)
Release(void);
private:
void
Destroy();
nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
RefPtr<StorageDBParent> mParent;
nsCString mOriginSuffix, mOriginNoSuffix;
bool mLoaded;
@ -181,14 +201,23 @@ public:
public:
UsageParentBridge(StorageDBParent* aParentDB,
const nsACString& aOriginScope)
: mParent(aParentDB), mOriginScope(aOriginScope) {}
: mOwningEventTarget(GetCurrentThreadSerialEventTarget())
, mParent(aParentDB)
, mOriginScope(aOriginScope) {}
virtual ~UsageParentBridge() {}
// StorageUsageBridge
virtual const nsCString& OriginScope() { return mOriginScope; }
virtual void LoadUsage(const int64_t usage);
NS_IMETHOD_(MozExternalRefCountType)
Release(void);
private:
void
Destroy();
nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
RefPtr<StorageDBParent> mParent;
nsCString mOriginScope;
};

View File

@ -70,8 +70,9 @@ StorageObserver::Init()
// Shutdown
obs->AddObserver(sSelf, "profile-after-change", true);
obs->AddObserver(sSelf, "profile-before-change", true);
obs->AddObserver(sSelf, "xpcom-shutdown", true);
if (XRE_IsParentProcess()) {
obs->AddObserver(sSelf, "profile-before-change", true);
}
// Observe low device storage notifications.
obs->AddObserver(sSelf, "disk-space-watcher", true);
@ -145,6 +146,12 @@ StorageObserver::Notify(const char* aTopic,
}
}
void
StorageObserver::NoteBackgroundThread(nsIEventTarget* aBackgroundThread)
{
mBackgroundThread = aBackgroundThread;
}
NS_IMETHODIMP
StorageObserver::Observe(nsISupports* aSubject,
const char* aTopic,
@ -177,8 +184,11 @@ StorageObserver::Observe(nsISupports* aSubject,
if (timer == mDBThreadStartDelayTimer) {
mDBThreadStartDelayTimer = nullptr;
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
#endif
}
return NS_OK;
@ -190,10 +200,13 @@ StorageObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearAll();
#endif
Notify("cookie-cleared");
@ -254,10 +267,13 @@ StorageObserver::Observe(nsISupports* aSubject,
}
if (!strcmp(aTopic, "extension:purge-localStorage")) {
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearAll();
#endif
Notify("extension:purge-localStorage-caches");
@ -287,10 +303,13 @@ StorageObserver::Observe(nsISupports* aSubject,
rv = CreateReversedDomain(aceDomain, originScope);
NS_ENSURE_SUCCESS(rv, rv);
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearMatchingOrigin(originScope);
#endif
Notify("domain-data-cleared", EmptyString(), originScope);
@ -312,10 +331,13 @@ StorageObserver::Observe(nsISupports* aSubject,
return NS_ERROR_FAILURE;
}
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearMatchingOriginAttributes(pattern);
#endif
Notify("origin-attr-pattern-cleared", nsDependentString(aData));
@ -328,11 +350,20 @@ StorageObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
if (!strcmp(aTopic, "profile-before-change") ||
!strcmp(aTopic, "xpcom-shutdown")) {
rv = LocalStorageCache::StopDatabase();
if (NS_FAILED(rv)) {
NS_WARNING("Error while stopping Storage DB background thread");
if (!strcmp(aTopic, "profile-before-change")) {
MOZ_ASSERT(XRE_IsParentProcess());
if (mBackgroundThread) {
bool done = false;
RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
new StorageDBThread::ShutdownRunnable(done);
MOZ_ALWAYS_SUCCEEDS(
mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done; }));
mBackgroundThread = nullptr;
}
return NS_OK;
@ -350,10 +381,13 @@ StorageObserver::Observe(nsISupports* aSubject,
#ifdef DOM_STORAGE_TESTS
if (!strcmp(aTopic, "domstorage-test-flush-force")) {
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::GetDatabase();
if (db) {
db->AsyncFlush();
}
#endif
return NS_OK;
}

View File

@ -51,6 +51,9 @@ public:
const nsAString& aOriginAttributesPattern = EmptyString(),
const nsACString& aOriginScope = EmptyCString());
void
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
private:
virtual ~StorageObserver() {}
@ -58,6 +61,8 @@ private:
static StorageObserver* sSelf;
nsCOMPtr<nsIEventTarget> mBackgroundThread;
// Weak references
nsTArray<StorageObserverSink*> mSinks;
nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;