Bug 1542669 - LSNG: GetItem, SetItem, RemoveItem should pass the key when creating a new snapsot; r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D29136
This commit is contained in:
Jan Varga 2019-04-09 06:18:29 +02:00
Родитель 6f634e87e9
Коммит 128af24b07
8 изменённых файлов: 165 добавлений и 60 удалений

Просмотреть файл

@ -70,9 +70,9 @@ mozilla::ipc::IPCResult LSDatabaseChild::RecvRequestAllowToClose() {
}
PBackgroundLSSnapshotChild* LSDatabaseChild::AllocPBackgroundLSSnapshotChild(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) {
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
MOZ_CRASH("PBackgroundLSSnapshotChild actor should be manually constructed!");
}

Просмотреть файл

@ -78,9 +78,9 @@ class LSDatabaseChild final : public PBackgroundLSDatabaseChild {
mozilla::ipc::IPCResult RecvRequestAllowToClose() override;
PBackgroundLSSnapshotChild* AllocPBackgroundLSSnapshotChild(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) override;
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
bool DeallocPBackgroundLSSnapshotChild(
PBackgroundLSSnapshotChild* aActor) override;

Просмотреть файл

@ -1713,7 +1713,8 @@ class Datastore final
void NoteInactiveDatabase(Database* aDatabase);
void GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
void GetSnapshotInitInfo(const nsString& aKey, bool& aAddKeyToUnknownItems,
nsTHashtable<nsStringHashKey>& aLoadedItems,
nsTArray<LSItemInfo>& aItemInfos,
uint32_t& aNextLoadIndex, uint32_t& aTotalLength,
int64_t& aInitialUsage, int64_t& aPeakUsage,
@ -1929,14 +1930,15 @@ class Database final
mozilla::ipc::IPCResult RecvAllowToClose() override;
PBackgroundLSSnapshotParent* AllocPBackgroundLSSnapshotParent(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) override;
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
mozilla::ipc::IPCResult RecvPBackgroundLSSnapshotConstructor(
PBackgroundLSSnapshotParent* aActor, const nsString& aDocumentURI,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
const nsString& aKey, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) override;
bool DeallocPBackgroundLSSnapshotParent(
PBackgroundLSSnapshotParent* aActor) override;
@ -2051,6 +2053,7 @@ class Snapshot final : public PBackgroundLSSnapshotParent {
Snapshot(Database* aDatabase, const nsAString& aDocumentURI);
void Init(nsTHashtable<nsStringHashKey>& aLoadedItems,
nsTHashtable<nsStringHashKey>& aUnknownItems,
uint32_t aNextLoadIndex, uint32_t aTotalLength,
int64_t aInitialUsage, int64_t aPeakUsage,
LSSnapshot::LoadState aLoadState) {
@ -2064,14 +2067,17 @@ class Snapshot final : public PBackgroundLSSnapshotParent {
MOZ_ASSERT(mPeakUsage == -1);
mLoadedItems.SwapElements(aLoadedItems);
mUnknownItems.SwapElements(aUnknownItems);
mNextLoadIndex = aNextLoadIndex;
mTotalLength = aTotalLength;
mUsage = aInitialUsage;
mPeakUsage = aPeakUsage;
if (aLoadState == LSSnapshot::LoadState::AllOrderedKeys) {
MOZ_ASSERT(mUnknownItems.Count() == 0);
mLoadKeysReceived = true;
} else if (aLoadState == LSSnapshot::LoadState::AllOrderedItems) {
MOZ_ASSERT(mLoadedItems.Count() == 0);
MOZ_ASSERT(mUnknownItems.Count() == 0);
MOZ_ASSERT(mNextLoadIndex == mTotalLength);
mLoadedReceived = true;
mLoadedAllItems = true;
@ -4646,7 +4652,9 @@ void Datastore::NoteInactiveDatabase(Database* aDatabase) {
}
}
void Datastore::GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
void Datastore::GetSnapshotInitInfo(const nsString& aKey,
bool& aAddKeyToUnknownItems,
nsTHashtable<nsStringHashKey>& aLoadedItems,
nsTArray<LSItemInfo>& aItemInfos,
uint32_t& aNextLoadIndex,
uint32_t& aTotalLength,
@ -4656,6 +4664,20 @@ void Datastore::GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(!mInUpdateBatch);
nsString value;
int64_t sizeOfKey = 0;
int64_t sizeOfItem = 0;
bool checkKey = false;
if (!aKey.IsVoid()) {
GetItem(aKey, value);
if (!value.IsVoid()) {
sizeOfKey = aKey.Length();
sizeOfItem = sizeOfKey + value.Length();
checkKey = true;
}
}
#ifdef DEBUG
int64_t sizeOfKeys = 0;
int64_t sizeOfItems = 0;
@ -4668,44 +4690,81 @@ void Datastore::GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
MOZ_ASSERT(mSizeOfItems == sizeOfItems);
#endif
if (mSizeOfKeys <= gSnapshotPrefill) {
if (mSizeOfItems <= gSnapshotPrefill) {
if (mSizeOfKeys - sizeOfKey <= gSnapshotPrefill) {
if (mSizeOfItems - sizeOfItem <= gSnapshotPrefill) {
// We're sending all ordered items, we don't need to check keys because
// mOrderedItems must contain a value for aKey if checkKey is true.
aItemInfos.AppendElements(mOrderedItems);
MOZ_ASSERT(aItemInfos.Length() == mValues.Count());
aNextLoadIndex = mValues.Count();
aAddKeyToUnknownItems = false;
aLoadState = LSSnapshot::LoadState::AllOrderedItems;
} else {
// We don't have enough snapshot budget to send all items, but we do have
// enough to send all of the keys and to make a best effort to populate as
// many values as possible. We send void string values once we run out of
// budget. A complicating factor is that we want to make sure that we send
// the value for aKey which is a localStorage read that's triggering this
// request. Since that key can happen anywhere in the list of items, we
// need to handle it specially.
//
// The loop is effectively doing 2 things in parallel:
//
// 1. Looking for the `aKey` to send. This is tracked by `checkKey`
// which is true if there was an `aKey` specified and until we
// populate its value, and false thereafter.
// 2. Sending values until we run out of `size` budget and switch to
// sending void values. `doneSendingValues` tracks when we've run out
// of size budget, with `setVoidValue` tracking whether a value
// should be sent for each turn of the event loop but can be
// overridden when `aKey` is found.
int64_t size = mSizeOfKeys;
nsString value;
bool setVoidValue = false;
bool doneSendingValues = false;
for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
const LSItemInfo& item = mOrderedItems[index];
const nsString& key = item.key();
const nsString& value = item.value();
if (!value.IsVoid()) {
value = item.value();
size += static_cast<int64_t>(value.Length());
if (size <= gSnapshotPrefill) {
aLoadedItems.PutEntry(key);
if (checkKey && key == aKey) {
checkKey = false;
setVoidValue = false;
} else if (!setVoidValue) {
if (doneSendingValues) {
setVoidValue = true;
} else {
value.SetIsVoid(true);
size += static_cast<int64_t>(value.Length());
// We set value to void so that will guard against entering the
// parent branch during next iterations. So aNextLoadIndex is set
// only once.
aNextLoadIndex = index;
if (size > gSnapshotPrefill) {
setVoidValue = true;
doneSendingValues = true;
// We set doneSendingValues to true and that will guard against
// entering this branch during next iterations. So aNextLoadIndex
// is set only once.
aNextLoadIndex = index;
}
}
}
LSItemInfo* itemInfo = aItemInfos.AppendElement();
itemInfo->key() = key;
itemInfo->value() = value;
if (setVoidValue) {
itemInfo->value().SetIsVoid(true);
} else {
aLoadedItems.PutEntry(key);
itemInfo->value() = value;
}
}
aAddKeyToUnknownItems = false;
aLoadState = LSSnapshot::LoadState::AllOrderedKeys;
}
} else {
@ -4716,12 +4775,16 @@ void Datastore::GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
const nsString& key = item.key();
const nsString& value = item.value();
size += static_cast<int64_t>(key.Length()) +
static_cast<int64_t>(value.Length());
if (checkKey && key == aKey) {
checkKey = false;
} else {
size += static_cast<int64_t>(key.Length()) +
static_cast<int64_t>(value.Length());
if (size > gSnapshotPrefill) {
aNextLoadIndex = index;
break;
if (size > gSnapshotPrefill) {
aNextLoadIndex = index;
break;
}
}
aLoadedItems.PutEntry(key);
@ -4731,6 +4794,18 @@ void Datastore::GetSnapshotInitInfo(nsTHashtable<nsStringHashKey>& aLoadedItems,
itemInfo->value() = value;
}
aAddKeyToUnknownItems = false;
if (!aKey.IsVoid()) {
if (value.IsVoid()) {
aAddKeyToUnknownItems = true;
} else if (checkKey) {
LSItemInfo* itemInfo = aItemInfos.AppendElement();
itemInfo->key() = aKey;
itemInfo->value() = value;
}
}
MOZ_ASSERT(aItemInfos.Length() < mOrderedItems.Length());
aLoadState = LSSnapshot::LoadState::Partial;
}
@ -5283,9 +5358,9 @@ mozilla::ipc::IPCResult Database::RecvAllowToClose() {
}
PBackgroundLSSnapshotParent* Database::AllocPBackgroundLSSnapshotParent(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) {
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
AssertIsOnBackgroundThread();
if (NS_WARN_IF(aIncreasePeakUsage && aRequestedSize <= 0)) {
@ -5311,8 +5386,9 @@ PBackgroundLSSnapshotParent* Database::AllocPBackgroundLSSnapshotParent(
mozilla::ipc::IPCResult Database::RecvPBackgroundLSSnapshotConstructor(
PBackgroundLSSnapshotParent* aActor, const nsString& aDocumentURI,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
const nsString& aKey, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) {
AssertIsOnBackgroundThread();
MOZ_ASSERT_IF(aIncreasePeakUsage, aRequestedSize > 0);
MOZ_ASSERT_IF(aIncreasePeakUsage, aMinSize > 0);
@ -5321,8 +5397,7 @@ mozilla::ipc::IPCResult Database::RecvPBackgroundLSSnapshotConstructor(
auto* snapshot = static_cast<Snapshot*>(aActor);
// TODO: This can be optimized depending on which operation triggers snapshot
// creation. For example clear() doesn't need to receive items at all.
bool addKeyToUnknownItems;
nsTHashtable<nsStringHashKey> loadedItems;
nsTArray<LSItemInfo> itemInfos;
uint32_t nextLoadIndex;
@ -5330,20 +5405,26 @@ mozilla::ipc::IPCResult Database::RecvPBackgroundLSSnapshotConstructor(
int64_t initialUsage;
int64_t peakUsage;
LSSnapshot::LoadState loadState;
mDatastore->GetSnapshotInitInfo(loadedItems, itemInfos, nextLoadIndex,
totalLength, initialUsage, peakUsage,
loadState);
mDatastore->GetSnapshotInitInfo(aKey, addKeyToUnknownItems, loadedItems,
itemInfos, nextLoadIndex, totalLength,
initialUsage, peakUsage, loadState);
nsTHashtable<nsStringHashKey> unknownItems;
if (addKeyToUnknownItems) {
unknownItems.PutEntry(aKey);
}
if (aIncreasePeakUsage) {
int64_t size = mDatastore->RequestUpdateUsage(aRequestedSize, aMinSize);
peakUsage += size;
}
snapshot->Init(loadedItems, nextLoadIndex, totalLength, initialUsage,
peakUsage, loadState);
snapshot->Init(loadedItems, unknownItems, nextLoadIndex, totalLength,
initialUsage, peakUsage, loadState);
RegisterSnapshot(snapshot);
aInitInfo->addKeyToUnknownItems() = addKeyToUnknownItems;
aInitInfo->itemInfos() = std::move(itemInfos);
aInitInfo->totalLength() = totalLength;
aInitInfo->initialUsage() = initialUsage;

Просмотреть файл

@ -120,7 +120,7 @@ nsresult LSDatabase::GetLength(LSObject* aObject, uint32_t* aResult) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -140,7 +140,7 @@ nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -160,7 +160,7 @@ nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -179,7 +179,7 @@ nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -200,7 +200,7 @@ nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -220,7 +220,7 @@ nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -239,7 +239,7 @@ nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -262,7 +262,7 @@ nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
return NS_ERROR_ALREADY_INITIALIZED;
}
nsresult rv = EnsureSnapshot(aObject, /* aExplicit */ true);
nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -290,7 +290,8 @@ nsresult LSDatabase::EndExplicitSnapshot(LSObject* aObject) {
return NS_OK;
}
nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
bool aExplicit) {
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT_IF(mSnapshot, !aExplicit);
@ -306,7 +307,7 @@ nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
LSSnapshotInitInfo initInfo;
bool ok = mActor->SendPBackgroundLSSnapshotConstructor(
actor, aObject->DocumentURI(),
actor, aObject->DocumentURI(), nsString(aKey),
/* increasePeakUsage */ true,
/* requestedSize */ 131072,
/* minSize */ 4096, &initInfo);
@ -317,7 +318,7 @@ nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
snapshot->SetActor(actor);
// This add refs snapshot.
nsresult rv = snapshot->Init(initInfo, aExplicit);
nsresult rv = snapshot->Init(aKey, initInfo, aExplicit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

Просмотреть файл

@ -79,7 +79,8 @@ class LSDatabase final {
private:
~LSDatabase();
nsresult EnsureSnapshot(LSObject* aObject, bool aExplicit = false);
nsresult EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
bool aExplicit = false);
void AllowToClose();
};

Просмотреть файл

@ -59,7 +59,8 @@ void LSSnapshot::SetActor(LSSnapshotChild* aActor) {
mActor = aActor;
}
nsresult LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
nsresult LSSnapshot::Init(const nsAString& aKey,
const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
AssertIsOnOwningThread();
MOZ_ASSERT(!mSelfRef);
MOZ_ASSERT(mActor);
@ -85,6 +86,9 @@ nsresult LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
}
if (loadState == LoadState::Partial) {
if (aInitInfo.addKeyToUnknownItems()) {
mUnknownItems.PutEntry(aKey);
}
mInitLength = aInitInfo.totalLength();
mLength = mInitLength;
} else if (loadState == LoadState::AllOrderedKeys) {

Просмотреть файл

@ -112,7 +112,8 @@ class LSSnapshot final : public nsIRunnable {
bool Explicit() const { return mExplicit; }
nsresult Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit);
nsresult Init(const nsAString& aKey, const LSSnapshotInitInfo& aInitInfo,
bool aExplicit);
nsresult GetLength(uint32_t* aResult);

Просмотреть файл

@ -22,6 +22,14 @@ namespace dom {
*/
struct LSSnapshotInitInfo
{
/**
* Boolean indicating whether the `key` provided as an argument to the
* PBackgroundLSSnapshot constructor did not exist in the Datastore and should
* be treated as an unknown and therefore undefined value. Note that `key` may
* have been provided as a void string, in which case this value is forced to
* be false.
*/
bool addKeyToUnknownItems;
/**
* As many key/value or key/void pairs as the snapshot prefill byte budget
* allowed.
@ -99,11 +107,20 @@ parent:
* consult any other threads or perform any I/O. Additionally, the response
* is explicitly bounded in size by the tunable snapshot prefill byte limit.
*
* @param key
* If key is non-void, then the snapshot is being triggered by a direct
* access to a localStorage key (get, set, or removal, with set/removal
* requiring the old value in order to properly populate the "storage"
* event), the key being requested. It's possible the key is not present in
* localStorage, in which case LSSnapshotInitInfo::addKeyToUnknownItems will
* be true indicating that there is no such key/value pair, otherwise it
* will be false.
* @param increasePeakUsage
* Whether the parent should attempt to pre-allocate some amount of quota
* usage to the Snapshot.
*/
sync PBackgroundLSSnapshot(nsString documentURI,
nsString key,
bool increasePeakUsage,
int64_t requestedSize,
int64_t minSize)