summaryrefslogtreecommitdiffstats
path: root/dom/storage/SessionStorageCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/storage/SessionStorageCache.cpp')
-rw-r--r--dom/storage/SessionStorageCache.cpp333
1 files changed, 333 insertions, 0 deletions
diff --git a/dom/storage/SessionStorageCache.cpp b/dom/storage/SessionStorageCache.cpp
new file mode 100644
index 0000000000..0e6d5aac16
--- /dev/null
+++ b/dom/storage/SessionStorageCache.cpp
@@ -0,0 +1,333 @@
+/* -*- 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 "SessionStorageCache.h"
+
+#include "LocalStorageManager.h"
+#include "StorageIPC.h"
+#include "mozilla/dom/LSWriteOptimizer.h"
+#include "mozilla/dom/PBackgroundSessionStorageCache.h"
+#include "nsDOMString.h"
+
+namespace mozilla {
+namespace dom {
+
+void SSWriteOptimizer::Enumerate(nsTArray<SSWriteInfo>& aWriteInfos) {
+ AssertIsOnOwningThread();
+
+ // The mWriteInfos hash table contains all write infos, but it keeps them in
+ // an arbitrary order, which means write infos need to be sorted before being
+ // processed.
+
+ nsTArray<NotNull<WriteInfo*>> writeInfos;
+ GetSortedWriteInfos(writeInfos);
+
+ for (const auto& writeInfo : writeInfos) {
+ switch (writeInfo->GetType()) {
+ case WriteInfo::InsertItem: {
+ const auto& insertItemInfo =
+ static_cast<const InsertItemInfo&>(*writeInfo);
+
+ aWriteInfos.AppendElement(
+ SSSetItemInfo{nsString{insertItemInfo.GetKey()},
+ nsString{insertItemInfo.GetValue()}});
+
+ break;
+ }
+
+ case WriteInfo::UpdateItem: {
+ const auto& updateItemInfo =
+ static_cast<const UpdateItemInfo&>(*writeInfo);
+
+ if (updateItemInfo.UpdateWithMove()) {
+ // See the comment in LSWriteOptimizer::InsertItem for more details
+ // about the UpdateWithMove flag.
+
+ aWriteInfos.AppendElement(
+ SSRemoveItemInfo{nsString{updateItemInfo.GetKey()}});
+ }
+
+ aWriteInfos.AppendElement(
+ SSSetItemInfo{nsString{updateItemInfo.GetKey()},
+ nsString{updateItemInfo.GetValue()}});
+
+ break;
+ }
+
+ case WriteInfo::DeleteItem: {
+ const auto& deleteItemInfo =
+ static_cast<const DeleteItemInfo&>(*writeInfo);
+
+ aWriteInfos.AppendElement(
+ SSRemoveItemInfo{nsString{deleteItemInfo.GetKey()}});
+
+ break;
+ }
+
+ case WriteInfo::Truncate: {
+ aWriteInfos.AppendElement(SSClearInfo{});
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Bad type!");
+ }
+ }
+}
+
+SessionStorageCache::SessionStorageCache()
+ : mActor(nullptr), mLoadedOrCloned(false) {}
+
+SessionStorageCache::~SessionStorageCache() {
+ if (mActor) {
+ mActor->SendDeleteMeInternal();
+ MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
+ }
+}
+
+SessionStorageCache::DataSet* SessionStorageCache::Set(
+ DataSetType aDataSetType) {
+ if (aDataSetType == eDefaultSetType) {
+ return &mDefaultSet;
+ }
+
+ MOZ_ASSERT(aDataSetType == eSessionSetType);
+
+ return &mSessionSet;
+}
+
+int64_t SessionStorageCache::GetOriginQuotaUsage(DataSetType aDataSetType) {
+ return Set(aDataSetType)->mOriginQuotaUsage;
+}
+
+uint32_t SessionStorageCache::Length(DataSetType aDataSetType) {
+ return Set(aDataSetType)->mKeys.Count();
+}
+
+void SessionStorageCache::Key(DataSetType aDataSetType, uint32_t aIndex,
+ nsAString& aResult) {
+ aResult.SetIsVoid(true);
+ for (auto iter = Set(aDataSetType)->mKeys.Iter(); !iter.Done(); iter.Next()) {
+ if (aIndex == 0) {
+ aResult = iter.Key();
+ return;
+ }
+ aIndex--;
+ }
+}
+
+void SessionStorageCache::GetItem(DataSetType aDataSetType,
+ const nsAString& aKey, nsAString& aResult) {
+ // not using AutoString since we don't want to copy buffer to result
+ nsString value;
+ if (!Set(aDataSetType)->mKeys.Get(aKey, &value)) {
+ SetDOMStringToNull(value);
+ }
+ aResult = value;
+}
+
+void SessionStorageCache::GetKeys(DataSetType aDataSetType,
+ nsTArray<nsString>& aKeys) {
+ for (auto iter = Set(aDataSetType)->mKeys.Iter(); !iter.Done(); iter.Next()) {
+ aKeys.AppendElement(iter.Key());
+ }
+}
+
+nsresult SessionStorageCache::SetItem(DataSetType aDataSetType,
+ const nsAString& aKey,
+ const nsAString& aValue,
+ nsString& aOldValue,
+ bool aRecordWriteInfo) {
+ int64_t delta = 0;
+ DataSet* dataSet = Set(aDataSetType);
+ MOZ_ASSERT(dataSet);
+
+ if (!dataSet->mKeys.Get(aKey, &aOldValue)) {
+ SetDOMStringToNull(aOldValue);
+
+ // We only consider key size if the key doesn't exist before.
+ delta = static_cast<int64_t>(aKey.Length());
+ }
+
+ delta += static_cast<int64_t>(aValue.Length()) -
+ static_cast<int64_t>(aOldValue.Length());
+
+ if (aValue == aOldValue &&
+ DOMStringIsNull(aValue) == DOMStringIsNull(aOldValue)) {
+ return NS_SUCCESS_DOM_NO_OPERATION;
+ }
+
+ if (!dataSet->ProcessUsageDelta(delta)) {
+ return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
+ }
+
+ if (aRecordWriteInfo && XRE_IsContentProcess()) {
+ if (DOMStringIsNull(aOldValue)) {
+ dataSet->mWriteOptimizer.InsertItem(aKey, aValue);
+ } else {
+ dataSet->mWriteOptimizer.UpdateItem(aKey, aValue);
+ }
+ }
+
+ dataSet->mKeys.Put(aKey, nsString(aValue));
+ return NS_OK;
+}
+
+nsresult SessionStorageCache::RemoveItem(DataSetType aDataSetType,
+ const nsAString& aKey,
+ nsString& aOldValue,
+ bool aRecordWriteInfo) {
+ DataSet* dataSet = Set(aDataSetType);
+ MOZ_ASSERT(dataSet);
+
+ if (!dataSet->mKeys.Get(aKey, &aOldValue)) {
+ return NS_SUCCESS_DOM_NO_OPERATION;
+ }
+
+ // Recalculate the cached data size
+ dataSet->ProcessUsageDelta(-(static_cast<int64_t>(aOldValue.Length()) +
+ static_cast<int64_t>(aKey.Length())));
+
+ if (aRecordWriteInfo && XRE_IsContentProcess()) {
+ dataSet->mWriteOptimizer.DeleteItem(aKey);
+ }
+
+ dataSet->mKeys.Remove(aKey);
+ return NS_OK;
+}
+
+void SessionStorageCache::Clear(DataSetType aDataSetType,
+ bool aByUserInteraction,
+ bool aRecordWriteInfo) {
+ DataSet* dataSet = Set(aDataSetType);
+ MOZ_ASSERT(dataSet);
+
+ dataSet->ProcessUsageDelta(-dataSet->mOriginQuotaUsage);
+
+ if (aRecordWriteInfo && XRE_IsContentProcess()) {
+ dataSet->mWriteOptimizer.Truncate();
+ }
+
+ dataSet->mKeys.Clear();
+}
+
+void SessionStorageCache::ResetWriteInfos(DataSetType aDataSetType) {
+ Set(aDataSetType)->mWriteOptimizer.Reset();
+}
+
+already_AddRefed<SessionStorageCache> SessionStorageCache::Clone() const {
+ RefPtr<SessionStorageCache> cache = new SessionStorageCache();
+
+ cache->mDefaultSet.mOriginQuotaUsage = mDefaultSet.mOriginQuotaUsage;
+ for (auto iter = mDefaultSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
+ cache->mDefaultSet.mKeys.Put(iter.Key(), iter.Data());
+ cache->mDefaultSet.mWriteOptimizer.InsertItem(iter.Key(), iter.Data());
+ }
+
+ cache->mSessionSet.mOriginQuotaUsage = mSessionSet.mOriginQuotaUsage;
+ for (auto iter = mSessionSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
+ cache->mSessionSet.mKeys.Put(iter.Key(), iter.Data());
+ cache->mSessionSet.mWriteOptimizer.InsertItem(iter.Key(), iter.Data());
+ }
+
+ return cache.forget();
+}
+
+nsTArray<SSSetItemInfo> SessionStorageCache::SerializeData(
+ DataSetType aDataSetType) {
+ nsTArray<SSSetItemInfo> data;
+ for (auto iter = Set(aDataSetType)->mKeys.Iter(); !iter.Done(); iter.Next()) {
+ SSSetItemInfo keyValuePair;
+ keyValuePair.key() = iter.Key();
+ keyValuePair.value() = iter.Data();
+ data.EmplaceBack(std::move(keyValuePair));
+ }
+ return data;
+}
+
+nsTArray<SSWriteInfo> SessionStorageCache::SerializeWriteInfos(
+ DataSetType aDataSetType) {
+ nsTArray<SSWriteInfo> writeInfos;
+ Set(aDataSetType)->mWriteOptimizer.Enumerate(writeInfos);
+ return writeInfos;
+}
+
+void SessionStorageCache::DeserializeData(
+ DataSetType aDataSetType, const nsTArray<SSSetItemInfo>& aData) {
+ Clear(aDataSetType, false, /* aRecordWriteInfo */ false);
+ for (const auto& keyValuePair : aData) {
+ nsString oldValue;
+ SetItem(aDataSetType, keyValuePair.key(), keyValuePair.value(), oldValue,
+ false);
+ }
+}
+
+void SessionStorageCache::DeserializeWriteInfos(
+ DataSetType aDataSetType, const nsTArray<SSWriteInfo>& aInfos) {
+ for (const auto& writeInfo : aInfos) {
+ switch (writeInfo.type()) {
+ case SSWriteInfo::TSSSetItemInfo: {
+ const SSSetItemInfo& info = writeInfo.get_SSSetItemInfo();
+
+ nsString oldValue;
+ SetItem(aDataSetType, info.key(), info.value(), oldValue,
+ /* aRecordWriteInfo */ false);
+
+ break;
+ }
+
+ case SSWriteInfo::TSSRemoveItemInfo: {
+ const SSRemoveItemInfo& info = writeInfo.get_SSRemoveItemInfo();
+
+ nsString oldValue;
+ RemoveItem(aDataSetType, info.key(), oldValue,
+ /* aRecordWriteInfo */ false);
+
+ break;
+ }
+
+ case SSWriteInfo::TSSClearInfo: {
+ Clear(aDataSetType, false, /* aRecordWriteInfo */ false);
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+ }
+}
+
+void SessionStorageCache::SetActor(SessionStorageCacheChild* aActor) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(!mActor);
+
+ mActor = aActor;
+}
+
+void SessionStorageCache::ClearActor() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mActor);
+
+ mActor = nullptr;
+}
+
+bool SessionStorageCache::DataSet::ProcessUsageDelta(int64_t aDelta) {
+ // Check limit per this origin
+ uint64_t newOriginUsage = mOriginQuotaUsage + aDelta;
+ if (aDelta > 0 && newOriginUsage > LocalStorageManager::GetOriginQuota()) {
+ return false;
+ }
+
+ // Update size in our data set
+ mOriginQuotaUsage = newOriginUsage;
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla