nsresult DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } Data& data = DataSet(aStorage); if (!data.mKeys.Get(aKey, &aOld)) { SetDOMStringToNull(aOld); return NS_SUCCESS_DOM_NO_OPERATION; } // Recalculate the cached data size const int64_t delta = -(static_cast<int64_t>(aOld.Length()) + static_cast<int64_t>(aKey.Length())); Unused << ProcessUsageDelta(aStorage, delta); data.mKeys.Remove(aKey); if (Persist(aStorage)) { if (!sDatabase) { NS_ERROR("Writing to localStorage after the database has been shut down" ", data lose!"); return NS_ERROR_NOT_INITIALIZED; } return sDatabase->AsyncRemoveItem(this, aKey); } return NS_OK; }
bool LocalStorageCache::ProcessUsageDelta(const LocalStorage* aStorage, int64_t aDelta, const MutationSource aSource) { return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource); }
nsresult DOMStorageCache::Clear(const DOMStorage* aStorage) { Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer; bool refresh = false; if (Persist(aStorage)) { // We need to preload all data (know the size) before we can proceeed // to correctly decrease cached usage number. // XXX as in case of unload, this is not technically needed now, but // after super-scope quota introduction we have to do this. Get telemetry // right now. WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { // When we failed to load data from the database, force delete of the // scope data and make use of the storage possible again. refresh = true; mLoadResult = NS_OK; } } Data& data = DataSet(aStorage); bool hadData = !!data.mKeys.Count(); if (hadData) { unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage); data.mKeys.Clear(); } if (Persist(aStorage) && (refresh || hadData)) { return sDatabase->AsyncClear(this); } return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION; }
nsresult DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld) { Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer; if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } Data& data = DataSet(aStorage); if (!data.mKeys.Get(aKey, &aOld)) { SetDOMStringToNull(aOld); return NS_SUCCESS_DOM_NO_OPERATION; } // Recalculate the cached data size const int64_t delta = -(static_cast<int64_t>(aOld.Length())); unused << ProcessUsageDelta(aStorage, delta); data.mKeys.Remove(aKey); if (Persist(aStorage)) { return sDatabase->AsyncRemoveItem(this, aKey); } return NS_OK; }
DOMStorageCache::Data& DOMStorageCache::DataSet(const DOMStorage* aStorage) { uint32_t index = GetDataSetIndex(aStorage); if (index == kSessionSet && !mSessionOnlyDataSetActive) { // Session only data set is demanded but not filled with // current data set, copy to session only set now. WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS); Data& defaultSet = mData[kDefaultSet]; Data& sessionSet = mData[kSessionSet]; defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet); mSessionOnlyDataSetActive = true; // This updates sessionSet.mOriginQuotaUsage and also updates global usage // for all session only data ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage); } return mData[index]; }
nsresult LocalStorageCache::SetItem(const LocalStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld, const MutationSource aSource) { // Size of the cache that will change after this action. int64_t delta = 0; if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } Data& data = DataSet(aStorage); if (!data.mKeys.Get(aKey, &aOld)) { SetDOMStringToNull(aOld); // 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>(aOld.Length()); if (!ProcessUsageDelta(aStorage, delta, aSource)) { return NS_ERROR_DOM_QUOTA_REACHED; } if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) { return NS_SUCCESS_DOM_NO_OPERATION; } data.mKeys.Put(aKey, aValue); if (aSource == ContentMutation && Persist(aStorage)) { if (!sDatabase) { NS_ERROR("Writing to localStorage after the database has been shut down" ", data lose!"); return NS_ERROR_NOT_INITIALIZED; } if (DOMStringIsNull(aOld)) { return sDatabase->AsyncAddItem(this, aKey, aValue); } return sDatabase->AsyncUpdateItem(this, aKey, aValue); } return NS_OK; }
void DOMStorageCache::CloneFrom(const DOMStorageCache* aThat) { mLoaded = aThat->mLoaded; mInitialized = aThat->mInitialized; mPersistent = aThat->mPersistent; mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive; for (uint32_t i = 0; i < kDataSetCount; ++i) { aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]); ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage); } }
void DOMStorageCache::UnloadItems(uint32_t aUnloadFlags) { if (aUnloadFlags & kUnloadDefault) { // Must wait for preload to pass correct usage to ProcessUsageDelta // XXX this is not technically needed right now since there is just // per-origin isolated quota handling, but when we introduce super- // -scope quotas, we have to do this. Better to start getting // telemetry right now. WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS); mData[kDefaultSet].mKeys.Clear(); ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage); } if (aUnloadFlags & kUnloadPrivate) { mData[kPrivateSet].mKeys.Clear(); ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage); } if (aUnloadFlags & kUnloadSession) { mData[kSessionSet].mKeys.Clear(); ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage); mSessionOnlyDataSetActive = false; } #ifdef DOM_STORAGE_TESTS if (aUnloadFlags & kTestReload) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS); mData[kDefaultSet].mKeys.Clear(); mLoaded = false; // This is only used in testing code Preload(); } #endif }
nsresult DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } Data& data = DataSet(aStorage); if (!data.mKeys.Get(aKey, &aOld)) { SetDOMStringToNull(aOld); } // Check the quota first const int64_t delta = static_cast<int64_t>(aValue.Length()) - static_cast<int64_t>(aOld.Length()); if (!ProcessUsageDelta(aStorage, delta)) { return NS_ERROR_DOM_QUOTA_REACHED; } if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) { return NS_SUCCESS_DOM_NO_OPERATION; } data.mKeys.Put(aKey, aValue); if (Persist(aStorage)) { if (!sDatabase) { NS_ERROR("Writing to localStorage after the database has been shut down" ", data lose!"); return NS_ERROR_NOT_INITIALIZED; } if (DOMStringIsNull(aOld)) { return sDatabase->AsyncAddItem(this, aKey, aValue); } return sDatabase->AsyncUpdateItem(this, aKey, aValue); } return NS_OK; }
nsresult LocalStorageCache::Clear(const LocalStorage* aStorage, const MutationSource aSource) { bool refresh = false; if (Persist(aStorage)) { // We need to preload all data (know the size) before we can proceeed // to correctly decrease cached usage number. // XXX as in case of unload, this is not technically needed now, but // after super-scope quota introduction we have to do this. Get telemetry // right now. WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { // When we failed to load data from the database, force delete of the // scope data and make use of the storage possible again. refresh = true; mLoadResult = NS_OK; } } Data& data = DataSet(aStorage); bool hadData = !!data.mKeys.Count(); if (hadData) { Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource); data.mKeys.Clear(); } if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) { if (!sDatabase) { NS_ERROR("Writing to localStorage after the database has been shut down" ", data lose!"); return NS_ERROR_NOT_INITIALIZED; } return sDatabase->AsyncClear(this); } return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION; }
void DOMStorageCache::CloneFrom(const DOMStorageCache* aThat) { // This will never be called on anything else than SessionStorage. // This means mData will never be touched on any other thread than // the main thread and it never went through the loading process. MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mPersistent); MOZ_ASSERT(!(bool)aThat->mLoaded); mLoaded = false; mInitialized = aThat->mInitialized; mPersistent = false; mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive; for (uint32_t i = 0; i < kDataSetCount; ++i) { for (auto it = aThat->mData[i].mKeys.ConstIter(); !it.Done(); it.Next()) { mData[i].mKeys.Put(it.Key(), it.UserData()); } ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage); } }
bool DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta) { return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta); }