Bug 1322316 - Split SessionStorage and LocalStorage implementation - part 11 - SessionStorageCache must have 2 DataSet: default and sessionOnly, r=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 17 May 2017 07:01:15 +0200
changeset 409209 2f47ba116b4225020550d00b4627aa6aa0001e46
parent 409208 47c603ef87ce736d3a24e6491a9166f945ceec94
child 409210 2268a93ddfc82e0fc7dc941b8eba87faef878a80
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1322316
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1322316 - Split SessionStorage and LocalStorage implementation - part 11 - SessionStorageCache must have 2 DataSet: default and sessionOnly, r=asuth
dom/storage/SessionStorage.cpp
dom/storage/SessionStorageCache.cpp
dom/storage/SessionStorageCache.h
dom/storage/SessionStorageManager.cpp
dom/storage/SessionStorageManager.h
--- a/dom/storage/SessionStorage.cpp
+++ b/dom/storage/SessionStorage.cpp
@@ -9,16 +9,20 @@
 #include "SessionStorageManager.h"
 
 #include "mozilla/dom/StorageBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 #include "nsIPrincipal.h"
 #include "nsPIDOMWindow.h"
 
+#define DATASET IsSessionOnly()                          \
+                  ? SessionStorageCache::eSessionSetType \
+                  : SessionStorageCache::eDefaultSetType
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(SessionStorage, Storage, mManager);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorage)
 NS_INTERFACE_MAP_END_INHERITING(Storage)
 
@@ -51,81 +55,81 @@ SessionStorage::Clone() const
     new SessionStorage(GetParentObject(), Principal(), mCache, mManager,
                        mDocumentURI, mIsPrivate);
   return storage.forget();
 }
 
 int64_t
 SessionStorage::GetOriginQuotaUsage() const
 {
-  return mCache->GetOriginQuotaUsage();
+  return mCache->GetOriginQuotaUsage(DATASET);
 }
 
 uint32_t
 SessionStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return 0;
   }
 
-  return mCache->Length();
+  return mCache->Length(DATASET);
 }
 
 void
 SessionStorage::Key(uint32_t aIndex, nsAString& aResult,
                     nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
-  mCache->Key(aIndex, aResult);
+  mCache->Key(DATASET, aIndex, aResult);
 }
 
 void
 SessionStorage::GetItem(const nsAString& aKey, nsAString& aResult,
                         nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
-  mCache->GetItem(aKey, aResult);
+  mCache->GetItem(DATASET, aKey, aResult);
 }
 
 void
 SessionStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
   if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
     // return just an empty array
     aKeys.Clear();
     return;
   }
 
-  mCache->GetKeys(aKeys);
+  mCache->GetKeys(DATASET, aKeys);
 }
 
 void
 SessionStorage::SetItem(const nsAString& aKey, const nsAString& aValue,
                         nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsString oldValue;
-  nsresult rv = mCache->SetItem(aKey, aValue, oldValue);
+  nsresult rv = mCache->SetItem(DATASET, aKey, aValue, oldValue);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
     return;
   }
 
   if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
     return;
   }
@@ -139,17 +143,17 @@ SessionStorage::RemoveItem(const nsAStri
                            ErrorResult& aRv)
 {
   if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsString oldValue;
-  nsresult rv = mCache->RemoveItem(aKey, oldValue);
+  nsresult rv = mCache->RemoveItem(DATASET, aKey, oldValue);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
     return;
   }
 
   BroadcastChangeNotification(aKey, oldValue, NullString());
 }
