SessionStorageCache.cpp (8319B)
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/. */ 6 7 #include "SessionStorageCache.h" 8 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" 15 16 namespace mozilla::dom { 17 18 void SSWriteOptimizer::Enumerate(nsTArray<SSWriteInfo>& aWriteInfos) { 19 AssertIsOnOwningThread(); 20 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. 24 25 nsTArray<NotNull<WriteInfo*>> writeInfos; 26 GetSortedWriteInfos(writeInfos); 27 28 for (const auto& writeInfo : writeInfos) { 29 switch (writeInfo->GetType()) { 30 case WriteInfo::InsertItem: { 31 const auto& insertItemInfo = 32 static_cast<const InsertItemInfo&>(*writeInfo); 33 34 aWriteInfos.AppendElement( 35 SSSetItemInfo{nsString{insertItemInfo.GetKey()}, 36 nsString{insertItemInfo.GetValue()}}); 37 38 break; 39 } 40 41 case WriteInfo::UpdateItem: { 42 const auto& updateItemInfo = 43 static_cast<const UpdateItemInfo&>(*writeInfo); 44 45 if (updateItemInfo.UpdateWithMove()) { 46 // See the comment in LSWriteOptimizer::InsertItem for more details 47 // about the UpdateWithMove flag. 48 49 aWriteInfos.AppendElement( 50 SSRemoveItemInfo{nsString{updateItemInfo.GetKey()}}); 51 } 52 53 aWriteInfos.AppendElement( 54 SSSetItemInfo{nsString{updateItemInfo.GetKey()}, 55 nsString{updateItemInfo.GetValue()}}); 56 57 break; 58 } 59 60 case WriteInfo::DeleteItem: { 61 const auto& deleteItemInfo = 62 static_cast<const DeleteItemInfo&>(*writeInfo); 63 64 aWriteInfos.AppendElement( 65 SSRemoveItemInfo{nsString{deleteItemInfo.GetKey()}}); 66 67 break; 68 } 69 70 case WriteInfo::Truncate: { 71 aWriteInfos.AppendElement(SSClearInfo{}); 72 73 break; 74 } 75 76 default: 77 MOZ_CRASH("Bad type!"); 78 } 79 } 80 } 81 82 SessionStorageCache::SessionStorageCache() 83 : mActor(nullptr), mLoadedOrCloned(false) {} 84 85 SessionStorageCache::~SessionStorageCache() { 86 if (mActor) { 87 mActor->SendDeleteMeInternal(); 88 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!"); 89 } 90 } 91 92 int64_t SessionStorageCache::GetOriginQuotaUsage() { 93 return mDataSet.mOriginQuotaUsage; 94 } 95 96 uint32_t SessionStorageCache::Length() { return mDataSet.mKeys.Count(); } 97 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; 104 } 105 aIndex--; 106 } 107 } 108 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); 114 } 115 aResult = value; 116 } 117 118 void SessionStorageCache::GetKeys(nsTArray<nsString>& aKeys) { 119 AppendToArray(aKeys, mDataSet.mKeys.Keys()); 120 } 121 122 nsresult SessionStorageCache::SetItem(const nsAString& aKey, 123 const nsAString& aValue, 124 nsString& aOldValue, 125 bool aRecordWriteInfo) { 126 int64_t delta = 0; 127 128 if (!mDataSet.mKeys.Get(aKey, &aOldValue)) { 129 SetDOMStringToNull(aOldValue); 130 131 // We only consider key size if the key doesn't exist before. 132 delta = static_cast<int64_t>(aKey.Length()); 133 } 134 135 delta += static_cast<int64_t>(aValue.Length()) - 136 static_cast<int64_t>(aOldValue.Length()); 137 138 if (aValue == aOldValue && 139 DOMStringIsNull(aValue) == DOMStringIsNull(aOldValue)) { 140 return NS_SUCCESS_DOM_NO_OPERATION; 141 } 142 143 if (!mDataSet.ProcessUsageDelta(delta)) { 144 return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR; 145 } 146 147 if (aRecordWriteInfo && XRE_IsContentProcess()) { 148 if (DOMStringIsNull(aOldValue)) { 149 mDataSet.mWriteOptimizer.InsertItem(aKey, aValue); 150 } else { 151 mDataSet.mWriteOptimizer.UpdateItem(aKey, aValue); 152 } 153 } 154 155 mDataSet.mKeys.InsertOrUpdate(aKey, nsString(aValue)); 156 return NS_OK; 157 } 158 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; 164 } 165 166 // Recalculate the cached data size 167 mDataSet.ProcessUsageDelta(-(static_cast<int64_t>(aOldValue.Length()) + 168 static_cast<int64_t>(aKey.Length()))); 169 170 if (aRecordWriteInfo && XRE_IsContentProcess()) { 171 mDataSet.mWriteOptimizer.DeleteItem(aKey); 172 } 173 174 mDataSet.mKeys.Remove(aKey); 175 return NS_OK; 176 } 177 178 void SessionStorageCache::Clear(bool aByUserInteraction, 179 bool aRecordWriteInfo) { 180 mDataSet.ProcessUsageDelta(-mDataSet.mOriginQuotaUsage); 181 182 if (aRecordWriteInfo && XRE_IsContentProcess()) { 183 mDataSet.mWriteOptimizer.Truncate(); 184 } 185 186 mDataSet.mKeys.Clear(); 187 } 188 189 void SessionStorageCache::ResetWriteInfos() { 190 mDataSet.mWriteOptimizer.Reset(); 191 } 192 193 already_AddRefed<SessionStorageCache> SessionStorageCache::Clone() const { 194 RefPtr<SessionStorageCache> cache = new SessionStorageCache(); 195 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()); 201 } 202 203 return cache.forget(); 204 } 205 206 nsTArray<SSSetItemInfo> SessionStorageCache::SerializeData() { 207 nsTArray<SSSetItemInfo> data; 208 for (const auto& keyEntry : mDataSet.mKeys) { 209 data.EmplaceBack(nsString{keyEntry.GetKey()}, keyEntry.GetData()); 210 } 211 return data; 212 } 213 214 nsTArray<SSWriteInfo> SessionStorageCache::SerializeWriteInfos() { 215 nsTArray<SSWriteInfo> writeInfos; 216 mDataSet.mWriteOptimizer.Enumerate(writeInfos); 217 return writeInfos; 218 } 219 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); 226 } 227 } 228 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(); 235 236 nsString oldValue; 237 SetItem(info.key(), info.value(), oldValue, 238 /* aRecordWriteInfo */ false); 239 240 break; 241 } 242 243 case SSWriteInfo::TSSRemoveItemInfo: { 244 const SSRemoveItemInfo& info = writeInfo.get_SSRemoveItemInfo(); 245 246 nsString oldValue; 247 RemoveItem(info.key(), oldValue, 248 /* aRecordWriteInfo */ false); 249 250 break; 251 } 252 253 case SSWriteInfo::TSSClearInfo: { 254 Clear(false, /* aRecordWriteInfo */ false); 255 256 break; 257 } 258 259 default: 260 MOZ_CRASH("Should never get here!"); 261 } 262 } 263 } 264 265 void SessionStorageCache::SetActor(SessionStorageCacheChild* aActor) { 266 AssertIsOnMainThread(); 267 MOZ_ASSERT(aActor); 268 MOZ_ASSERT(!mActor); 269 270 mActor = aActor; 271 } 272 273 void SessionStorageCache::ClearActor() { 274 AssertIsOnMainThread(); 275 MOZ_ASSERT(mActor); 276 277 mActor = nullptr; 278 } 279 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; 285 } 286 287 // Update size in our data set 288 mOriginQuotaUsage = newOriginUsage; 289 return true; 290 } 291 292 } // namespace mozilla::dom