Bug 1286798 - Part 29: Implement implicit snapshotting of databases; r=asuth,mccr8

This improves performance a lot in cases when multiple operations are invoked by a single JS function (number of sync IPC calls is reduced to a minimum). It also improves correctness since changes are not visible to other content processes until a JS function finishes.
The patch implements core infrastructure, all items are sent to content when a snapshot is initialized and everything is fully working. However, sending of all items at once is not optimal for bigger databases. Support for lazy loading of items is implemented in a following patch.
This commit is contained in:
Jan Varga 2018-11-29 21:48:47 +01:00
parent 54be9b7307
commit c4f55013cf
16 changed files with 1425 additions and 480 deletions

View File

@ -10,6 +10,7 @@
#include "LSDatabase.h"
#include "LSObject.h"
#include "LSObserver.h"
#include "LSSnapshot.h"
namespace mozilla {
namespace dom {
@ -66,7 +67,7 @@ LSDatabaseChild::RecvRequestAllowToClose()
AssertIsOnOwningThread();
if (mDatabase) {
mDatabase->AllowToClose();
mDatabase->RequestAllowToClose();
// TODO: A new datastore will be prepared at first LocalStorage API
// synchronous call. It would be better to start preparing a new
@ -77,6 +78,24 @@ LSDatabaseChild::RecvRequestAllowToClose()
return IPC_OK();
}
PBackgroundLSSnapshotChild*
LSDatabaseChild::AllocPBackgroundLSSnapshotChild(const nsString& aDocumentURI,
const int64_t& aRequestedSize,
LSSnapshotInitInfo* aInitInfo)
{
MOZ_CRASH("PBackgroundLSSnapshotChild actor should be manually constructed!");
}
bool
LSDatabaseChild::DeallocPBackgroundLSSnapshotChild(
PBackgroundLSSnapshotChild* aActor)
{
MOZ_ASSERT(aActor);
delete aActor;
return true;
}
/*******************************************************************************
* LSObserverChild
******************************************************************************/
@ -251,5 +270,51 @@ LSSimpleRequestChild::Recv__delete__(const LSSimpleRequestResponse& aResponse)
return IPC_OK();
}
/*******************************************************************************
* LSSnapshotChild
******************************************************************************/
LSSnapshotChild::LSSnapshotChild(LSSnapshot* aSnapshot)
: mSnapshot(aSnapshot)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aSnapshot);
MOZ_COUNT_CTOR(LSSnapshotChild);
}
LSSnapshotChild::~LSSnapshotChild()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(LSSnapshotChild);
}
void
LSSnapshotChild::SendDeleteMeInternal()
{
AssertIsOnOwningThread();
if (mSnapshot) {
mSnapshot->ClearActor();
mSnapshot = nullptr;
MOZ_ALWAYS_TRUE(PBackgroundLSSnapshotChild::SendDeleteMe());
}
}
void
LSSnapshotChild::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
if (mSnapshot) {
mSnapshot->ClearActor();
#ifdef DEBUG
mSnapshot = nullptr;
#endif
}
}
} // namespace dom
} // namespace mozilla

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/PBackgroundLSObserverChild.h"
#include "mozilla/dom/PBackgroundLSRequestChild.h"
#include "mozilla/dom/PBackgroundLSSimpleRequestChild.h"
#include "mozilla/dom/PBackgroundLSSnapshotChild.h"
namespace mozilla {
@ -28,6 +29,7 @@ class LSObject;
class LSObserver;
class LSRequestChildCallback;
class LSSimpleRequestChildCallback;
class LSSnapshot;
class LSDatabaseChild final
: public PBackgroundLSDatabaseChild
@ -63,6 +65,15 @@ private:
mozilla::ipc::IPCResult
RecvRequestAllowToClose() override;
PBackgroundLSSnapshotChild*
AllocPBackgroundLSSnapshotChild(const nsString& aDocumentURI,
const int64_t& aRequestedSize,
LSSnapshotInitInfo* aInitInfo) override;
bool
DeallocPBackgroundLSSnapshotChild(PBackgroundLSSnapshotChild* aActor)
override;
};
class LSObserverChild final
@ -203,6 +214,38 @@ protected:
{ }
};
class LSSnapshotChild final
: public PBackgroundLSSnapshotChild
{
friend class LSDatabase;
friend class LSSnapshot;
LSSnapshot* mSnapshot;
NS_DECL_OWNINGTHREAD
public:
void
AssertIsOnOwningThread() const
{
NS_ASSERT_OWNINGTHREAD(LSSnapshotChild);
}
private:
// Only created by LSDatabase.
explicit LSSnapshotChild(LSSnapshot* aSnapshot);
// Only destroyed by LSDatabaseChild.
~LSSnapshotChild();
void
SendDeleteMeInternal();
// IPDL methods are only called by IPDL.
void
ActorDestroy(ActorDestroyReason aWhy) override;
};
} // namespace dom
} // namespace mozilla

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,10 @@ StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
LSDatabase::LSDatabase(const nsACString& aOrigin)
: mActor(nullptr)
, mSnapshot(nullptr)
, mOrigin(aOrigin)
, mAllowedToClose(false)
, mRequestedAllowToClose(false)
{
AssertIsOnOwningThread();
@ -35,6 +37,7 @@ LSDatabase::LSDatabase(const nsACString& aOrigin)
LSDatabase::~LSDatabase()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mSnapshot);
if (!mAllowedToClose) {
AllowToClose();
@ -63,11 +66,239 @@ LSDatabase::SetActor(LSDatabaseChild* aActor)
mActor = aActor;
}
void
LSDatabase::RequestAllowToClose()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mRequestedAllowToClose);
mRequestedAllowToClose = true;
if (!mSnapshot) {
AllowToClose();
}
}
void
LSDatabase::NoteFinishedSnapshot(LSSnapshot* aSnapshot)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aSnapshot == mSnapshot);
mSnapshot = nullptr;
if (mRequestedAllowToClose) {
AllowToClose();
}
}
nsresult
LSDatabase::GetLength(LSObject* aObject,
uint32_t* aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->GetLength(aResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::GetKey(LSObject* aObject,
uint32_t aIndex,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->GetKey(aIndex, aResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::GetItem(LSObject* aObject,
const nsAString& aKey,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->GetItem(aKey, aResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::GetKeys(LSObject* aObject,
nsTArray<nsString>& aKeys)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->GetKeys(aKeys);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::SetItem(LSObject* aObject,
const nsAString& aKey,
const nsAString& aValue,
LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::RemoveItem(LSObject* aObject,
const nsAString& aKey,
LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::Clear(LSObject* aObject,
LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject, /* aRequestedBySetItem */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSnapshot->Clear(aNotifyInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
LSDatabase::EnsureSnapshot(LSObject* aObject,
bool aRequestedBySetItem)
{
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
if (mSnapshot) {
return NS_OK;
}
RefPtr<LSSnapshot> snapshot = new LSSnapshot(this);
LSSnapshotChild* actor = new LSSnapshotChild(snapshot);
int64_t requestedSize = aRequestedBySetItem ? 4096 : 0;
LSSnapshotInitInfo initInfo;
bool ok =
mActor->SendPBackgroundLSSnapshotConstructor(actor,
aObject->DocumentURI(),
requestedSize,
&initInfo);
if (NS_WARN_IF(!ok)) {
return NS_ERROR_FAILURE;
}
snapshot->SetActor(actor);
// This add refs snapshot.
nsresult rv = snapshot->Init(initInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
mSnapshot = snapshot;
return NS_OK;
}
void
LSDatabase::AllowToClose()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mAllowedToClose);
MOZ_ASSERT(!mSnapshot);
mAllowedToClose = true;
@ -84,130 +315,5 @@ LSDatabase::AllowToClose()
}
}
nsresult
LSDatabase::GetLength(uint32_t* aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
uint32_t result;
if (NS_WARN_IF(!mActor->SendGetLength(&result))) {
return NS_ERROR_FAILURE;
}
*aResult = result;
return NS_OK;
}
nsresult
LSDatabase::GetKey(uint32_t aIndex,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsString result;
if (NS_WARN_IF(!mActor->SendGetKey(aIndex, &result))) {
return NS_ERROR_FAILURE;
}
aResult = result;
return NS_OK;
}
nsresult
LSDatabase::GetItem(const nsAString& aKey,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsString result;
if (NS_WARN_IF(!mActor->SendGetItem(nsString(aKey), &result))) {
return NS_ERROR_FAILURE;
}
aResult = result;
return NS_OK;
}
nsresult
LSDatabase::GetKeys(nsTArray<nsString>& aKeys)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsTArray<nsString> result;
if (NS_WARN_IF(!mActor->SendGetKeys(&result))) {
return NS_ERROR_FAILURE;
}
aKeys.SwapElements(result);
return NS_OK;
}
nsresult
LSDatabase::SetItem(const nsAString& aDocumentURI,
const nsAString& aKey,
const nsAString& aValue,
LSWriteOpResponse& aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
LSWriteOpResponse response;
if (NS_WARN_IF(!mActor->SendSetItem(nsString(aDocumentURI),
nsString(aKey),
nsString(aValue),
&response))) {
return NS_ERROR_FAILURE;
}
aResponse = response;
return NS_OK;
}
nsresult
LSDatabase::RemoveItem(const nsAString& aDocumentURI,
const nsAString& aKey,
LSWriteOpResponse& aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
LSWriteOpResponse response;
if (NS_WARN_IF(!mActor->SendRemoveItem(nsString(aDocumentURI),
nsString(aKey),
&response))) {
return NS_ERROR_FAILURE;
}
aResponse = response;
return NS_OK;
}
nsresult
LSDatabase::Clear(const nsAString& aDocumentURI,
LSWriteOpResponse& aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
LSWriteOpResponse response;
if (NS_WARN_IF(!mActor->SendClear(nsString(aDocumentURI), &response))) {
return NS_ERROR_FAILURE;
}
aResponse = response;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -11,15 +11,18 @@ namespace mozilla {
namespace dom {
class LSDatabaseChild;
class LSWriteOpResponse;
class LSSnapshot;
class LSDatabase final
{
LSDatabaseChild* mActor;
LSSnapshot* mSnapshot;
const nsCString mOrigin;
bool mAllowedToClose;
bool mRequestedAllowToClose;
public:
explicit LSDatabase(const nsACString& aOrigin);
@ -47,9 +50,6 @@ public:
mActor = nullptr;
}
void
AllowToClose();
bool
IsAllowedToClose() const
{
@ -58,37 +58,54 @@ public:
return mAllowedToClose;
}
nsresult
GetLength(uint32_t* aResult);
void
RequestAllowToClose();
void
NoteFinishedSnapshot(LSSnapshot* aSnapshot);
nsresult
GetKey(uint32_t aIndex,
GetLength(LSObject* aObject,
uint32_t* aResult);
nsresult
GetKey(LSObject* aObject,
uint32_t aIndex,
nsAString& aResult);
nsresult
GetItem(const nsAString& aKey,
GetItem(LSObject* aObject,
const nsAString& aKey,
nsAString& aResult);
nsresult
GetKeys(nsTArray<nsString>& aKeys);
GetKeys(LSObject* aObject,
nsTArray<nsString>& aKeys);
nsresult
SetItem(const nsAString& aDocumentURI,
SetItem(LSObject* aObject,
const nsAString& aKey,
const nsAString& aValue,
LSWriteOpResponse& aResponse);
LSNotifyInfo& aNotifyInfo);
nsresult
RemoveItem(const nsAString& aDocumentURI,
RemoveItem(LSObject* aObject,
const nsAString& aKey,
LSWriteOpResponse& aResponse);
LSNotifyInfo& aNotifyInfo);
nsresult
Clear(const nsAString& aDocumentURI,
LSWriteOpResponse& aResponse);
Clear(LSObject* aObject,
LSNotifyInfo& aNotifyInfo);
private:
~LSDatabase();
nsresult
EnsureSnapshot(LSObject* aObject,
bool aRequestedBySetItem);
void
AllowToClose();
};
} // namespace dom

View File

@ -349,7 +349,7 @@ LSObject::GetLength(nsIPrincipal& aSubjectPrincipal,
}
uint32_t result;
rv = mDatabase->GetLength(&result);
rv = mDatabase->GetLength(this, &result);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return 0;
@ -378,7 +378,7 @@ LSObject::Key(uint32_t aIndex,
}
nsString result;
rv = mDatabase->GetKey(aIndex, result);
rv = mDatabase->GetKey(this, aIndex, result);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
@ -407,7 +407,7 @@ LSObject::GetItem(const nsAString& aKey,
}
nsString result;
rv = mDatabase->GetItem(aKey, result);
rv = mDatabase->GetItem(this, aKey, result);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
@ -432,7 +432,7 @@ LSObject::GetSupportedNames(nsTArray<nsString>& aNames)
return;
}
rv = mDatabase->GetKeys(aNames);
rv = mDatabase->GetKeys(this, aNames);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -457,16 +457,12 @@ LSObject::SetItem(const nsAString& aKey,
return;
}
LSWriteOpResponse response;
rv = mDatabase->SetItem(mDocumentURI, aKey, aValue, response);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
LSNotifyInfo info;
rv = GetInfoFromResponse(response, info);
if (NS_FAILED(rv)) {
rv = mDatabase->SetItem(this, aKey, aValue, info);
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
rv = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
@ -494,16 +490,9 @@ LSObject::RemoveItem(const nsAString& aKey,
return;
}
LSWriteOpResponse response;
rv = mDatabase->RemoveItem(mDocumentURI, aKey, response);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
LSNotifyInfo info;
rv = GetInfoFromResponse(response, info);
if (NS_FAILED(rv)) {
rv = mDatabase->RemoveItem(this, aKey, info);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
@ -530,16 +519,9 @@ LSObject::Clear(nsIPrincipal& aSubjectPrincipal,
return;
}
LSWriteOpResponse response;
rv = mDatabase->Clear(mDocumentURI, response);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
LSNotifyInfo info;
rv = GetInfoFromResponse(response, info);
if (NS_FAILED(rv)) {
rv = mDatabase->Clear(this, info);
if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv);
return;
}
@ -780,28 +762,6 @@ LSObject::DropObserver()
}
}
nsresult
LSObject::GetInfoFromResponse(const LSWriteOpResponse& aResponse,
LSNotifyInfo& aInfo)
{
AssertIsOnOwningThread();
if (aResponse.type() == LSWriteOpResponse::Tnsresult) {
nsresult errorCode = aResponse.get_nsresult();
if (errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE) {
errorCode = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
}
return errorCode;
}
MOZ_ASSERT(aResponse.type() == LSWriteOpResponse::TLSNotifyInfo);
aInfo = aResponse.get_LSNotifyInfo();
return NS_OK;
}
void
LSObject::OnChange(const nsAString& aKey,
const nsAString& aOldValue,

View File

@ -26,14 +26,12 @@ class PrincipalInfo;
namespace dom {
class LSDatabase;
class LSNotifyInfo;
class LSObjectChild;
class LSObserver;
class LSRequestChild;
class LSRequestChildCallback;
class LSRequestParams;
class LSRequestResponse;
class LSWriteOpResponse;
class LSObject final
: public Storage
@ -80,6 +78,12 @@ public:
NS_ASSERT_OWNINGTHREAD(LSObject);
}
const nsString&
DocumentURI() const
{
return mDocumentURI;
}
LSRequestChild*
StartRequest(nsIEventTarget* aMainEventTarget,
const LSRequestParams& aParams,
@ -162,10 +166,6 @@ private:
void
DropObserver();
nsresult
GetInfoFromResponse(const LSWriteOpResponse& aResponse,
LSNotifyInfo& aInfo);
void
OnChange(const nsAString& aKey,
const nsAString& aOldValue,

View File

@ -0,0 +1,320 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "LSSnapshot.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
LSSnapshot::LSSnapshot(LSDatabase* aDatabase)
: mDatabase(aDatabase)
, mActor(nullptr)
, mExactUsage(0)
, mPeakUsage(0)
#ifdef DEBUG
, mInitialized(false)
, mSentFinish(false)
#endif
{
AssertIsOnOwningThread();
}
LSSnapshot::~LSSnapshot()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mDatabase);
MOZ_ASSERT_IF(mInitialized, mSentFinish);
if (mActor) {
mActor->SendDeleteMeInternal();
MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
}
}
void
LSSnapshot::SetActor(LSSnapshotChild* aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(!mActor);
mActor = aActor;
}
nsresult
LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mInitialized);
MOZ_ASSERT(!mSentFinish);
const nsTArray<LSItemInfo>& itemInfos = aInitInfo.itemInfos();
for (uint32_t i = 0; i < itemInfos.Length(); i++) {
const LSItemInfo& itemInfo = itemInfos[i];
mValues.Put(itemInfo.key(), itemInfo.value());
}
mExactUsage = aInitInfo.initialUsage();
mPeakUsage = aInitInfo.peakUsage();
nsCOMPtr<nsIRunnable> runnable = this;
nsContentUtils::RunInStableState(runnable.forget());
#ifdef DEBUG
mInitialized = true;
#endif
return NS_OK;
}
nsresult
LSSnapshot::GetLength(uint32_t* aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
*aResult = mValues.Count();
return NS_OK;
}
nsresult
LSSnapshot::GetKey(uint32_t aIndex,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
aResult.SetIsVoid(true);
for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
if (aIndex == 0) {
aResult = iter.Key();
return NS_OK;
}
aIndex--;
}
return NS_OK;
}
nsresult
LSSnapshot::GetItem(const nsAString& aKey,
nsAString& aResult)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
nsString result;
if (!mValues.Get(aKey, &result)) {
result.SetIsVoid(true);
}
aResult = result;
return NS_OK;
}
nsresult
LSSnapshot::GetKeys(nsTArray<nsString>& aKeys)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
aKeys.AppendElement(iter.Key());
}
return NS_OK;
}
nsresult
LSSnapshot::SetItem(const nsAString& aKey,
const nsAString& aValue,
LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
nsString oldValue;
nsresult rv = GetItem(aKey, oldValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool changed;
if (oldValue == aValue && oldValue.IsVoid() == aValue.IsVoid()) {
changed = false;
} else {
changed = true;
int64_t delta = static_cast<int64_t>(aValue.Length()) -
static_cast<int64_t>(oldValue.Length());
if (oldValue.IsVoid()) {
delta += static_cast<int64_t>(aKey.Length());
}
rv = UpdateUsage(delta);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mValues.Put(aKey, nsString(aValue));
LSSetItemInfo setItemInfo;
setItemInfo.key() = aKey;
setItemInfo.oldValue() = oldValue;
setItemInfo.value() = aValue;
mWriteInfos.AppendElement(std::move(setItemInfo));
}
aNotifyInfo.changed() = changed;
aNotifyInfo.oldValue() = oldValue;
return NS_OK;
}
nsresult
LSSnapshot::RemoveItem(const nsAString& aKey,
LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
nsString oldValue;
nsresult rv = GetItem(aKey, oldValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool changed;
if (oldValue.IsVoid()) {
changed = false;
} else {
changed = true;
int64_t delta = -(static_cast<int64_t>(aKey.Length()) +
static_cast<int64_t>(oldValue.Length()));
DebugOnly<nsresult> rv = UpdateUsage(delta);
MOZ_ASSERT(NS_SUCCEEDED(rv));
mValues.Remove(aKey);
LSRemoveItemInfo removeItemInfo;
removeItemInfo.key() = aKey;
removeItemInfo.oldValue() = oldValue;
mWriteInfos.AppendElement(std::move(removeItemInfo));
}
aNotifyInfo.changed() = changed;
aNotifyInfo.oldValue() = oldValue;
return NS_OK;
}
nsresult
LSSnapshot::Clear(LSNotifyInfo& aNotifyInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
bool changed;
if (!mValues.Count()) {
changed = false;
} else {
changed = true;
DebugOnly<nsresult> rv = UpdateUsage(-mExactUsage);
MOZ_ASSERT(NS_SUCCEEDED(rv));
mValues.Clear();
LSClearInfo clearInfo;
mWriteInfos.AppendElement(std::move(clearInfo));
}
aNotifyInfo.changed() = changed;
return NS_OK;
}
nsresult
LSSnapshot::UpdateUsage(int64_t aDelta)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mDatabase);
MOZ_ASSERT(mActor);
MOZ_ASSERT(mPeakUsage >= mExactUsage);
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(!mSentFinish);
int64_t newExactUsage = mExactUsage + aDelta;
if (newExactUsage > mPeakUsage) {
int64_t minSize = newExactUsage - mPeakUsage;
int64_t requestedSize = minSize + 4096;
int64_t size;
if (NS_WARN_IF(!mActor->SendIncreasePeakUsage(requestedSize,
minSize,
&size))) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(size >= 0);
if (size == 0) {
return NS_ERROR_FILE_NO_DEVICE_SPACE;
}
mPeakUsage += size;
}
mExactUsage = newExactUsage;
return NS_OK;
}
NS_IMPL_ISUPPORTS(LSSnapshot, nsIRunnable)
NS_IMETHODIMP
LSSnapshot::Run()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mDatabase);
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mSentFinish);
MOZ_ALWAYS_TRUE(mActor->SendFinish(mWriteInfos));
#ifdef DEBUG
mSentFinish = true;
#endif
mDatabase->NoteFinishedSnapshot(this);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,96 @@
/* -*- 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/. */
#ifndef mozilla_dom_localstorage_LSSnapshot_h
#define mozilla_dom_localstorage_LSSnapshot_h
namespace mozilla {
namespace dom {
class LSSnapshotChild;
class LSSnapshot final
: public nsIRunnable
{
RefPtr<LSDatabase> mDatabase;
LSSnapshotChild* mActor;
nsDataHashtable<nsStringHashKey, nsString> mValues;
nsTArray<LSWriteInfo> mWriteInfos;
int64_t mExactUsage;
int64_t mPeakUsage;
#ifdef DEBUG
bool mInitialized;
bool mSentFinish;
#endif
public:
explicit LSSnapshot(LSDatabase* aDatabase);
void
AssertIsOnOwningThread() const
{
NS_ASSERT_OWNINGTHREAD(LSSnapshot);
}
void
SetActor(LSSnapshotChild* aActor);
void
ClearActor()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mActor);
mActor = nullptr;
}
nsresult
Init(const LSSnapshotInitInfo& aInitInfo);
nsresult
GetLength(uint32_t* aResult);
nsresult
GetKey(uint32_t aIndex,
nsAString& aResult);
nsresult
GetItem(const nsAString& aKey,
nsAString& aResult);
nsresult
GetKeys(nsTArray<nsString>& aKeys);
nsresult
SetItem(const nsAString& aKey,
const nsAString& aValue,
LSNotifyInfo& aNotifyInfo);
nsresult
RemoveItem(const nsAString& aKey,
LSNotifyInfo& aNotifyInfo);
nsresult
Clear(LSNotifyInfo& aNotifyInfo);
private:
~LSSnapshot();
nsresult
UpdateUsage(int64_t aDelta);
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_localstorage_LSSnapshot_h

View File

@ -186,6 +186,41 @@ namespace dom {
extern const char16_t* kLocalStorageType;
class MOZ_STACK_CLASS LSNotifyInfo
{
bool mChanged;
nsString mOldValue;
public:
LSNotifyInfo()
: mChanged(false)
{ }
bool
changed() const
{
return mChanged;
}
bool&
changed()
{
return mChanged;
}
const nsString&
oldValue() const
{
return mOldValue;
}
nsString&
oldValue()
{
return mOldValue;
}
};
bool
NextGenLocalStorageEnabled();

View File

@ -3,25 +3,28 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PBackgroundLSSnapshot;
namespace mozilla {
namespace dom {
struct LSNotifyInfo
struct LSItemInfo
{
bool changed;
nsString oldValue;
nsString key;
nsString value;
};
union LSWriteOpResponse
struct LSSnapshotInitInfo
{
nsresult;
LSNotifyInfo;
LSItemInfo[] itemInfos;
int64_t initialUsage;
int64_t peakUsage;
};
sync protocol PBackgroundLSDatabase
{
manager PBackground;
manages PBackgroundLSSnapshot;
parent:
// The DeleteMe message is used to avoid a race condition between the parent
@ -37,26 +40,9 @@ parent:
async AllowToClose();
sync GetLength()
returns (uint32_t length);
sync GetKey(uint32_t index)
returns (nsString key);
sync GetItem(nsString key)
returns (nsString value);
sync GetKeys()
returns (nsString[] keys);
sync SetItem(nsString documentURI, nsString key, nsString value)
returns (LSWriteOpResponse response);
sync RemoveItem(nsString documentURI, nsString key)
returns (LSWriteOpResponse response);
sync Clear(nsString documentURI)
returns (LSWriteOpResponse response);
sync PBackgroundLSSnapshot(nsString documentURI,
int64_t requestedSize)
returns (LSSnapshotInitInfo initInfo);
child:
async __delete__();

View File

@ -0,0 +1,57 @@
/* 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 protocol PBackground;
include protocol PBackgroundLSDatabase;
namespace mozilla {
namespace dom {
struct LSSetItemInfo
{
nsString key;
nsString oldValue;
nsString value;
};
struct LSRemoveItemInfo
{
nsString key;
nsString oldValue;
};
struct LSClearInfo
{
};
union LSWriteInfo
{
LSSetItemInfo;
LSRemoveItemInfo;
LSClearInfo;
};
struct LSSnapshotFinishInfo
{
LSWriteInfo[] writeInfos;
};
sync protocol PBackgroundLSSnapshot
{
manager PBackgroundLSDatabase;
parent:
async DeleteMe();
async Finish(LSSnapshotFinishInfo finishInfo);
sync IncreasePeakUsage(int64_t requestedSize, int64_t minSize)
returns (int64_t size);
child:
async __delete__();
};
} // namespace dom
} // namespace mozilla

View File

@ -33,6 +33,7 @@ UNIFIED_SOURCES += [
'LSDatabase.cpp',
'LSObject.cpp',
'LSObserver.cpp',
'LSSnapshot.cpp',
'ReportInternalError.cpp',
]
@ -42,6 +43,7 @@ IPDL_SOURCES += [
'PBackgroundLSRequest.ipdl',
'PBackgroundLSSharedTypes.ipdlh',
'PBackgroundLSSimpleRequest.ipdl',
'PBackgroundLSSnapshot.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -43,6 +43,13 @@ function finishTest()
})
}
function continueToNextStep()
{
executeSoon(function() {
testGenerator.next();
});
}
function continueToNextStepSync()
{
testGenerator.next();

View File

@ -67,6 +67,11 @@ function* testSteps()
storages[0].clear();
// Let the internal snapshot finish (usage is not descreased until all
// snapshots finish)..
continueToNextStep();
yield undefined;
info("Verifying more data can be written");
for (let i = 0; i < urls.length; i++) {

View File

@ -922,19 +922,9 @@ description =
description =
[PBackgroundStorage::Preload]
description =
[PBackgroundLSDatabase::GetLength]
[PBackgroundLSDatabase::PBackgroundLSSnapshot]
description =
[PBackgroundLSDatabase::GetKey]
description =
[PBackgroundLSDatabase::GetItem]
description =
[PBackgroundLSDatabase::GetKeys]
description =
[PBackgroundLSDatabase::SetItem]
description =
[PBackgroundLSDatabase::RemoveItem]
description =
[PBackgroundLSDatabase::Clear]
[PBackgroundLSSnapshot::IncreasePeakUsage]
description =
[PRemoteSpellcheckEngine::Check]
description =