summaryrefslogtreecommitdiffstats
path: root/dom/quota/CanonicalQuotaObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/quota/CanonicalQuotaObject.cpp')
-rw-r--r--dom/quota/CanonicalQuotaObject.cpp344
1 files changed, 344 insertions, 0 deletions
diff --git a/dom/quota/CanonicalQuotaObject.cpp b/dom/quota/CanonicalQuotaObject.cpp
new file mode 100644
index 0000000000..9c284cb471
--- /dev/null
+++ b/dom/quota/CanonicalQuotaObject.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanonicalQuotaObject.h"
+
+#include "GroupInfo.h"
+#include "GroupInfoPair.h"
+#include "mozilla/dom/StorageActivityService.h"
+#include "mozilla/dom/quota/AssertionsImpl.h"
+#include "mozilla/dom/quota/DirectoryLock.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "OriginInfo.h"
+
+namespace mozilla::dom::quota {
+
+NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::AddRef() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ if (!quotaManager) {
+ NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
+
+ return ++mRefCnt;
+ }
+
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ return ++mRefCnt;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::Release() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ if (!quotaManager) {
+ NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
+
+ nsrefcnt count = --mRefCnt;
+ if (count == 0) {
+ mRefCnt = 1;
+ delete this;
+ return 0;
+ }
+
+ return mRefCnt;
+ }
+
+ {
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ --mRefCnt;
+
+ if (mRefCnt > 0) {
+ return mRefCnt;
+ }
+
+ if (mOriginInfo) {
+ mOriginInfo->mCanonicalQuotaObjects.Remove(mPath);
+ }
+ }
+
+ delete this;
+ return 0;
+}
+
+bool CanonicalQuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ return LockedMaybeUpdateSize(aSize, aTruncate);
+}
+
+bool CanonicalQuotaObject::IncreaseSize(int64_t aDelta) {
+ MOZ_ASSERT(aDelta >= 0);
+
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ AssertNoOverflow(mSize, aDelta);
+ int64_t size = mSize + aDelta;
+
+ return LockedMaybeUpdateSize(size, /* aTruncate */ false);
+}
+
+void CanonicalQuotaObject::DisableQuotaCheck() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ mQuotaCheckDisabled = true;
+}
+
+void CanonicalQuotaObject::EnableQuotaCheck() {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+ mQuotaCheckDisabled = false;
+}
+
+bool CanonicalQuotaObject::LockedMaybeUpdateSize(int64_t aSize,
+ bool aTruncate) {
+ QuotaManager* quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
+
+ if (mWritingDone == false && mOriginInfo) {
+ mWritingDone = true;
+ StorageActivityService::SendActivity(mOriginInfo->mOrigin);
+ }
+
+ if (mQuotaCheckDisabled) {
+ return true;
+ }
+
+ if (mSize == aSize) {
+ return true;
+ }
+
+ if (!mOriginInfo) {
+ mSize = aSize;
+ return true;
+ }
+
+ GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
+ MOZ_ASSERT(groupInfo);
+
+ if (mSize > aSize) {
+ if (aTruncate) {
+ const int64_t delta = mSize - aSize;
+
+ AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta);
+ quotaManager->mTemporaryStorageUsage -= delta;
+
+ if (!mOriginInfo->LockedPersisted()) {
+ AssertNoUnderflow(groupInfo->mUsage, delta);
+ groupInfo->mUsage -= delta;
+ }
+
+ AssertNoUnderflow(mOriginInfo->mUsage, delta);
+ mOriginInfo->mUsage -= delta;
+
+ MOZ_ASSERT(mOriginInfo->mClientUsages[mClientType].isSome());
+ AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType].value(), delta);
+ mOriginInfo->mClientUsages[mClientType] =
+ Some(mOriginInfo->mClientUsages[mClientType].value() - delta);
+
+ mSize = aSize;
+ }
+ return true;
+ }
+
+ MOZ_ASSERT(mSize < aSize);
+
+ const auto& complementaryPersistenceTypes =
+ ComplementaryPersistenceTypes(groupInfo->mPersistenceType);
+
+ uint64_t delta = aSize - mSize;
+
+ AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
+ uint64_t newClientUsage =
+ mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
+
+ AssertNoOverflow(mOriginInfo->mUsage, delta);
+ uint64_t newUsage = mOriginInfo->mUsage + delta;
+
+ // Temporary storage has no limit for origin usage (there's a group and the
+ // global limit though).
+
+ uint64_t newGroupUsage = groupInfo->mUsage;
+ if (!mOriginInfo->LockedPersisted()) {
+ AssertNoOverflow(groupInfo->mUsage, delta);
+ newGroupUsage += delta;
+
+ uint64_t groupUsage = groupInfo->mUsage;
+ for (const auto& complementaryPersistenceType :
+ complementaryPersistenceTypes) {
+ const auto& complementaryGroupInfo =
+ groupInfo->mGroupInfoPair->LockedGetGroupInfo(
+ complementaryPersistenceType);
+
+ if (complementaryGroupInfo) {
+ AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
+ groupUsage += complementaryGroupInfo->mUsage;
+ }
+ }
+
+ // Temporary storage has a hard limit for group usage (20 % of the global
+ // limit).
+ AssertNoOverflow(groupUsage, delta);
+ if (groupUsage + delta > quotaManager->GetGroupLimit()) {
+ return false;
+ }
+ }
+
+ AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
+ uint64_t newTemporaryStorageUsage =
+ quotaManager->mTemporaryStorageUsage + delta;
+
+ if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
+ // This will block the thread without holding the lock while waitting.
+
+ AutoTArray<RefPtr<OriginDirectoryLock>, 10> locks;
+ uint64_t sizeToBeFreed;
+
+ if (::mozilla::ipc::IsOnBackgroundThread()) {
+ MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+ sizeToBeFreed = quotaManager->CollectOriginsForEviction(delta, locks);
+ } else {
+ sizeToBeFreed =
+ quotaManager->LockedCollectOriginsForEviction(delta, locks);
+ }
+
+ if (!sizeToBeFreed) {
+ uint64_t usage = quotaManager->mTemporaryStorageUsage;
+
+ MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+ quotaManager->NotifyStoragePressure(usage);
+
+ return false;
+ }
+
+ NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
+
+ {
+ MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+ for (const auto& lock : locks) {
+ quotaManager->DeleteOriginDirectory(lock->OriginMetadata());
+ }
+ }
+
+ // Relocked.
+
+ NS_ASSERTION(mOriginInfo, "How come?!");
+
+ for (const auto& lock : locks) {
+ MOZ_ASSERT(!(lock->GetPersistenceType() == groupInfo->mPersistenceType &&
+ lock->Origin() == mOriginInfo->mOrigin),
+ "Deleted itself!");
+
+ quotaManager->LockedRemoveQuotaForOrigin(lock->OriginMetadata());
+ }
+
+ // We unlocked and relocked several times so we need to recompute all the
+ // essential variables and recheck the group limit.
+
+ AssertNoUnderflow(aSize, mSize);
+ delta = aSize - mSize;
+
+ AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
+ newClientUsage = mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
+
+ AssertNoOverflow(mOriginInfo->mUsage, delta);
+ newUsage = mOriginInfo->mUsage + delta;
+
+ newGroupUsage = groupInfo->mUsage;
+ if (!mOriginInfo->LockedPersisted()) {
+ AssertNoOverflow(groupInfo->mUsage, delta);
+ newGroupUsage += delta;
+
+ uint64_t groupUsage = groupInfo->mUsage;
+
+ for (const auto& complementaryPersistenceType :
+ complementaryPersistenceTypes) {
+ const auto& complementaryGroupInfo =
+ groupInfo->mGroupInfoPair->LockedGetGroupInfo(
+ complementaryPersistenceType);
+
+ if (complementaryGroupInfo) {
+ AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
+ groupUsage += complementaryGroupInfo->mUsage;
+ }
+ }
+
+ AssertNoOverflow(groupUsage, delta);
+ if (groupUsage + delta > quotaManager->GetGroupLimit()) {
+ // Unfortunately some other thread increased the group usage in the
+ // meantime and we are not below the group limit anymore.
+
+ // However, the origin eviction must be finalized in this case too.
+ MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+ quotaManager->FinalizeOriginEviction(std::move(locks));
+
+ return false;
+ }
+ }
+
+ AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
+ newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
+
+ NS_ASSERTION(
+ newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit,
+ "How come?!");
+
+ // Ok, we successfully freed enough space and the operation can continue
+ // without throwing the quota error.
+ mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
+
+ mOriginInfo->mUsage = newUsage;
+ if (!mOriginInfo->LockedPersisted()) {
+ groupInfo->mUsage = newGroupUsage;
+ }
+ quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
+ ;
+
+ // Some other thread could increase the size in the meantime, but no more
+ // than this one.
+ MOZ_ASSERT(mSize < aSize);
+ mSize = aSize;
+
+ // Finally, release IO thread only objects and allow next synchronized
+ // ops for the evicted origins.
+ MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+ quotaManager->FinalizeOriginEviction(std::move(locks));
+
+ return true;
+ }
+
+ mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
+
+ mOriginInfo->mUsage = newUsage;
+ if (!mOriginInfo->LockedPersisted()) {
+ groupInfo->mUsage = newGroupUsage;
+ }
+ quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
+
+ mSize = aSize;
+
+ return true;
+}
+
+} // namespace mozilla::dom::quota