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 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())); 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; }
nsresult StorageCache::GetKey(const Storage* aStorage, uint32_t aIndex, nsAString& aRetval) { // XXX: This does a linear search for the key at index, which would // suck if there's a large numer of indexes. Do we care? If so, // maybe we need to have a lazily populated key array here or // something? if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } aRetval.SetIsVoid(true); for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) { if (aIndex == 0) { aRetval = iter.Key(); break; } aIndex--; } return NS_OK; }
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; }
nsresult DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } *aRetval = DataSet(aStorage).mKeys.Count(); return NS_OK; }
void DOMStorageCache::GetKeys(const DOMStorage* aStorage, nsTArray<nsString>& aKeys) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS); } if (NS_FAILED(mLoadResult)) { return; } DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, &aKeys); }
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 }
void DOMStorageCache::GetKeys(const DOMStorage* aStorage, nsTArray<nsString>& aKeys) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS); } if (NS_FAILED(mLoadResult)) { return; } for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) { aKeys.AppendElement(iter.Key()); } }
nsTArray<nsString>* DOMStorageCache::GetKeys(const DOMStorage* aStorage) { Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer; if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS); } nsTArray<nsString>* result = new nsTArray<nsString>(); if (NS_SUCCEEDED(mLoadResult)) { DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result); } return result; }
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 DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval) { // XXX: This does a linear search for the key at index, which would // suck if there's a large numer of indexes. Do we care? If so, // maybe we need to have a lazily populated key array here or // something? if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } IndexFinderData data(aIndex, aRetval); DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data); return NS_OK; }
nsresult DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval) { if (Persist(aStorage)) { WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS); if (NS_FAILED(mLoadResult)) { return mLoadResult; } } // not using AutoString since we don't want to copy buffer to result nsString value; if (!DataSet(aStorage).mKeys.Get(aKey, &value)) { SetDOMStringToNull(value); } aRetval = value; 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; }