@@ -158,17 +162,17 @@ void
 SessionStorage::Clear(nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
   uint32_t length = GetLength(aSubjectPrincipal, aRv);
   if (!length) {
     return;
   }
 
-  mCache->Clear();
+  mCache->Clear(DATASET);
   BroadcastChangeNotification(NullString(), NullString(), NullString());
 }
 
 void
 SessionStorage::BroadcastChangeNotification(const nsAString& aKey,
                                             const nsAString& aOldValue,
                                             const nsAString& aNewValue)
 {
--- a/dom/storage/SessionStorageCache.cpp
+++ b/dom/storage/SessionStorageCache.cpp
@@ -5,123 +5,175 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SessionStorageCache.h"
 
 namespace mozilla {
 namespace dom {
 
 SessionStorageCache::SessionStorageCache()
-  : mOriginQuotaUsage(0)
+  : mSessionDataSetActive(false)
 {}
 
+SessionStorageCache::DataSet*
+SessionStorageCache::Set(DataSetType aDataSetType)
+{
+  if (aDataSetType == eDefaultSetType) {
+    return &mDefaultSet;
+  }
+
+  MOZ_ASSERT(aDataSetType == eSessionSetType);
+
+  if (!mSessionDataSetActive) {
+    mSessionSet.mOriginQuotaUsage = mDefaultSet.mOriginQuotaUsage;
+
+    for (auto iter = mDefaultSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
+      mSessionSet.mKeys.Put(iter.Key(), iter.Data());
+    }
+
+    mSessionDataSetActive = true;
+  }
+
+  return &mSessionSet;
+}
+
+int64_t
+SessionStorageCache::GetOriginQuotaUsage(DataSetType aDataSetType)
+{
+  return Set(aDataSetType)->mOriginQuotaUsage;
+}
+
+uint32_t
+SessionStorageCache::Length(DataSetType aDataSetType)
+{
+  return Set(aDataSetType)->mKeys.Count();
+}
+
 void
-SessionStorageCache::Key(uint32_t aIndex, nsAString& aResult)
+SessionStorageCache::Key(DataSetType aDataSetType, uint32_t aIndex,
+                         nsAString& aResult)
 {
   aResult.SetIsVoid(true);
-  for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) {
+  for (auto iter = Set(aDataSetType)->mKeys.Iter(); !iter.Done(); iter.Next()) {
     if (aIndex == 0) {
       aResult = iter.Key();
       return;
     }
     aIndex--;
   }
 }
 
 void
-SessionStorageCache::GetItem(const nsAString& aKey, nsAString& aResult)
+SessionStorageCache::GetItem(DataSetType aDataSetType, const nsAString& aKey,
+                             nsAString& aResult)
 {
   // not using AutoString since we don't want to copy buffer to result
   nsString value;
-  if (!mKeys.Get(aKey, &value)) {
+  if (!Set(aDataSetType)->mKeys.Get(aKey, &value)) {
     SetDOMStringToNull(value);
   }
   aResult = value;
 }
 
 void
-SessionStorageCache::GetKeys(nsTArray<nsString>& aKeys)
+SessionStorageCache::GetKeys(DataSetType aDataSetType, nsTArray<nsString>& aKeys)
 {
-  for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) {
+  for (auto iter = Set(aDataSetType)->mKeys.Iter(); !iter.Done(); iter.Next()) {
     aKeys.AppendElement(iter.Key());
   }
 }
 
 nsresult
-SessionStorageCache::SetItem(const nsAString& aKey, const nsAString& aValue,
-                             nsString& aOldValue)
+SessionStorageCache::SetItem(DataSetType aDataSetType, const nsAString& aKey,
+                             const nsAString& aValue, nsString& aOldValue)
 {
   int64_t delta = 0;
+  DataSet* dataSet = Set(aDataSetType);
 
-  if (!mKeys.Get(aKey, &aOldValue)) {
+  if (!dataSet->mKeys.Get(aKey, &aOldValue)) {
     SetDOMStringToNull(aOldValue);
 
     // We only consider key size if the key doesn't exist before.
     delta = static_cast<int64_t>(aKey.Length());
   }
 
   delta += static_cast<int64_t>(aValue.Length()) -
            static_cast<int64_t>(aOldValue.Length());
 
   if (aValue == aOldValue &&
       DOMStringIsNull(aValue) == DOMStringIsNull(aOldValue)) {
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
-  if (!ProcessUsageDelta(delta)) {
+  if (!dataSet->ProcessUsageDelta(delta)) {
     return NS_ERROR_DOM_QUOTA_REACHED;
   }
 
-  mKeys.Put(aKey, nsString(aValue));
+  dataSet->mKeys.Put(aKey, nsString(aValue));
   return NS_OK;
 }
 
 nsresult
-SessionStorageCache::RemoveItem(const nsAString& aKey, nsString& aOldValue)
+SessionStorageCache::RemoveItem(DataSetType aDataSetType, const nsAString& aKey,
+                                nsString& aOldValue)
 {
-  if (!mKeys.Get(aKey, &aOldValue)) {
+  DataSet* dataSet = Set(aDataSetType);
+
+  if (!dataSet->mKeys.Get(aKey, &aOldValue)) {
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   // Recalculate the cached data size
-  ProcessUsageDelta(-(static_cast<int64_t>(aOldValue.Length()) +
-                      static_cast<int64_t>(aKey.Length())));
+  dataSet->ProcessUsageDelta(-(static_cast<int64_t>(aOldValue.Length()) +
+                               static_cast<int64_t>(aKey.Length())));
 
-  mKeys.Remove(aKey);
+  dataSet->mKeys.Remove(aKey);
   return NS_OK;
 }
 
 void
-SessionStorageCache::Clear()
+SessionStorageCache::Clear(DataSetType aDataSetType, bool aByUserInteraction)
+{
+  DataSet* dataSet = Set(aDataSetType);
+  dataSet->ProcessUsageDelta(-dataSet->mOriginQuotaUsage);
+  dataSet->mKeys.Clear();
+
+  if (!aByUserInteraction && aDataSetType == eSessionSetType) {
+    mSessionDataSetActive = false;
+  }
+}
+
+already_AddRefed<SessionStorageCache>
+SessionStorageCache::Clone() const
 {
-  ProcessUsageDelta(-mOriginQuotaUsage);
-  mKeys.Clear();
+  RefPtr<SessionStorageCache> cache = new SessionStorageCache();
+
+  cache->mSessionDataSetActive = mSessionDataSetActive;
+
+  cache->mDefaultSet.mOriginQuotaUsage = mDefaultSet.mOriginQuotaUsage;
+  for (auto iter = mDefaultSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
+    cache->mDefaultSet.mKeys.Put(iter.Key(), iter.Data());
+  }
+
+  cache->mSessionSet.mOriginQuotaUsage = mSessionSet.mOriginQuotaUsage;
+  for (auto iter = mSessionSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
+    cache->mSessionSet.mKeys.Put(iter.Key(), iter.Data());
+  }
+
+  return cache.forget();
 }
 
 bool
-SessionStorageCache::ProcessUsageDelta(int64_t aDelta)
+SessionStorageCache::DataSet::ProcessUsageDelta(int64_t aDelta)
 {
   // Check limit per this origin
   uint64_t newOriginUsage = mOriginQuotaUsage + aDelta;
   if (aDelta > 0 && newOriginUsage > LocalStorageManager::GetQuota()) {
     return false;
   }
 
   // Update size in our data set
   mOriginQuotaUsage = newOriginUsage;
   return true;
 }
 
-already_AddRefed<SessionStorageCache>
-SessionStorageCache::Clone() const
-{
-  RefPtr<SessionStorageCache> cache = new SessionStorageCache();
-  cache->mOriginQuotaUsage = mOriginQuotaUsage;
-
-  for (auto iter = mKeys.ConstIter(); !iter.Done(); iter.Next()) {
-    cache->mKeys.Put(iter.Key(), iter.Data());
-  }
-
-  return cache.forget();
-}
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/storage/SessionStorageCache.h
+++ b/dom/storage/SessionStorageCache.h
@@ -14,45 +14,61 @@ namespace dom {
 
 class SessionStorageCache final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(SessionStorageCache)
 
   SessionStorageCache();
 
-  int64_t GetOriginQuotaUsage() const
-  {
-    return mOriginQuotaUsage;
-  }
+  enum DataSetType {
+    eDefaultSetType,
+    eSessionSetType,
+  };
 
-  uint32_t Length() const { return mKeys.Count(); }
+  int64_t GetOriginQuotaUsage(DataSetType aDataSetType);
 
-  void Key(uint32_t aIndex, nsAString& aResult);
+  uint32_t Length(DataSetType aDataSetType);
 
-  void GetItem(const nsAString& aKey, nsAString& aResult);
+  void Key(DataSetType aDataSetType, uint32_t aIndex, nsAString& aResult);
 
-  void GetKeys(nsTArray<nsString>& aKeys);
+  void GetItem(DataSetType aDataSetType, const nsAString& aKey,
+               nsAString& aResult);
 
-  nsresult SetItem(const nsAString& aKey, const nsAString& aValue,
-                   nsString& aOldValue);
+  void GetKeys(DataSetType aDataSetType, nsTArray<nsString>& aKeys);
 
-  nsresult RemoveItem(const nsAString& aKey,
+  nsresult SetItem(DataSetType aDataSetType, const nsAString& aKey,
+                   const nsAString& aValue, nsString& aOldValue);
+
+  nsresult RemoveItem(DataSetType aDataSetType, const nsAString& aKey,
                       nsString& aOldValue);
 
-  void Clear();
+  void Clear(DataSetType aDataSetType, bool aByUserInteraction = true);
 
   already_AddRefed<SessionStorageCache>
   Clone() const;
 
 private:
   ~SessionStorageCache() = default;
 
-  bool ProcessUsageDelta(int64_t aDelta);
+  struct DataSet
+  {
+    DataSet()
+      : mOriginQuotaUsage(0)
+    {}
+
+    bool ProcessUsageDelta(int64_t aDelta);
 
-  int64_t mOriginQuotaUsage;
-  nsDataHashtable<nsStringHashKey, nsString> mKeys;
+    int64_t mOriginQuotaUsage;
+    nsDataHashtable<nsStringHashKey, nsString> mKeys;
+  };
+
+  DataSet* Set(DataSetType aDataSetType);
+
+  DataSet mDefaultSet;
+  DataSet mSessionSet;
+  bool mSessionDataSetActive;
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif //mozilla_dom_SessionStorageCache_h
--- a/dom/storage/SessionStorageManager.cpp
+++ b/dom/storage/SessionStorageManager.cpp
@@ -207,33 +207,40 @@ SessionStorageManager::GetLocalStorageFo
                                                    const nsAString& aDocumentURI,
                                                    bool aPrivate,
                                                    nsIDOMStorage** aRetval)
 {
   return NS_ERROR_UNEXPECTED;
 }
 
 void
-SessionStorageManager::ClearStorages(const OriginAttributesPattern& aPattern,
+SessionStorageManager::ClearStorages(ClearStorageType aType,
+                                     const OriginAttributesPattern& aPattern,
                                      const nsACString& aOriginScope)
 {
   for (auto iter1 = mOATable.Iter(); !iter1.Done(); iter1.Next()) {
     OriginAttributes oa;
     DebugOnly<bool> ok = oa.PopulateFromSuffix(iter1.Key());
     MOZ_ASSERT(ok);
     if (!aPattern.Matches(oa)) {
       // This table doesn't match the given origin attributes pattern
       continue;
     }
 
     OriginKeyHashTable* table = iter1.Data();
     for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
       if (aOriginScope.IsEmpty() ||
           StringBeginsWith(iter2.Key(), aOriginScope)) {
-        iter2.Data()->Clear();
+        if (aType == eAll) {
+          iter2.Data()->Clear(SessionStorageCache::eDefaultSetType, false);
+          iter2.Data()->Clear(SessionStorageCache::eSessionSetType, false);
+        } else {
+          MOZ_ASSERT(aType == eSessionOnly);
+          iter2.Data()->Clear(SessionStorageCache::eSessionSetType, false);
+        }
       }
     }
   }
 }
 
 nsresult
 SessionStorageManager::Observe(const char* aTopic,
                                const nsAString& aOriginAttributesPattern,
@@ -242,37 +249,37 @@ SessionStorageManager::Observe(const cha
   OriginAttributesPattern pattern;
   if (!pattern.Init(aOriginAttributesPattern)) {
     NS_ERROR("Cannot parse origin attributes pattern");
     return NS_ERROR_FAILURE;
   }
 
   // Clear everything, caches + database
   if (!strcmp(aTopic, "cookie-cleared")) {
-    ClearStorages(pattern, EmptyCString());
+    ClearStorages(eAll, pattern, EmptyCString());
     return NS_OK;
   }
 
   // Clear from caches everything that has been stored
   // while in session-only mode
   if (!strcmp(aTopic, "session-only-cleared")) {
-    ClearStorages(pattern, aOriginScope);
+    ClearStorages(eSessionOnly, pattern, aOriginScope);
     return NS_OK;
   }
 
   // Clear everything (including so and pb data) from caches and database
   // for the gived domain and subdomains.
   if (!strcmp(aTopic, "domain-data-cleared")) {
-    ClearStorages(pattern, aOriginScope);
+    ClearStorages(eAll, pattern, aOriginScope);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "profile-change")) {
     // For case caches are still referenced - clear them completely
-    ClearStorages(pattern, EmptyCString());
+    ClearStorages(eAll, pattern, EmptyCString());
     mOATable.Clear();
     return NS_OK;
   }
 
   return NS_OK;
 }
 
 } // dom namespace
--- a/dom/storage/SessionStorageManager.h
+++ b/dom/storage/SessionStorageManager.h
@@ -30,18 +30,24 @@ private:
   ~SessionStorageManager();
 
   // StorageObserverSink, handler to various chrome clearing notification
   nsresult
   Observe(const char* aTopic,
           const nsAString& aOriginAttributesPattern,
           const nsACString& aOriginScope) override;
 
+  enum ClearStorageType {
+    eAll,
+    eSessionOnly,
+  };
+
   void
-  ClearStorages(const OriginAttributesPattern& aPattern,
+  ClearStorages(ClearStorageType aType,
+                const OriginAttributesPattern& aPattern,
                 const nsACString& aOriginScope);
 
   typedef nsRefPtrHashtable<nsCStringHashKey, SessionStorageCache> OriginKeyHashTable;
   nsClassHashtable<nsCStringHashKey, OriginKeyHashTable> mOATable;
 };
 
 } // dom namespace
 } // mozilla namespace