250 lines
6.5 KiB
C++
250 lines
6.5 KiB
C++
/* 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 "OriginInfo.h"
|
|
#include "mozilla/DebugOnly.h"
|
|
#include "mozilla/dom/StorageActivityService.h"
|
|
#include "mozilla/dom/quota/AssertionsImpl.h"
|
|
#include "mozilla/dom/quota/NotifyUtils.h"
|
|
#include "mozilla/dom/quota/OriginDirectoryLock.h"
|
|
#include "mozilla/dom/quota/QuotaManager.h"
|
|
#include "mozilla/ipc/BackgroundParent.h"
|
|
|
|
namespace mozilla::dom::quota {
|
|
|
|
CanonicalQuotaObject::CanonicalQuotaObject(
|
|
const RefPtr<OriginInfo>& aOriginInfo, Client::Type aClientType,
|
|
const nsAString& aPath, int64_t aSize)
|
|
: QuotaObject(/* aIsRemote */ false),
|
|
mOriginInfo(aOriginInfo),
|
|
mPath(aPath),
|
|
mSize(aSize),
|
|
mClientType(aClientType),
|
|
mQuotaCheckDisabled(false),
|
|
mWritingDone(false) {
|
|
MOZ_COUNT_CTOR(CanonicalQuotaObject);
|
|
}
|
|
|
|
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 (RefPtr<OriginInfo> originInfo = RefPtr<OriginInfo>(mOriginInfo)) {
|
|
originInfo->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)
|
|
MOZ_NO_THREAD_SAFETY_ANALYSIS {
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
|
|
|
|
RefPtr<OriginInfo> originInfo = RefPtr<OriginInfo>(mOriginInfo);
|
|
|
|
if (mWritingDone == false && originInfo) {
|
|
mWritingDone = true;
|
|
StorageActivityService::SendActivity(originInfo->mOrigin);
|
|
}
|
|
|
|
if (mQuotaCheckDisabled) {
|
|
return true;
|
|
}
|
|
|
|
if (mSize == aSize) {
|
|
return true;
|
|
}
|
|
|
|
if (!originInfo) {
|
|
mSize = aSize;
|
|
return true;
|
|
}
|
|
|
|
DebugOnly<GroupInfo*> groupInfo = originInfo->mGroupInfo;
|
|
MOZ_ASSERT(groupInfo);
|
|
|
|
if (mSize > aSize) {
|
|
if (aTruncate) {
|
|
const int64_t delta = mSize - aSize;
|
|
originInfo->LockedTruncateUsages(mClientType, delta);
|
|
mSize = aSize;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MOZ_ASSERT(mSize < aSize);
|
|
|
|
uint64_t delta = aSize - mSize;
|
|
|
|
// Temporary storage has no limit for origin usage (there's a group and the
|
|
// global limit though).
|
|
|
|
if (const auto& maybeReturnValue =
|
|
originInfo->LockedUpdateUsages(mClientType, delta)) {
|
|
if (maybeReturnValue.value()) {
|
|
mSize = aSize; // No limit was breached and we are done.
|
|
}
|
|
|
|
return maybeReturnValue.value();
|
|
}
|
|
|
|
// 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);
|
|
|
|
NotifyStoragePressure(*quotaManager, usage);
|
|
|
|
return false;
|
|
}
|
|
|
|
NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
|
|
|
|
{
|
|
MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
|
|
|
|
for (const auto& lock : locks) {
|
|
quotaManager->DeleteOriginDirectory(lock->OriginMetadata());
|
|
}
|
|
}
|
|
|
|
// Relocked.
|
|
|
|
for (const auto& lock : locks) {
|
|
MOZ_ASSERT(!(lock->GetPersistenceType() == groupInfo->mPersistenceType &&
|
|
lock->Origin() == originInfo->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.
|
|
|
|
QM_ASSERT_NO_UNDERFLOW(aSize, mSize);
|
|
const uint64_t increase = aSize - mSize;
|
|
|
|
if (!originInfo->LockedUpdateUsagesForEviction(mClientType, increase)) {
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
} // namespace mozilla::dom::quota
|