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"
16 namespace mozilla::dom
{
18 void SSWriteOptimizer::Enumerate(nsTArray
<SSWriteInfo
>& aWriteInfos
) {
19 AssertIsOnOwningThread();
21 // The mWriteInfos hash table contains all write infos, but it keeps them in
22 // an arbitrary order, which means write infos need to be sorted before being
25 nsTArray
<NotNull
<WriteInfo
*>> writeInfos
;
26 GetSortedWriteInfos(writeInfos
);
28 for (const auto& writeInfo
: writeInfos
) {
29 switch (writeInfo
->GetType()) {
30 case WriteInfo::InsertItem
: {
31 const auto& insertItemInfo
=
32 static_cast<const InsertItemInfo
&>(*writeInfo
);
34 aWriteInfos
.AppendElement(
35 SSSetItemInfo
{nsString
{insertItemInfo
.GetKey()},
36 nsString
{insertItemInfo
.GetValue()}});
41 case WriteInfo::UpdateItem
: {
42 const auto& updateItemInfo
=
43 static_cast<const UpdateItemInfo
&>(*writeInfo
);
45 if (updateItemInfo
.UpdateWithMove()) {
46 // See the comment in LSWriteOptimizer::InsertItem for more details
47 // about the UpdateWithMove flag.
49 aWriteInfos
.AppendElement(
50 SSRemoveItemInfo
{nsString
{updateItemInfo
.GetKey()}});
53 aWriteInfos
.AppendElement(
54 SSSetItemInfo
{nsString
{updateItemInfo
.GetKey()},
55 nsString
{updateItemInfo
.GetValue()}});
60 case WriteInfo::DeleteItem
: {
61 const auto& deleteItemInfo
=
62 static_cast<const DeleteItemInfo
&>(*writeInfo
);
64 aWriteInfos
.AppendElement(
65 SSRemoveItemInfo
{nsString
{deleteItemInfo
.GetKey()}});
70 case WriteInfo::Truncate
: {
71 aWriteInfos
.AppendElement(SSClearInfo
{});
77 MOZ_CRASH("Bad type!");
82 SessionStorageCache::SessionStorageCache()
83 : mActor(nullptr), mLoadedOrCloned(false) {}
85 SessionStorageCache::~SessionStorageCache() {
87 mActor
->SendDeleteMeInternal();
88 MOZ_ASSERT(!mActor
, "SendDeleteMeInternal should have cleared!");
92 int64_t SessionStorageCache::GetOriginQuotaUsage() {
93 return mDataSet
.mOriginQuotaUsage
;
96 uint32_t SessionStorageCache::Length() { return mDataSet
.mKeys
.Count(); }
98 void SessionStorageCache::Key(uint32_t aIndex
, nsAString
& aResult
) {
99 aResult
.SetIsVoid(true);
100 for (auto iter
= mDataSet
.mKeys
.ConstIter(); !iter
.Done(); iter
.Next()) {
102 aResult
= iter
.Key();
109 void SessionStorageCache::GetItem(const nsAString
& aKey
, nsAString
& aResult
) {
110 // not using AutoString since we don't want to copy buffer to result
112 if (!mDataSet
.mKeys
.Get(aKey
, &value
)) {
113 SetDOMStringToNull(value
);
118 void SessionStorageCache::GetKeys(nsTArray
<nsString
>& aKeys
) {
119 AppendToArray(aKeys
, mDataSet
.mKeys
.Keys());
122 nsresult
SessionStorageCache::SetItem(const nsAString
& aKey
,
123 const nsAString
& aValue
,
125 bool aRecordWriteInfo
) {
128 if (!mDataSet
.mKeys
.Get(aKey
, &aOldValue
)) {
129 SetDOMStringToNull(aOldValue
);
131 // We only consider key size if the key doesn't exist before.
132 delta
= static_cast<int64_t>(aKey
.Length());
135 delta
+= static_cast<int64_t>(aValue
.Length()) -
136 static_cast<int64_t>(aOldValue
.Length());
138 if (aValue
== aOldValue
&&
139 DOMStringIsNull(aValue
) == DOMStringIsNull(aOldValue
)) {
140 return NS_SUCCESS_DOM_NO_OPERATION
;
143 if (!mDataSet
.ProcessUsageDelta(delta
)) {
144 return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR
;
147 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
148 if (DOMStringIsNull(aOldValue
)) {
149 mDataSet
.mWriteOptimizer
.InsertItem(aKey
, aValue
);
151 mDataSet
.mWriteOptimizer
.UpdateItem(aKey
, aValue
);
155 mDataSet
.mKeys
.InsertOrUpdate(aKey
, nsString(aValue
));
159 nsresult
SessionStorageCache::RemoveItem(const nsAString
& aKey
,
161 bool aRecordWriteInfo
) {
162 if (!mDataSet
.mKeys
.Get(aKey
, &aOldValue
)) {
163 return NS_SUCCESS_DOM_NO_OPERATION
;
166 // Recalculate the cached data size
167 mDataSet
.ProcessUsageDelta(-(static_cast<int64_t>(aOldValue
.Length()) +
168 static_cast<int64_t>(aKey
.Length())));
170 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
171 mDataSet
.mWriteOptimizer
.DeleteItem(aKey
);
174 mDataSet
.mKeys
.Remove(aKey
);
178 void SessionStorageCache::Clear(bool aByUserInteraction
,
179 bool aRecordWriteInfo
) {
180 mDataSet
.ProcessUsageDelta(-mDataSet
.mOriginQuotaUsage
);
182 if (aRecordWriteInfo
&& XRE_IsContentProcess()) {
183 mDataSet
.mWriteOptimizer
.Truncate();
186 mDataSet
.mKeys
.Clear();
189 void SessionStorageCache::ResetWriteInfos() {
190 mDataSet
.mWriteOptimizer
.Reset();
193 already_AddRefed
<SessionStorageCache
> SessionStorageCache::Clone() const {
194 RefPtr
<SessionStorageCache
> cache
= new SessionStorageCache();
196 cache
->mDataSet
.mOriginQuotaUsage
= mDataSet
.mOriginQuotaUsage
;
197 for (const auto& keyEntry
: mDataSet
.mKeys
) {
198 cache
->mDataSet
.mKeys
.InsertOrUpdate(keyEntry
.GetKey(), keyEntry
.GetData());
199 cache
->mDataSet
.mWriteOptimizer
.InsertItem(keyEntry
.GetKey(),
203 return cache
.forget();
206 nsTArray
<SSSetItemInfo
> SessionStorageCache::SerializeData() {
207 nsTArray
<SSSetItemInfo
> data
;
208 for (const auto& keyEntry
: mDataSet
.mKeys
) {
209 data
.EmplaceBack(nsString
{keyEntry
.GetKey()}, keyEntry
.GetData());
214 nsTArray
<SSWriteInfo
> SessionStorageCache::SerializeWriteInfos() {
215 nsTArray
<SSWriteInfo
> writeInfos
;
216 mDataSet
.mWriteOptimizer
.Enumerate(writeInfos
);
220 void SessionStorageCache::DeserializeData(
221 const nsTArray
<SSSetItemInfo
>& aData
) {
222 Clear(false, /* aRecordWriteInfo */ false);
223 for (const auto& keyValuePair
: aData
) {
225 SetItem(keyValuePair
.key(), keyValuePair
.value(), oldValue
, false);
229 void SessionStorageCache::DeserializeWriteInfos(
230 const nsTArray
<SSWriteInfo
>& aInfos
) {
231 for (const auto& writeInfo
: aInfos
) {
232 switch (writeInfo
.type()) {
233 case SSWriteInfo::TSSSetItemInfo
: {
234 const SSSetItemInfo
& info
= writeInfo
.get_SSSetItemInfo();
237 SetItem(info
.key(), info
.value(), oldValue
,
238 /* aRecordWriteInfo */ false);
243 case SSWriteInfo::TSSRemoveItemInfo
: {
244 const SSRemoveItemInfo
& info
= writeInfo
.get_SSRemoveItemInfo();
247 RemoveItem(info
.key(), oldValue
,
248 /* aRecordWriteInfo */ false);
253 case SSWriteInfo::TSSClearInfo
: {
254 Clear(false, /* aRecordWriteInfo */ false);
260 MOZ_CRASH("Should never get here!");
265 void SessionStorageCache::SetActor(SessionStorageCacheChild
* aActor
) {
266 AssertIsOnMainThread();
273 void SessionStorageCache::ClearActor() {
274 AssertIsOnMainThread();
280 bool SessionStorageCache::DataSet::ProcessUsageDelta(int64_t aDelta
) {
281 // Check limit per this origin
282 uint64_t newOriginUsage
= mOriginQuotaUsage
+ aDelta
;
283 if (aDelta
> 0 && newOriginUsage
> LocalStorageManager::GetOriginQuota()) {
287 // Update size in our data set
288 mOriginQuotaUsage
= newOriginUsage
;
292 } // namespace mozilla::dom