1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SessionStorageCache.h"
9 #include "LocalStorageManager.h"
10 #include "StorageIPC.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/dom/LSWriteOptimizer.h"
13 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
14 #include "nsDOMString.h"
19 void SSWriteOptimizer::Enumerate(nsTArray
<SSWriteInfo
>& aWriteInfos
) {
20 AssertIsOnOwningThread();
22 // The mWriteInfos hash table contains all write infos, but it keeps them in
23 // an arbitrary order, which means write infos need to be sorted before being
26 nsTArray
<NotNull
<WriteInfo
*>> writeInfos
;
27 GetSortedWriteInfos(writeInfos
);
29 for (const auto& writeInfo
: writeInfos
) {
30 switch (writeInfo
->GetType()) {
31 case WriteInfo::InsertItem
: {
32 const auto& insertItemInfo
=
33 static_cast<const InsertItemInfo
&>(*writeInfo
);
35 aWriteInfos
.AppendElement(
36 SSSetItemInfo
{nsString
{insertItemInfo
.GetKey()},
37 nsString
{insertItemInfo
.GetValue()}});
42 case WriteInfo::UpdateItem
: {
43 const auto& updateItemInfo
=
44 static_cast<const UpdateItemInfo
&>(*writeInfo
);
46 if (updateItemInfo
.UpdateWithMove()) {
47 // See the comment in LSWriteOptimizer::InsertItem for more details
48 // about the UpdateWithMove flag.
50 aWriteInfos
.AppendElement(
51 SSRemoveItemInfo
{nsString
{updateItemInfo
.GetKey()}});
54 aWriteInfos
.AppendElement(
55 SSSetItemInfo
{nsString
{updateItemInfo
.GetKey()},
56 nsString
{updateItemInfo
.GetValue()}});
61 case WriteInfo::DeleteItem
: {
62 const auto& deleteItemInfo
=
63 static_cast<const DeleteItemInfo
&>(*writeInfo
);
65 aWriteInfos
.AppendElement(
66 SSRemoveItemInfo
{nsString
{deleteItemInfo
.GetKey()}});
71 case WriteInfo::Truncate
: {
72 aWriteInfos
.AppendElement(SSClearInfo
{});
78 MOZ_CRASH("Bad type!");
83 SessionStorageCache::SessionStorageCache()
84 : mActor(nullptr), mLoadedOrCloned(false) {}
86 SessionStorageCache::~SessionStorageCache() {
88 mActor
->SendDeleteMeInternal();
89 MOZ_ASSERT(!mActor
, "SendDeleteMeInternal should have cleared!");
93 int64_t SessionStorageCache::GetOriginQuotaUsage() {
94 return mDataSet
.mOriginQuotaUsage
;
97 uint32_t SessionStorageCache::Length() { return mDataSet
.mKeys
.Count(); }
99 void SessionStorageCache::Key(uint32_t aIndex
, nsAString
& aResult
) {
100 aResult
.SetIsVoid(true);
101 for (auto iter
= mDataSet
.mKeys
.ConstIter(); !iter
.Done(); iter
.Next()) {
103 aResult
= iter
.Key();
110 void SessionStorageCache::GetItem(const nsAString
& aKey
, nsAString
& aResult
) {
111 // not using AutoString since we don't want to copy buffer to result
113 if (!mDataSet
.mKeys
.Get(aKey
, &value
)) {
114 SetDOMStringToNull(value
);
119 void SessionStorageCache::GetKeys(nsTArray
<nsString
>& aKeys
) {
120 AppendToArray(aKeys
, mDataSet
.mKeys
.Keys());
123 nsresult
SessionStorageCache::SetItem(const nsAString
& aKey
,
124 const nsAString
& aValue
,
126 bool aRecordWriteInfo
) {
129 if (!mDataSet
.mKeys
.Get(aKey
, &aOldValue
)) {
130 SetDOMStringToNull(aOldValue
);
132 // We only consider key size if the key doesn't exist before.
133 delta
= static_cast<int64_t>(aKey
.Length());
136 delta
+= static_cast<int64_t>(aValue
.Length()) -
137 static_cast<int64_t>(aOldValue
.Length());
139 if (aValue
== aOldValue
&&
140 DOMStringIsNull(aValue
) == DOMStringIsNull(aOldValue
)) {
141 return NS_SUCCESS_DOM_NO_OPERATION
;
144 if (!mDataSet
.ProcessUsageDelta(delta
)) {
145 return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR
;
148 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
149 if (DOMStringIsNull(aOldValue
)) {
150 mDataSet
.mWriteOptimizer
.InsertItem(aKey
, aValue
);
152 mDataSet
.mWriteOptimizer
.UpdateItem(aKey
, aValue
);
156 mDataSet
.mKeys
.InsertOrUpdate(aKey
, nsString(aValue
));
160 nsresult
SessionStorageCache::RemoveItem(const nsAString
& aKey
,
162 bool aRecordWriteInfo
) {
163 if (!mDataSet
.mKeys
.Get(aKey
, &aOldValue
)) {
164 return NS_SUCCESS_DOM_NO_OPERATION
;
167 // Recalculate the cached data size
168 mDataSet
.ProcessUsageDelta(-(static_cast<int64_t>(aOldValue
.Length()) +
169 static_cast<int64_t>(aKey
.Length())));
171 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
172 mDataSet
.mWriteOptimizer
.DeleteItem(aKey
);
175 mDataSet
.mKeys
.Remove(aKey
);
179 void SessionStorageCache::Clear(bool aByUserInteraction
,
180 bool aRecordWriteInfo
) {
181 mDataSet
.ProcessUsageDelta(-mDataSet
.mOriginQuotaUsage
);
183 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
184 mDataSet
.mWriteOptimizer
.Truncate();
187 mDataSet
.mKeys
.Clear();
190 void SessionStorageCache::ResetWriteInfos() {
191 mDataSet
.mWriteOptimizer
.Reset();
194 already_AddRefed
<SessionStorageCache
> SessionStorageCache::Clone() const {
195 RefPtr
<SessionStorageCache
> cache
= new SessionStorageCache();
197 cache
->mDataSet
.mOriginQuotaUsage
= mDataSet
.mOriginQuotaUsage
;
198 for (const auto& keyEntry
: mDataSet
.mKeys
) {
199 cache
->mDataSet
.mKeys
.InsertOrUpdate(keyEntry
.GetKey(), keyEntry
.GetData());
200 cache
->mDataSet
.mWriteOptimizer
.InsertItem(keyEntry
.GetKey(),
204 return cache
.forget();
207 nsTArray
<SSSetItemInfo
> SessionStorageCache::SerializeData() {
208 nsTArray
<SSSetItemInfo
> data
;
209 for (const auto& keyEntry
: mDataSet
.mKeys
) {
210 data
.EmplaceBack(nsString
{keyEntry
.GetKey()}, keyEntry
.GetData());
215 nsTArray
<SSWriteInfo
> SessionStorageCache::SerializeWriteInfos() {
216 nsTArray
<SSWriteInfo
> writeInfos
;
217 mDataSet
.mWriteOptimizer
.Enumerate(writeInfos
);
221 void SessionStorageCache::DeserializeData(
222 const nsTArray
<SSSetItemInfo
>& aData
) {
223 Clear(false, /* aRecordWriteInfo */ false);
224 for (const auto& keyValuePair
: aData
) {
226 SetItem(keyValuePair
.key(), keyValuePair
.value(), oldValue
, false);
230 void SessionStorageCache::DeserializeWriteInfos(
231 const nsTArray
<SSWriteInfo
>& aInfos
) {
232 for (const auto& writeInfo
: aInfos
) {
233 switch (writeInfo
.type()) {
234 case SSWriteInfo::TSSSetItemInfo
: {
235 const SSSetItemInfo
& info
= writeInfo
.get_SSSetItemInfo();
238 SetItem(info
.key(), info
.value(), oldValue
,
239 /* aRecordWriteInfo */ false);
244 case SSWriteInfo::TSSRemoveItemInfo
: {
245 const SSRemoveItemInfo
& info
= writeInfo
.get_SSRemoveItemInfo();
248 RemoveItem(info
.key(), oldValue
,
249 /* aRecordWriteInfo */ false);
254 case SSWriteInfo::TSSClearInfo
: {
255 Clear(false, /* aRecordWriteInfo */ false);
261 MOZ_CRASH("Should never get here!");
266 void SessionStorageCache::SetActor(SessionStorageCacheChild
* aActor
) {
267 AssertIsOnMainThread();
274 void SessionStorageCache::ClearActor() {
275 AssertIsOnMainThread();
281 bool SessionStorageCache::DataSet::ProcessUsageDelta(int64_t aDelta
) {
282 // Check limit per this origin
283 uint64_t newOriginUsage
= mOriginQuotaUsage
+ aDelta
;
284 if (aDelta
> 0 && newOriginUsage
> LocalStorageManager::GetOriginQuota()) {
288 // Update size in our data set
289 mOriginQuotaUsage
= newOriginUsage
;
294 } // namespace mozilla