SaveOriginAccessTimeOp::DoDirectoryWork currently doesn't call QuotaManager::EnsureStorageIsInitializedInternal and just expects that something else initialized storage previously. This seems to work, but it would be cleaner to always make sure that storage is initialized. However, adding QuotaManager::EnsureStorageIsInitializedInternal revealed another problem. Storage shudown or storage clearing acquires an exlusive lock over entire storage area which essentially forces that all existing directory locks are released first. When the last directory lock for an origin is released, saving of origin access time is scheduled. The problem is that it's scheduled after the exclusive lock for storage shutdown or storage clearing, so storage would be initialized again in the end or access time wouldn't be saved at all due to quota manager shutdown being already in progress. Changes done in this patch: - added QuotaManager::EnsureStorageIsInitializedInternal call to SaveOriginAccessTimeOp::DoDirectoryWork - changed QuotaManager::UnregisterDirectoryLock to work with already cleared directory lock tables - added a new QuotaManager::ClearDirectoryLockTables method - added QuotaManager::ClearDirectoryLockTables call to ShutdownStorageOp::OpenDirectory and ClearStorageOp::OpenDirectory - made ClearStorageOp and ShutdownStorageOp friend classes of QuotaManager Differential Revision: https://phabricator.services.mozilla.com/D187877
2357 lines
72 KiB
C++
2357 lines
72 KiB
C++
/* -*- 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 "OriginOperations.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
|
|
#include "ErrorList.h"
|
|
#include "FileUtils.h"
|
|
#include "GroupInfo.h"
|
|
#include "MainThreadUtils.h"
|
|
#include "mozilla/AppShutdown.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Atomics.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/NotNull.h"
|
|
#include "mozilla/ProfilerLabels.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/Result.h"
|
|
#include "mozilla/ResultExtensions.h"
|
|
#include "mozilla/dom/Nullable.h"
|
|
#include "mozilla/dom/quota/CommonMetadata.h"
|
|
#include "mozilla/dom/quota/Client.h"
|
|
#include "mozilla/dom/quota/Constants.h"
|
|
#include "mozilla/dom/quota/DirectoryLock.h"
|
|
#include "mozilla/dom/quota/PersistenceType.h"
|
|
#include "mozilla/dom/quota/PQuota.h"
|
|
#include "mozilla/dom/quota/PQuotaRequest.h"
|
|
#include "mozilla/dom/quota/PQuotaUsageRequest.h"
|
|
#include "mozilla/dom/quota/OriginScope.h"
|
|
#include "mozilla/dom/quota/QuotaCommon.h"
|
|
#include "mozilla/dom/quota/QuotaManager.h"
|
|
#include "mozilla/dom/quota/QuotaManagerImpl.h"
|
|
#include "mozilla/dom/quota/ResultExtensions.h"
|
|
#include "mozilla/dom/quota/StreamUtils.h"
|
|
#include "mozilla/dom/quota/UsageInfo.h"
|
|
#include "mozilla/fallible.h"
|
|
#include "mozilla/ipc/BackgroundParent.h"
|
|
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
|
#include "NormalOriginOperationBase.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsTHashMap.h"
|
|
#include "nsDebug.h"
|
|
#include "nsError.h"
|
|
#include "nsHashKeys.h"
|
|
#include "nsIBinaryOutputStream.h"
|
|
#include "nsIFile.h"
|
|
#include "nsIObjectOutputStream.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsLiteralString.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsString.h"
|
|
#include "nsTArray.h"
|
|
#include "OriginInfo.h"
|
|
#include "OriginOperationBase.h"
|
|
#include "QuotaRequestBase.h"
|
|
#include "QuotaUsageRequestBase.h"
|
|
#include "ResolvableNormalOriginOp.h"
|
|
#include "prthread.h"
|
|
#include "prtime.h"
|
|
|
|
namespace mozilla::dom::quota {
|
|
|
|
using namespace mozilla::ipc;
|
|
|
|
class FinalizeOriginEvictionOp : public OriginOperationBase {
|
|
nsTArray<RefPtr<OriginDirectoryLock>> mLocks;
|
|
|
|
public:
|
|
FinalizeOriginEvictionOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
nsTArray<RefPtr<OriginDirectoryLock>>&& aLocks)
|
|
: OriginOperationBase(std::move(aQuotaManager),
|
|
"dom::quota::FinalizeOriginEvictionOp"),
|
|
mLocks(std::move(aLocks)) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
private:
|
|
~FinalizeOriginEvictionOp() = default;
|
|
|
|
virtual RefPtr<BoolPromise> Open() override;
|
|
|
|
virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
virtual void UnblockOpen() override;
|
|
};
|
|
|
|
class SaveOriginAccessTimeOp : public NormalOriginOperationBase {
|
|
const OriginMetadata mOriginMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
int64_t mTimestamp;
|
|
|
|
public:
|
|
SaveOriginAccessTimeOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginMetadata& aOriginMetadata,
|
|
int64_t aTimestamp)
|
|
: NormalOriginOperationBase(std::move(aQuotaManager),
|
|
"dom::quota::SaveOriginAccessTimeOp"),
|
|
mOriginMetadata(aOriginMetadata),
|
|
mTimestamp(aTimestamp) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
private:
|
|
~SaveOriginAccessTimeOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
virtual void SendResults() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearPrivateRepositoryOp : public ResolvableNormalOriginOp<bool> {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
explicit ClearPrivateRepositoryOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager),
|
|
"dom::quota::ClearPrivateRepositoryOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
private:
|
|
~ClearPrivateRepositoryOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override { return true; }
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ShutdownStorageOp : public ResolvableNormalOriginOp<bool> {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
explicit ShutdownStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager),
|
|
"dom::quota::ShutdownStorageOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
private:
|
|
~ShutdownStorageOp() = default;
|
|
|
|
#ifdef DEBUG
|
|
nsresult DirectoryOpen() override;
|
|
#endif
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override { return true; }
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
// A mix-in class to simplify operations that need to process every origin in
|
|
// one or more repositories. Sub-classes should call TraverseRepository in their
|
|
// DoDirectoryWork and implement a ProcessOrigin method for their per-origin
|
|
// logic.
|
|
class TraverseRepositoryHelper {
|
|
public:
|
|
TraverseRepositoryHelper() = default;
|
|
|
|
protected:
|
|
virtual ~TraverseRepositoryHelper() = default;
|
|
|
|
// If ProcessOrigin returns an error, TraverseRepository will immediately
|
|
// terminate and return the received error code to its caller.
|
|
nsresult TraverseRepository(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType);
|
|
|
|
private:
|
|
virtual const Atomic<bool>& GetIsCanceledFlag() = 0;
|
|
|
|
virtual nsresult ProcessOrigin(QuotaManager& aQuotaManager,
|
|
nsIFile& aOriginDir, const bool aPersistent,
|
|
const PersistenceType aPersistenceType) = 0;
|
|
};
|
|
|
|
class GetUsageOp final : public QuotaUsageRequestBase,
|
|
public TraverseRepositoryHelper {
|
|
nsTArray<OriginUsage> mOriginUsages;
|
|
nsTHashMap<nsCStringHashKey, uint32_t> mOriginUsagesIndex;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
bool mGetAll;
|
|
|
|
public:
|
|
GetUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams);
|
|
|
|
private:
|
|
~GetUsageOp() = default;
|
|
|
|
void ProcessOriginInternal(QuotaManager* aQuotaManager,
|
|
const PersistenceType aPersistenceType,
|
|
const nsACString& aOrigin,
|
|
const int64_t aTimestamp, const bool aPersisted,
|
|
const uint64_t aUsage);
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
const Atomic<bool>& GetIsCanceledFlag() override;
|
|
|
|
nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir,
|
|
const bool aPersistent,
|
|
const PersistenceType aPersistenceType) override;
|
|
|
|
void GetResponse(UsageRequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class GetOriginUsageOp final : public QuotaUsageRequestBase {
|
|
const OriginUsageParams mParams;
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
uint64_t mUsage;
|
|
uint64_t mFileUsage;
|
|
bool mFromMemory;
|
|
|
|
public:
|
|
GetOriginUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams);
|
|
|
|
private:
|
|
~GetOriginUsageOp() = default;
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(UsageRequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class StorageNameOp final : public QuotaRequestBase {
|
|
nsString mName;
|
|
|
|
public:
|
|
explicit StorageNameOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager);
|
|
|
|
private:
|
|
~StorageNameOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitializedRequestBase : public QuotaRequestBase {
|
|
protected:
|
|
bool mInitialized;
|
|
|
|
InitializedRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName);
|
|
|
|
private:
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class StorageInitializedOp final : public InitializedRequestBase {
|
|
public:
|
|
explicit StorageInitializedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: InitializedRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::StorageInitializedOp") {}
|
|
|
|
private:
|
|
~StorageInitializedOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class TemporaryStorageInitializedOp final : public InitializedRequestBase {
|
|
public:
|
|
explicit TemporaryStorageInitializedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: InitializedRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::StorageInitializedOp") {}
|
|
|
|
private:
|
|
~TemporaryStorageInitializedOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class InitOp final : public ResolvableNormalOriginOp<bool> {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
InitOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock);
|
|
|
|
private:
|
|
~InitOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitTemporaryStorageOp final : public QuotaRequestBase {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
explicit InitTemporaryStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager);
|
|
|
|
private:
|
|
~InitTemporaryStorageOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitializeOriginRequestBase : public QuotaRequestBase {
|
|
protected:
|
|
const PrincipalInfo mPrincipalInfo;
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
const PersistenceType mPersistenceType;
|
|
bool mCreated;
|
|
|
|
InitializeOriginRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName,
|
|
PersistenceType aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo);
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
private:
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitializePersistentOriginOp final : public InitializeOriginRequestBase {
|
|
public:
|
|
InitializePersistentOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~InitializePersistentOriginOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class InitializeTemporaryOriginOp final : public InitializeOriginRequestBase {
|
|
public:
|
|
InitializeTemporaryOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~InitializeTemporaryOriginOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class GetFullOriginMetadataOp : public QuotaRequestBase {
|
|
const GetFullOriginMetadataParams mParams;
|
|
// XXX Consider wrapping with LazyInitializedOnce
|
|
OriginMetadata mOriginMetadata;
|
|
Maybe<FullOriginMetadata> mMaybeFullOriginMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
GetFullOriginMetadataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const GetFullOriginMetadataParams& aParams);
|
|
|
|
private:
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearStorageOp final : public ResolvableNormalOriginOp<bool> {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
explicit ClearStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager);
|
|
|
|
private:
|
|
~ClearStorageOp() = default;
|
|
|
|
void DeleteFiles(QuotaManager& aQuotaManager);
|
|
|
|
void DeleteStorageFile(QuotaManager& aQuotaManager);
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearRequestBase : public QuotaRequestBase {
|
|
protected:
|
|
OriginScope mOriginScope;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
Nullable<PersistenceType> mPersistenceType;
|
|
Nullable<Client::Type> mClientType;
|
|
|
|
ClearRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName)
|
|
: QuotaRequestBase(std::move(aQuotaManager), aName) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
void DeleteFiles(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType);
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearOriginOp final : public ClearRequestBase {
|
|
const ClearResetOriginParams mParams;
|
|
const bool mMatchAll;
|
|
|
|
public:
|
|
ClearOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~ClearOriginOp() = default;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class ClearDataOp final : public ClearRequestBase {
|
|
const ClearDataParams mParams;
|
|
|
|
public:
|
|
ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~ClearDataOp() = default;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class ResetOriginOp final : public QuotaRequestBase {
|
|
nsCString mOrigin;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
Nullable<PersistenceType> mPersistenceType;
|
|
Nullable<Client::Type> mClientType;
|
|
|
|
public:
|
|
ResetOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~ResetOriginOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class PersistRequestBase : public QuotaRequestBase {
|
|
const PrincipalInfo mPrincipalInfo;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
protected:
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
|
|
protected:
|
|
PersistRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo);
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
private:
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class PersistedOp final : public PersistRequestBase {
|
|
bool mPersisted;
|
|
|
|
public:
|
|
PersistedOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~PersistedOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class PersistOp final : public PersistRequestBase {
|
|
public:
|
|
PersistOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams);
|
|
|
|
private:
|
|
~PersistOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
};
|
|
|
|
class EstimateOp final : public QuotaRequestBase {
|
|
const EstimateParams mParams;
|
|
OriginMetadata mOriginMetadata;
|
|
std::pair<uint64_t, uint64_t> mUsageAndLimit;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
EstimateOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const EstimateParams& aParams);
|
|
|
|
private:
|
|
~EstimateOp() = default;
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ListOriginsOp final : public QuotaRequestBase,
|
|
public TraverseRepositoryHelper {
|
|
// XXX Bug 1521541 will make each origin has it's own state.
|
|
nsTArray<nsCString> mOrigins;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
explicit ListOriginsOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager);
|
|
|
|
private:
|
|
~ListOriginsOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
const Atomic<bool>& GetIsCanceledFlag() override;
|
|
|
|
nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir,
|
|
const bool aPersistent,
|
|
const PersistenceType aPersistenceType) override;
|
|
|
|
void GetResponse(RequestResponse& aResponse) override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
RefPtr<OriginOperationBase> CreateFinalizeOriginEvictionOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
nsTArray<RefPtr<OriginDirectoryLock>>&& aLocks) {
|
|
return MakeRefPtr<FinalizeOriginEvictionOp>(std::move(aQuotaManager),
|
|
std::move(aLocks));
|
|
}
|
|
|
|
RefPtr<NormalOriginOperationBase> CreateSaveOriginAccessTimeOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginMetadata& aOriginMetadata, int64_t aTimestamp) {
|
|
return MakeRefPtr<SaveOriginAccessTimeOp>(std::move(aQuotaManager),
|
|
aOriginMetadata, aTimestamp);
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateClearPrivateRepositoryOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<ClearPrivateRepositoryOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateShutdownStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<ShutdownStorageOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<QuotaUsageRequestBase> CreateGetUsageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams) {
|
|
return MakeRefPtr<GetUsageOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaUsageRequestBase> CreateGetOriginUsageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams) {
|
|
return MakeRefPtr<GetOriginUsageOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateStorageNameOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<StorageNameOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateStorageInitializedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<StorageInitializedOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateTemporaryStorageInitializedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<TemporaryStorageInitializedOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock) {
|
|
return MakeRefPtr<InitOp>(std::move(aQuotaManager),
|
|
std::move(aDirectoryLock));
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateInitTemporaryStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<InitTemporaryStorageOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateInitializePersistentOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<InitializePersistentOriginOp>(std::move(aQuotaManager),
|
|
aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateInitializeTemporaryOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<InitializeTemporaryOriginOp>(std::move(aQuotaManager),
|
|
aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateGetFullOriginMetadataOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const GetFullOriginMetadataParams& aParams) {
|
|
return MakeRefPtr<GetFullOriginMetadataOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateClearStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<ClearStorageOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateClearOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<ClearOriginOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateClearDataOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<ClearDataOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateResetOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<ResetOriginOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreatePersistedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<PersistedOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreatePersistOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams) {
|
|
return MakeRefPtr<PersistOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateEstimateOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const EstimateParams& aParams) {
|
|
return MakeRefPtr<EstimateOp>(std::move(aQuotaManager), aParams);
|
|
}
|
|
|
|
RefPtr<QuotaRequestBase> CreateListOriginsOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<ListOriginsOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<BoolPromise> FinalizeOriginEvictionOp::Open() {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(!mLocks.IsEmpty());
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
nsresult FinalizeOriginEvictionOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("FinalizeOriginEvictionOp::DoDirectoryWork", OTHER);
|
|
|
|
for (const auto& lock : mLocks) {
|
|
aQuotaManager.OriginClearCompleted(
|
|
lock->GetPersistenceType(), lock->Origin(), Nullable<Client::Type>());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void FinalizeOriginEvictionOp::UnblockOpen() {
|
|
AssertIsOnOwningThread();
|
|
|
|
#ifdef DEBUG
|
|
NoteActorDestroyed();
|
|
#endif
|
|
|
|
mLocks.Clear();
|
|
}
|
|
|
|
RefPtr<BoolPromise> SaveOriginAccessTimeOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("SaveOriginAccessTimeOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
QM_TRY_INSPECT(const auto& file,
|
|
aQuotaManager.GetOriginDirectory(mOriginMetadata));
|
|
|
|
// The origin directory might not exist
|
|
// anymore, because it was deleted by a clear operation.
|
|
QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists));
|
|
|
|
if (exists) {
|
|
QM_TRY(MOZ_TO_RESULT(file->Append(nsLiteralString(METADATA_V2_FILE_NAME))));
|
|
|
|
QM_TRY_INSPECT(const auto& stream,
|
|
GetBinaryOutputStream(*file, FileFlag::Update));
|
|
MOZ_ASSERT(stream);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(stream->Write64(mTimestamp)));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void SaveOriginAccessTimeOp::SendResults() {
|
|
#ifdef DEBUG
|
|
NoteActorDestroyed();
|
|
#endif
|
|
}
|
|
|
|
void SaveOriginAccessTimeOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearPrivateRepositoryOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(PERSISTENCE_TYPE_PRIVATE),
|
|
OriginScope::FromNull(), Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult ClearPrivateRepositoryOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ClearPrivateRepositoryOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
QM_TRY_INSPECT(
|
|
const auto& directory,
|
|
QM_NewLocalFile(aQuotaManager.GetStoragePath(PERSISTENCE_TYPE_PRIVATE)));
|
|
|
|
nsresult rv = directory->Remove(true);
|
|
if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
|
|
// This should never fail if we've closed all storage connections
|
|
// correctly...
|
|
MOZ_ASSERT(false, "Failed to remove directory!");
|
|
}
|
|
|
|
aQuotaManager.RemoveQuotaForRepository(PERSISTENCE_TYPE_PRIVATE);
|
|
|
|
aQuotaManager.RepositoryClearCompleted(PERSISTENCE_TYPE_PRIVATE);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ClearPrivateRepositoryOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
RefPtr<BoolPromise> ShutdownStorageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
// Clear directory lock tables (which also saves origin access time) before
|
|
// acquiring the exclusive lock below. Otherwise, saving of origin access
|
|
// time would be scheduled after storage shutdown and that would initialize
|
|
// storage again in the end.
|
|
mQuotaManager->ClearDirectoryLockTables();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(), OriginScope::FromNull(),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
nsresult ShutdownStorageOp::DirectoryOpen() {
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
mDirectoryLock->AssertIsAcquiredExclusively();
|
|
|
|
return NormalOriginOperationBase::DirectoryOpen();
|
|
}
|
|
#endif
|
|
|
|
nsresult ShutdownStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ShutdownStorageOp::DoDirectoryWork", OTHER);
|
|
|
|
aQuotaManager.ShutdownStorageInternal();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ShutdownStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
nsresult TraverseRepositoryHelper::TraverseRepository(
|
|
QuotaManager& aQuotaManager, PersistenceType aPersistenceType) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY_INSPECT(
|
|
const auto& directory,
|
|
QM_NewLocalFile(aQuotaManager.GetStoragePath(aPersistenceType)));
|
|
|
|
QM_TRY_INSPECT(const bool& exists,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists));
|
|
|
|
if (!exists) {
|
|
return NS_OK;
|
|
}
|
|
|
|
QM_TRY(CollectEachFileAtomicCancelable(
|
|
*directory, GetIsCanceledFlag(),
|
|
[this, aPersistenceType, &aQuotaManager,
|
|
persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT](
|
|
const nsCOMPtr<nsIFile>& originDir) -> Result<Ok, nsresult> {
|
|
QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*originDir));
|
|
|
|
switch (dirEntryKind) {
|
|
case nsIFileKind::ExistsAsDirectory:
|
|
QM_TRY(MOZ_TO_RESULT(ProcessOrigin(aQuotaManager, *originDir,
|
|
persistent, aPersistenceType)));
|
|
break;
|
|
|
|
case nsIFileKind::ExistsAsFile: {
|
|
QM_TRY_INSPECT(const auto& leafName,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
|
|
nsAutoString, originDir, GetLeafName));
|
|
|
|
// Unknown files during getting usages are allowed. Just warn if we
|
|
// find them.
|
|
if (!IsOSMetadata(leafName)) {
|
|
UNKNOWN_FILE_WARNING(leafName);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case nsIFileKind::DoesNotExist:
|
|
// Ignore files that got removed externally while iterating.
|
|
break;
|
|
}
|
|
|
|
return Ok{};
|
|
}));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
GetUsageOp::GetUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams)
|
|
: QuotaUsageRequestBase(std::move(aQuotaManager), "dom::quota::GetUsageOp"),
|
|
mGetAll(aParams.get_AllUsageParams().getAll()) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams);
|
|
}
|
|
|
|
void GetUsageOp::ProcessOriginInternal(QuotaManager* aQuotaManager,
|
|
const PersistenceType aPersistenceType,
|
|
const nsACString& aOrigin,
|
|
const int64_t aTimestamp,
|
|
const bool aPersisted,
|
|
const uint64_t aUsage) {
|
|
if (!mGetAll && aQuotaManager->IsOriginInternal(aOrigin)) {
|
|
return;
|
|
}
|
|
|
|
// We can't store pointers to OriginUsage objects in the hashtable
|
|
// since AppendElement() reallocates its internal array buffer as number
|
|
// of elements grows.
|
|
const auto& originUsage =
|
|
mOriginUsagesIndex.WithEntryHandle(aOrigin, [&](auto&& entry) {
|
|
if (entry) {
|
|
return WrapNotNullUnchecked(&mOriginUsages[entry.Data()]);
|
|
}
|
|
|
|
entry.Insert(mOriginUsages.Length());
|
|
|
|
return mOriginUsages.EmplaceBack(nsCString{aOrigin}, false, 0, 0);
|
|
});
|
|
|
|
if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
|
|
originUsage->persisted() = aPersisted;
|
|
}
|
|
|
|
originUsage->usage() = originUsage->usage() + aUsage;
|
|
|
|
originUsage->lastAccessed() =
|
|
std::max<int64_t>(originUsage->lastAccessed(), aTimestamp);
|
|
}
|
|
|
|
const Atomic<bool>& GetUsageOp::GetIsCanceledFlag() {
|
|
AssertIsOnIOThread();
|
|
|
|
return mCanceled;
|
|
}
|
|
|
|
// XXX Remove aPersistent
|
|
// XXX Remove aPersistenceType once GetUsageForOrigin uses the persistence
|
|
// type from OriginMetadata
|
|
nsresult GetUsageOp::ProcessOrigin(QuotaManager& aQuotaManager,
|
|
nsIFile& aOriginDir, const bool aPersistent,
|
|
const PersistenceType aPersistenceType) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY_UNWRAP(auto maybeMetadata,
|
|
QM_OR_ELSE_WARN_IF(
|
|
// Expression
|
|
aQuotaManager.LoadFullOriginMetadataWithRestore(&aOriginDir)
|
|
.map([](auto metadata) -> Maybe<FullOriginMetadata> {
|
|
return Some(std::move(metadata));
|
|
}),
|
|
// Predicate.
|
|
IsSpecificError<NS_ERROR_MALFORMED_URI>,
|
|
// Fallback.
|
|
ErrToDefaultOk<Maybe<FullOriginMetadata>>));
|
|
|
|
if (!maybeMetadata) {
|
|
// Unknown directories during getting usage are allowed. Just warn if we
|
|
// find them.
|
|
QM_TRY_INSPECT(const auto& leafName,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, aOriginDir,
|
|
GetLeafName));
|
|
|
|
UNKNOWN_FILE_WARNING(leafName);
|
|
return NS_OK;
|
|
}
|
|
|
|
auto metadata = maybeMetadata.extract();
|
|
|
|
QM_TRY_INSPECT(const auto& usageInfo,
|
|
GetUsageForOrigin(aQuotaManager, aPersistenceType, metadata));
|
|
|
|
ProcessOriginInternal(&aQuotaManager, aPersistenceType, metadata.mOrigin,
|
|
metadata.mLastAccessTime, metadata.mPersisted,
|
|
usageInfo.TotalUsage().valueOr(0));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> GetUsageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(), OriginScope::FromNull(),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult GetUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
nsresult rv;
|
|
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
rv = TraverseRepository(aQuotaManager, type);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// TraverseRepository above only consulted the filesystem. We also need to
|
|
// consider origins which may have pending quota usage, such as buffered
|
|
// LocalStorage writes for an origin which didn't previously have any
|
|
// LocalStorage data.
|
|
|
|
aQuotaManager.CollectPendingOriginsForListing(
|
|
[this, &aQuotaManager](const auto& originInfo) {
|
|
ProcessOriginInternal(
|
|
&aQuotaManager, originInfo->GetGroupInfo()->GetPersistenceType(),
|
|
originInfo->Origin(), originInfo->LockedAccessTime(),
|
|
originInfo->LockedPersisted(), originInfo->LockedUsage());
|
|
});
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GetUsageOp::GetResponse(UsageRequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = AllUsageResponse();
|
|
|
|
aResponse.get_AllUsageResponse().originUsages() = std::move(mOriginUsages);
|
|
}
|
|
|
|
void GetUsageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
GetOriginUsageOp::GetOriginUsageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams)
|
|
: QuotaUsageRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::GetOriginUsageOp"),
|
|
mParams(aParams.get_OriginUsageParams()),
|
|
mUsage(0),
|
|
mFileUsage(0) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams);
|
|
|
|
// Overwrite GetOriginUsageOp default values.
|
|
mFromMemory = mParams.fromMemory();
|
|
}
|
|
|
|
nsresult GetOriginUsageOp::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
mPrincipalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo()));
|
|
|
|
mPrincipalMetadata.AssertInvariants();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> GetOriginUsageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(),
|
|
OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult GetOriginUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(mUsage == 0);
|
|
MOZ_ASSERT(mFileUsage == 0);
|
|
|
|
AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
if (mFromMemory) {
|
|
// Ensure temporary storage is initialized. If temporary storage hasn't been
|
|
// initialized yet, the method will initialize it by traversing the
|
|
// repositories for temporary and default storage (including our origin).
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureTemporaryStorageIsInitialized()));
|
|
|
|
// Get cached usage (the method doesn't have to stat any files). File usage
|
|
// is not tracked in memory separately, so just add to the total usage.
|
|
mUsage = aQuotaManager.GetOriginUsage(mPrincipalMetadata);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
UsageInfo usageInfo;
|
|
|
|
// Add all the persistent/temporary/default storage files we care about.
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
const OriginMetadata originMetadata = {mPrincipalMetadata, type};
|
|
|
|
auto usageInfoOrErr =
|
|
GetUsageForOrigin(aQuotaManager, type, originMetadata);
|
|
if (NS_WARN_IF(usageInfoOrErr.isErr())) {
|
|
return usageInfoOrErr.unwrapErr();
|
|
}
|
|
|
|
usageInfo += usageInfoOrErr.unwrap();
|
|
}
|
|
|
|
mUsage = usageInfo.TotalUsage().valueOr(0);
|
|
mFileUsage = usageInfo.FileUsage().valueOr(0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
OriginUsageResponse usageResponse;
|
|
|
|
usageResponse.usage() = mUsage;
|
|
usageResponse.fileUsage() = mFileUsage;
|
|
|
|
aResponse = usageResponse;
|
|
}
|
|
|
|
void GetOriginUsageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
StorageNameOp::StorageNameOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: QuotaRequestBase(std::move(aQuotaManager), "dom::quota::StorageNameOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> StorageNameOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
nsresult StorageNameOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("StorageNameOp::DoDirectoryWork", OTHER);
|
|
|
|
mName = aQuotaManager.GetStorageName();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void StorageNameOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
StorageNameResponse storageNameResponse;
|
|
|
|
storageNameResponse.name() = mName;
|
|
|
|
aResponse = storageNameResponse;
|
|
}
|
|
|
|
void StorageNameOp::CloseDirectory() { AssertIsOnOwningThread(); }
|
|
|
|
InitializedRequestBase::InitializedRequestBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName)
|
|
: QuotaRequestBase(std::move(aQuotaManager), aName), mInitialized(false) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitializedRequestBase::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
void InitializedRequestBase::CloseDirectory() { AssertIsOnOwningThread(); }
|
|
|
|
nsresult StorageInitializedOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("StorageInitializedOp::DoDirectoryWork", OTHER);
|
|
|
|
mInitialized = aQuotaManager.IsStorageInitializedInternal();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void StorageInitializedOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
StorageInitializedResponse storageInitializedResponse;
|
|
|
|
storageInitializedResponse.initialized() = mInitialized;
|
|
|
|
aResponse = storageInitializedResponse;
|
|
}
|
|
|
|
nsresult TemporaryStorageInitializedOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("TemporaryStorageInitializedOp::DoDirectoryWork", OTHER);
|
|
|
|
mInitialized = aQuotaManager.IsTemporaryStorageInitialized();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void TemporaryStorageInitializedOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
TemporaryStorageInitializedResponse temporaryStorageInitializedResponse;
|
|
|
|
temporaryStorageInitializedResponse.initialized() = mInitialized;
|
|
|
|
aResponse = temporaryStorageInitializedResponse;
|
|
}
|
|
|
|
InitOp::InitOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager), "dom::quota::InitOp"),
|
|
mDirectoryLock(std::move(aDirectoryLock)) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
nsresult InitOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitOp::GetResolveValue() { return true; }
|
|
|
|
void InitOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
InitTemporaryStorageOp::InitTemporaryStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: QuotaRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::InitTemporaryStorageOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitTemporaryStorageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(), OriginScope::FromNull(),
|
|
Nullable<Client::Type>(), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult InitTemporaryStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitTemporaryStorageOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()),
|
|
NS_ERROR_NOT_INITIALIZED);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureTemporaryStorageIsInitialized()));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void InitTemporaryStorageOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = InitTemporaryStorageResponse();
|
|
}
|
|
|
|
void InitTemporaryStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
InitializeOriginRequestBase::InitializeOriginRequestBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName,
|
|
const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo)
|
|
: QuotaRequestBase(std::move(aQuotaManager), aName),
|
|
mPrincipalInfo(aPrincipalInfo),
|
|
mPersistenceType(aPersistenceType),
|
|
mCreated(false) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult InitializeOriginRequestBase::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
mPrincipalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo));
|
|
|
|
mPrincipalMetadata.AssertInvariants();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitializeOriginRequestBase::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(mPersistenceType),
|
|
OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
Nullable<Client::Type>(), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
void InitializeOriginRequestBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
InitializePersistentOriginOp::InitializePersistentOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: InitializeOriginRequestBase(
|
|
std::move(aQuotaManager), "dom::quota::InitializePersistentOriginOp",
|
|
PERSISTENCE_TYPE_PERSISTENT,
|
|
aParams.get_InitializePersistentOriginParams().principalInfo()) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aParams.type() ==
|
|
RequestParams::TInitializePersistentOriginParams);
|
|
}
|
|
|
|
nsresult InitializePersistentOriginOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitializePersistentOriginOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()),
|
|
NS_ERROR_NOT_INITIALIZED);
|
|
|
|
QM_TRY_UNWRAP(mCreated,
|
|
(aQuotaManager
|
|
.EnsurePersistentOriginIsInitialized(OriginMetadata{
|
|
mPrincipalMetadata, PERSISTENCE_TYPE_PERSISTENT})
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void InitializePersistentOriginOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = InitializePersistentOriginResponse(mCreated);
|
|
}
|
|
|
|
InitializeTemporaryOriginOp::InitializeTemporaryOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: InitializeOriginRequestBase(
|
|
std::move(aQuotaManager), "dom::quota::InitializeTemporaryOriginOp",
|
|
aParams.get_InitializeTemporaryOriginParams().persistenceType(),
|
|
aParams.get_InitializeTemporaryOriginParams().principalInfo()) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TInitializeTemporaryOriginParams);
|
|
}
|
|
|
|
nsresult InitializeTemporaryOriginOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitializeTemporaryOriginOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()),
|
|
NS_ERROR_NOT_INITIALIZED);
|
|
|
|
QM_TRY(OkIf(aQuotaManager.IsTemporaryStorageInitialized()),
|
|
NS_ERROR_NOT_INITIALIZED);
|
|
|
|
QM_TRY_UNWRAP(mCreated,
|
|
(aQuotaManager
|
|
.EnsureTemporaryOriginIsInitialized(
|
|
mPersistenceType,
|
|
OriginMetadata{mPrincipalMetadata, mPersistenceType})
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void InitializeTemporaryOriginOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = InitializeTemporaryOriginResponse(mCreated);
|
|
}
|
|
|
|
GetFullOriginMetadataOp::GetFullOriginMetadataOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const GetFullOriginMetadataParams& aParams)
|
|
: QuotaRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::GetFullOriginMetadataOp"),
|
|
mParams(aParams) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult GetFullOriginMetadataOp::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
PrincipalMetadata principalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo()));
|
|
|
|
principalMetadata.AssertInvariants();
|
|
|
|
mOriginMetadata = {std::move(principalMetadata), mParams.persistenceType()};
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> GetFullOriginMetadataOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult GetFullOriginMetadataOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("GetFullOriginMetadataOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
// Ensure temporary storage is initialized. If temporary storage hasn't
|
|
// been initialized yet, the method will initialize it by traversing the
|
|
// repositories for temporary and default storage (including our origin).
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureTemporaryStorageIsInitialized()));
|
|
|
|
// Get metadata cached in memory (the method doesn't have to stat any
|
|
// files).
|
|
mMaybeFullOriginMetadata =
|
|
aQuotaManager.GetFullOriginMetadata(mOriginMetadata);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GetFullOriginMetadataOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = GetFullOriginMetadataResponse();
|
|
aResponse.get_GetFullOriginMetadataResponse().maybeFullOriginMetadata() =
|
|
std::move(mMaybeFullOriginMetadata);
|
|
}
|
|
|
|
void GetFullOriginMetadataOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
ClearStorageOp::ClearStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager),
|
|
"dom::quota::ClearStorageOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
void ClearStorageOp::DeleteFiles(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
nsresult rv = aQuotaManager.AboutToClearOrigins(Nullable<PersistenceType>(),
|
|
OriginScope::FromNull(),
|
|
Nullable<Client::Type>());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
auto directoryOrErr = QM_NewLocalFile(aQuotaManager.GetStoragePath());
|
|
if (NS_WARN_IF(directoryOrErr.isErr())) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIFile> directory = directoryOrErr.unwrap();
|
|
|
|
rv = directory->Remove(true);
|
|
if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
|
|
// This should never fail if we've closed all storage connections
|
|
// correctly...
|
|
MOZ_ASSERT(false, "Failed to remove storage directory!");
|
|
}
|
|
}
|
|
|
|
void ClearStorageOp::DeleteStorageFile(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY_INSPECT(const auto& storageFile,
|
|
QM_NewLocalFile(aQuotaManager.GetBasePath()), QM_VOID);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(storageFile->Append(aQuotaManager.GetStorageName() +
|
|
kSQLiteSuffix)),
|
|
QM_VOID);
|
|
|
|
const nsresult rv = storageFile->Remove(true);
|
|
if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
|
|
// This should never fail if we've closed the storage connection
|
|
// correctly...
|
|
MOZ_ASSERT(false, "Failed to remove storage file!");
|
|
}
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearStorageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
// Clear directory lock tables (which also saves origin access time) before
|
|
// acquiring the exclusive lock below. Otherwise, saving of origin access
|
|
// time would be scheduled after storage clearing and that would initialize
|
|
// storage again in the end.
|
|
mQuotaManager->ClearDirectoryLockTables();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(), OriginScope::FromNull(),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult ClearStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ClearStorageOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
DeleteFiles(aQuotaManager);
|
|
|
|
aQuotaManager.RemoveQuota();
|
|
|
|
aQuotaManager.ShutdownStorageInternal();
|
|
|
|
DeleteStorageFile(aQuotaManager);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool ClearStorageOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
static Result<nsCOMPtr<nsIFile>, QMResult> OpenToBeRemovedDirectory(
|
|
const nsAString& aStoragePath) {
|
|
QM_TRY_INSPECT(const auto& dir,
|
|
QM_TO_RESULT_TRANSFORM(QM_NewLocalFile(aStoragePath)));
|
|
QM_TRY(QM_TO_RESULT(dir->Append(u"to-be-removed"_ns)));
|
|
|
|
nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
|
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
|
|
return dir;
|
|
}
|
|
return Err(QMResult(rv));
|
|
}
|
|
|
|
static Result<Ok, QMResult> RemoveOrMoveToDir(nsIFile& aFile,
|
|
nsIFile* aMoveTargetDir) {
|
|
if (!aMoveTargetDir) {
|
|
QM_TRY(QM_TO_RESULT(aFile.Remove(true)));
|
|
return Ok();
|
|
}
|
|
|
|
nsIDToCString uuid(nsID::GenerateUUID());
|
|
NS_ConvertUTF8toUTF16 subDirName(uuid.get(), NSID_LENGTH - 1);
|
|
QM_TRY(QM_TO_RESULT(aFile.MoveTo(aMoveTargetDir, subDirName)));
|
|
return Ok();
|
|
}
|
|
|
|
void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.AboutToClearOrigins(
|
|
Nullable<PersistenceType>(aPersistenceType), mOriginScope,
|
|
mClientType)),
|
|
QM_VOID);
|
|
|
|
QM_TRY_INSPECT(
|
|
const auto& directory,
|
|
QM_NewLocalFile(aQuotaManager.GetStoragePath(aPersistenceType)), QM_VOID);
|
|
|
|
QM_TRY_INSPECT(const bool& exists,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists), QM_VOID);
|
|
|
|
if (!exists) {
|
|
return;
|
|
}
|
|
|
|
nsTArray<nsCOMPtr<nsIFile>> directoriesForRemovalRetry;
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStep(
|
|
"ClearRequestBase: Starting deleting files"_ns);
|
|
nsCOMPtr<nsIFile> toBeRemovedDir;
|
|
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownTeardown)) {
|
|
QM_WARNONLY_TRY_UNWRAP(
|
|
auto result, OpenToBeRemovedDirectory(aQuotaManager.GetStoragePath()));
|
|
toBeRemovedDir = result.valueOr(nullptr);
|
|
}
|
|
QM_TRY(
|
|
CollectEachFile(
|
|
*directory,
|
|
[&originScope = mOriginScope, aPersistenceType, &aQuotaManager,
|
|
&directoriesForRemovalRetry, &toBeRemovedDir,
|
|
this](nsCOMPtr<nsIFile>&& file) -> mozilla::Result<Ok, nsresult> {
|
|
QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file));
|
|
|
|
QM_TRY_INSPECT(const auto& leafName,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, file,
|
|
GetLeafName));
|
|
|
|
switch (dirEntryKind) {
|
|
case nsIFileKind::ExistsAsDirectory: {
|
|
QM_TRY_UNWRAP(
|
|
auto maybeMetadata,
|
|
QM_OR_ELSE_WARN_IF(
|
|
// Expression
|
|
aQuotaManager.GetOriginMetadata(file).map(
|
|
[](auto metadata) -> Maybe<OriginMetadata> {
|
|
return Some(std::move(metadata));
|
|
}),
|
|
// Predicate.
|
|
IsSpecificError<NS_ERROR_MALFORMED_URI>,
|
|
// Fallback.
|
|
ErrToDefaultOk<Maybe<OriginMetadata>>));
|
|
|
|
if (!maybeMetadata) {
|
|
// Unknown directories during clearing are allowed. Just warn
|
|
// if we find them.
|
|
UNKNOWN_FILE_WARNING(leafName);
|
|
break;
|
|
}
|
|
|
|
auto metadata = maybeMetadata.extract();
|
|
|
|
MOZ_ASSERT(metadata.mPersistenceType == aPersistenceType);
|
|
|
|
// Skip the origin directory if it doesn't match the pattern.
|
|
if (!originScope.Matches(
|
|
OriginScope::FromOrigin(metadata.mOrigin))) {
|
|
break;
|
|
}
|
|
|
|
if (!mClientType.IsNull()) {
|
|
nsAutoString clientDirectoryName;
|
|
QM_TRY(
|
|
OkIf(Client::TypeToText(mClientType.Value(),
|
|
clientDirectoryName, fallible)),
|
|
Err(NS_ERROR_FAILURE));
|
|
|
|
QM_TRY(MOZ_TO_RESULT(file->Append(clientDirectoryName)));
|
|
|
|
QM_TRY_INSPECT(const bool& exists,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists));
|
|
|
|
if (!exists) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We can't guarantee that this will always succeed on
|
|
// Windows...
|
|
QM_WARNONLY_TRY(
|
|
RemoveOrMoveToDir(*file, toBeRemovedDir), [&](const auto&) {
|
|
directoriesForRemovalRetry.AppendElement(std::move(file));
|
|
});
|
|
|
|
const bool initialized =
|
|
aPersistenceType == PERSISTENCE_TYPE_PERSISTENT
|
|
? aQuotaManager.IsOriginInitialized(metadata.mOrigin)
|
|
: aQuotaManager.IsTemporaryStorageInitialized();
|
|
|
|
// If it hasn't been initialized, we don't need to update the
|
|
// quota and notify the removing client.
|
|
if (!initialized) {
|
|
break;
|
|
}
|
|
|
|
if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
|
|
if (mClientType.IsNull()) {
|
|
aQuotaManager.RemoveQuotaForOrigin(aPersistenceType,
|
|
metadata);
|
|
} else {
|
|
aQuotaManager.ResetUsageForClient(
|
|
ClientMetadata{metadata, mClientType.Value()});
|
|
}
|
|
}
|
|
|
|
aQuotaManager.OriginClearCompleted(
|
|
aPersistenceType, metadata.mOrigin, mClientType);
|
|
|
|
break;
|
|
}
|
|
|
|
case nsIFileKind::ExistsAsFile: {
|
|
// Unknown files during clearing are allowed. Just warn if we
|
|
// find them.
|
|
if (!IsOSMetadata(leafName)) {
|
|
UNKNOWN_FILE_WARNING(leafName);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case nsIFileKind::DoesNotExist:
|
|
// Ignore files that got removed externally while iterating.
|
|
break;
|
|
}
|
|
|
|
return Ok{};
|
|
}),
|
|
QM_VOID);
|
|
|
|
// Retry removing any directories that failed to be removed earlier now.
|
|
//
|
|
// XXX This will still block this operation. We might instead dispatch a
|
|
// runnable to our own thread for each retry round with a timer. We must
|
|
// ensure that the directory lock is upheld until we complete or give up
|
|
// though.
|
|
for (uint32_t index = 0; index < 10; index++) {
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() {
|
|
return nsPrintfCString(
|
|
"ClearRequestBase: Starting repeated directory removal #%d", index);
|
|
});
|
|
|
|
for (auto&& file : std::exchange(directoriesForRemovalRetry,
|
|
nsTArray<nsCOMPtr<nsIFile>>{})) {
|
|
QM_WARNONLY_TRY(
|
|
RemoveOrMoveToDir(*file, toBeRemovedDir),
|
|
([&directoriesForRemovalRetry, &file](const auto&) {
|
|
directoriesForRemovalRetry.AppendElement(std::move(file));
|
|
}));
|
|
}
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() {
|
|
return nsPrintfCString(
|
|
"ClearRequestBase: Completed repeated directory removal #%d", index);
|
|
});
|
|
|
|
if (directoriesForRemovalRetry.IsEmpty()) {
|
|
break;
|
|
}
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() {
|
|
return nsPrintfCString("ClearRequestBase: Before sleep #%d", index);
|
|
});
|
|
|
|
PR_Sleep(PR_MillisecondsToInterval(200));
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() {
|
|
return nsPrintfCString("ClearRequestBase: After sleep #%d", index);
|
|
});
|
|
}
|
|
|
|
QM_WARNONLY_TRY(OkIf(directoriesForRemovalRetry.IsEmpty()));
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStep(
|
|
"ClearRequestBase: Completed deleting files"_ns);
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearRequestBase::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
mPersistenceType, mOriginScope, mClientType, /* aExclusive */ true);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult ClearRequestBase::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
if (mPersistenceType.IsNull()) {
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
DeleteFiles(aQuotaManager, type);
|
|
}
|
|
} else {
|
|
DeleteFiles(aQuotaManager, mPersistenceType.Value());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ClearRequestBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
ClearOriginOp::ClearOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearOriginOp"),
|
|
mParams(aParams.get_ClearOriginParams().commonParams()),
|
|
mMatchAll(aParams.get_ClearOriginParams().matchAll()) {
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams);
|
|
|
|
// Figure out which origin we're dealing with.
|
|
const auto origin = QuotaManager::GetOriginFromValidatedPrincipalInfo(
|
|
mParams.principalInfo());
|
|
|
|
if (mMatchAll) {
|
|
mOriginScope.SetFromPrefix(origin);
|
|
} else {
|
|
mOriginScope.SetFromOrigin(origin);
|
|
}
|
|
|
|
if (mParams.persistenceTypeIsExplicit()) {
|
|
mPersistenceType.SetValue(mParams.persistenceType());
|
|
}
|
|
|
|
if (mParams.clientTypeIsExplicit()) {
|
|
mClientType.SetValue(mParams.clientType());
|
|
}
|
|
}
|
|
|
|
void ClearOriginOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = ClearOriginResponse();
|
|
}
|
|
|
|
ClearDataOp::ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearDataOp"),
|
|
mParams(aParams) {
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TClearDataParams);
|
|
|
|
mOriginScope.SetFromPattern(mParams.pattern());
|
|
}
|
|
|
|
void ClearDataOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = ClearDataResponse();
|
|
}
|
|
|
|
ResetOriginOp::ResetOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: QuotaRequestBase(std::move(aQuotaManager), "dom::quota::ResetOriginOp") {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TResetOriginParams);
|
|
|
|
const ClearResetOriginParams& params =
|
|
aParams.get_ResetOriginParams().commonParams();
|
|
|
|
mOrigin =
|
|
QuotaManager::GetOriginFromValidatedPrincipalInfo(params.principalInfo());
|
|
|
|
if (params.persistenceTypeIsExplicit()) {
|
|
mPersistenceType.SetValue(params.persistenceType());
|
|
}
|
|
|
|
if (params.clientTypeIsExplicit()) {
|
|
mClientType.SetValue(params.clientType());
|
|
}
|
|
}
|
|
|
|
RefPtr<BoolPromise> ResetOriginOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
mPersistenceType, OriginScope::FromOrigin(mOrigin), mClientType,
|
|
/* aExclusive */ true);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult ResetOriginOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ResetOriginOp::DoDirectoryWork", OTHER);
|
|
|
|
// All the work is handled by NormalOriginOperationBase parent class. In this
|
|
// particular case, we just needed to acquire an exclusive directory lock and
|
|
// that's it.
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ResetOriginOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = ResetOriginResponse();
|
|
}
|
|
|
|
void ResetOriginOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
PersistRequestBase::PersistRequestBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo)
|
|
: QuotaRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::PersistRequestBase"),
|
|
mPrincipalInfo(aPrincipalInfo) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult PersistRequestBase::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
// Figure out which origin we're dealing with.
|
|
QM_TRY_UNWRAP(
|
|
mPrincipalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo));
|
|
|
|
mPrincipalMetadata.AssertInvariants();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> PersistRequestBase::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
|
OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
Nullable<Client::Type>(), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
void PersistRequestBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
PersistedOp::PersistedOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: PersistRequestBase(std::move(aQuotaManager),
|
|
aParams.get_PersistedParams().principalInfo()),
|
|
mPersisted(false) {
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TPersistedParams);
|
|
}
|
|
|
|
nsresult PersistedOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("PersistedOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
const OriginMetadata originMetadata = {mPrincipalMetadata,
|
|
PERSISTENCE_TYPE_DEFAULT};
|
|
|
|
Nullable<bool> persisted = aQuotaManager.OriginPersisted(originMetadata);
|
|
|
|
if (!persisted.IsNull()) {
|
|
mPersisted = persisted.Value();
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we get here, it means the origin hasn't been initialized yet.
|
|
// Try to get the persisted flag from directory metadata on disk.
|
|
|
|
QM_TRY_INSPECT(const auto& directory,
|
|
aQuotaManager.GetOriginDirectory(originMetadata));
|
|
|
|
QM_TRY_INSPECT(const bool& exists,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists));
|
|
|
|
if (exists) {
|
|
// Get the metadata. We only use the persisted flag.
|
|
QM_TRY_INSPECT(const auto& metadata,
|
|
aQuotaManager.LoadFullOriginMetadataWithRestore(directory));
|
|
|
|
mPersisted = metadata.mPersisted;
|
|
} else {
|
|
// The directory has not been created yet.
|
|
mPersisted = false;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void PersistedOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
PersistedResponse persistedResponse;
|
|
persistedResponse.persisted() = mPersisted;
|
|
|
|
aResponse = persistedResponse;
|
|
}
|
|
|
|
PersistOp::PersistOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const RequestParams& aParams)
|
|
: PersistRequestBase(std::move(aQuotaManager),
|
|
aParams.get_PersistParams().principalInfo()) {
|
|
MOZ_ASSERT(aParams.type() == RequestParams::TPersistParams);
|
|
}
|
|
|
|
nsresult PersistOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
const OriginMetadata originMetadata = {mPrincipalMetadata,
|
|
PERSISTENCE_TYPE_DEFAULT};
|
|
|
|
AUTO_PROFILER_LABEL("PersistOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
// Update directory metadata on disk first. Then, create/update the originInfo
|
|
// if needed.
|
|
QM_TRY_INSPECT(const auto& directory,
|
|
aQuotaManager.GetOriginDirectory(originMetadata));
|
|
|
|
QM_TRY_INSPECT(const bool& created,
|
|
aQuotaManager.EnsureOriginDirectory(*directory));
|
|
|
|
if (created) {
|
|
int64_t timestamp;
|
|
|
|
// Origin directory has been successfully created.
|
|
// Create OriginInfo too if temporary storage was already initialized.
|
|
if (aQuotaManager.IsTemporaryStorageInitialized()) {
|
|
timestamp = aQuotaManager.NoteOriginDirectoryCreated(
|
|
originMetadata, /* aPersisted */ true);
|
|
} else {
|
|
timestamp = PR_Now();
|
|
}
|
|
|
|
QM_TRY(MOZ_TO_RESULT(QuotaManager::CreateDirectoryMetadata2(
|
|
*directory, timestamp,
|
|
/* aPersisted */ true, originMetadata)));
|
|
} else {
|
|
// Get the metadata (restore the metadata file if necessary). We only use
|
|
// the persisted flag.
|
|
QM_TRY_INSPECT(const auto& metadata,
|
|
aQuotaManager.LoadFullOriginMetadataWithRestore(directory));
|
|
|
|
if (!metadata.mPersisted) {
|
|
QM_TRY_INSPECT(const auto& file,
|
|
CloneFileAndAppend(
|
|
*directory, nsLiteralString(METADATA_V2_FILE_NAME)));
|
|
|
|
QM_TRY_INSPECT(const auto& stream,
|
|
GetBinaryOutputStream(*file, FileFlag::Update));
|
|
|
|
MOZ_ASSERT(stream);
|
|
|
|
// Update origin access time while we are here.
|
|
QM_TRY(MOZ_TO_RESULT(stream->Write64(PR_Now())));
|
|
|
|
// Set the persisted flag to true.
|
|
QM_TRY(MOZ_TO_RESULT(stream->WriteBoolean(true)));
|
|
}
|
|
|
|
// Directory metadata has been successfully updated.
|
|
// Update OriginInfo too if temporary storage was already initialized.
|
|
if (aQuotaManager.IsTemporaryStorageInitialized()) {
|
|
aQuotaManager.PersistOrigin(originMetadata);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void PersistOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = PersistResponse();
|
|
}
|
|
|
|
EstimateOp::EstimateOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const EstimateParams& aParams)
|
|
: QuotaRequestBase(std::move(aQuotaManager), "dom::quota::EstimateOp"),
|
|
mParams(aParams) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult EstimateOp::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
PrincipalMetadata principalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo()));
|
|
|
|
principalMetadata.AssertInvariants();
|
|
|
|
mOriginMetadata = {std::move(principalMetadata), PERSISTENCE_TYPE_DEFAULT};
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> EstimateOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
// XXX In theory, we should be locking entire group, not just one origin.
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult EstimateOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("EstimateOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
// Ensure temporary storage is initialized. If temporary storage hasn't been
|
|
// initialized yet, the method will initialize it by traversing the
|
|
// repositories for temporary and default storage (including origins belonging
|
|
// to our group).
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureTemporaryStorageIsInitialized()));
|
|
|
|
// Get cached usage (the method doesn't have to stat any files).
|
|
mUsageAndLimit = aQuotaManager.GetUsageAndLimitForEstimate(mOriginMetadata);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void EstimateOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
EstimateResponse estimateResponse;
|
|
|
|
estimateResponse.usage() = mUsageAndLimit.first;
|
|
estimateResponse.limit() = mUsageAndLimit.second;
|
|
|
|
aResponse = estimateResponse;
|
|
}
|
|
|
|
void EstimateOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
ListOriginsOp::ListOriginsOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: QuotaRequestBase(std::move(aQuotaManager), "dom::quota::ListOriginsOp"),
|
|
TraverseRepositoryHelper() {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> ListOriginsOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable<PersistenceType>(), OriginScope::FromNull(),
|
|
Nullable<Client::Type>(), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
nsresult ListOriginsOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ListOriginsOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal()));
|
|
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
QM_TRY(MOZ_TO_RESULT(TraverseRepository(aQuotaManager, type)));
|
|
}
|
|
|
|
// TraverseRepository above only consulted the file-system to get a list of
|
|
// known origins, but we also need to include origins that have pending quota
|
|
// usage.
|
|
|
|
aQuotaManager.CollectPendingOriginsForListing([this](const auto& originInfo) {
|
|
mOrigins.AppendElement(originInfo->Origin());
|
|
});
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
const Atomic<bool>& ListOriginsOp::GetIsCanceledFlag() {
|
|
AssertIsOnIOThread();
|
|
|
|
return mCanceled;
|
|
}
|
|
|
|
nsresult ListOriginsOp::ProcessOrigin(QuotaManager& aQuotaManager,
|
|
nsIFile& aOriginDir,
|
|
const bool aPersistent,
|
|
const PersistenceType aPersistenceType) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY_UNWRAP(auto maybeMetadata,
|
|
QM_OR_ELSE_WARN_IF(
|
|
// Expression
|
|
aQuotaManager.GetOriginMetadata(&aOriginDir)
|
|
.map([](auto metadata) -> Maybe<OriginMetadata> {
|
|
return Some(std::move(metadata));
|
|
}),
|
|
// Predicate.
|
|
IsSpecificError<NS_ERROR_MALFORMED_URI>,
|
|
// Fallback.
|
|
ErrToDefaultOk<Maybe<OriginMetadata>>));
|
|
|
|
if (!maybeMetadata) {
|
|
// Unknown directories during listing are allowed. Just warn if we find
|
|
// them.
|
|
QM_TRY_INSPECT(const auto& leafName,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, aOriginDir,
|
|
GetLeafName));
|
|
|
|
UNKNOWN_FILE_WARNING(leafName);
|
|
return NS_OK;
|
|
}
|
|
|
|
auto metadata = maybeMetadata.extract();
|
|
|
|
if (aQuotaManager.IsOriginInternal(metadata.mOrigin)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mOrigins.AppendElement(std::move(metadata.mOrigin));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ListOriginsOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = ListOriginsResponse();
|
|
if (mOrigins.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
nsTArray<nsCString>& origins = aResponse.get_ListOriginsResponse().origins();
|
|
mOrigins.SwapElements(origins);
|
|
}
|
|
|
|
void ListOriginsOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = nullptr;
|
|
}
|
|
|
|
} // namespace mozilla::dom::quota
|