Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / storage / SessionStorageCache.cpp
blob40f29ebc26c18edee38cf3c511c249924e0daa32
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
23 // processed.
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()}});
38 break;
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()}});
57 break;
60 case WriteInfo::DeleteItem: {
61 const auto& deleteItemInfo =
62 static_cast<const DeleteItemInfo&>(*writeInfo);
64 aWriteInfos.AppendElement(
65 SSRemoveItemInfo{nsString{deleteItemInfo.GetKey()}});
67 break;
70 case WriteInfo::Truncate: {
71 aWriteInfos.AppendElement(SSClearInfo{});
73 break;
76 default:
77 MOZ_CRASH("Bad type!");
82 SessionStorageCache::SessionStorageCache()
83 : mActor(nullptr), mLoadedOrCloned(false) {}
85 SessionStorageCache::~SessionStorageCache() {
86 if (mActor) {
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()) {
101 if (aIndex == 0) {
102 aResult = iter.Key();
103 return;
105 aIndex--;
109 void SessionStorageCache::GetItem(const nsAString& aKey, nsAString& aResult) {
110 // not using AutoString since we don't want to copy buffer to result
111 nsString value;
112 if (!mDataSet.mKeys.Get(aKey, &value)) {
113 SetDOMStringToNull(value);
115 aResult = 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,
124 nsString& aOldValue,
125 bool aRecordWriteInfo) {
126 int64_t delta = 0;
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);
150 } else {
151 mDataSet.mWriteOptimizer.UpdateItem(aKey, aValue);
155 mDataSet.mKeys.InsertOrUpdate(aKey, nsString(aValue));
156 return NS_OK;
159 nsresult SessionStorageCache::RemoveItem(const nsAString& aKey,
160 nsString& aOldValue,
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);
175 return NS_OK;
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(),
200 keyEntry.GetData());
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());
211 return data;
214 nsTArray<SSWriteInfo> SessionStorageCache::SerializeWriteInfos() {
215 nsTArray<SSWriteInfo> writeInfos;
216 mDataSet.mWriteOptimizer.Enumerate(writeInfos);
217 return writeInfos;
220 void SessionStorageCache::DeserializeData(
221 const nsTArray<SSSetItemInfo>& aData) {
222 Clear(false, /* aRecordWriteInfo */ false);
223 for (const auto& keyValuePair : aData) {
224 nsString oldValue;
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();
236 nsString oldValue;
237 SetItem(info.key(), info.value(), oldValue,
238 /* aRecordWriteInfo */ false);
240 break;
243 case SSWriteInfo::TSSRemoveItemInfo: {
244 const SSRemoveItemInfo& info = writeInfo.get_SSRemoveItemInfo();
246 nsString oldValue;
247 RemoveItem(info.key(), oldValue,
248 /* aRecordWriteInfo */ false);
250 break;
253 case SSWriteInfo::TSSClearInfo: {
254 Clear(false, /* aRecordWriteInfo */ false);
256 break;
259 default:
260 MOZ_CRASH("Should never get here!");
265 void SessionStorageCache::SetActor(SessionStorageCacheChild* aActor) {
266 AssertIsOnMainThread();
267 MOZ_ASSERT(aActor);
268 MOZ_ASSERT(!mActor);
270 mActor = aActor;
273 void SessionStorageCache::ClearActor() {
274 AssertIsOnMainThread();
275 MOZ_ASSERT(mActor);
277 mActor = nullptr;
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()) {
284 return false;
287 // Update size in our data set
288 mOriginQuotaUsage = newOriginUsage;
289 return true;
292 } // namespace mozilla::dom