tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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