There are currently EnsurePersistentOriginIsInitializedInternal and EnsureTemporaryOriginIsInitializedInternal methods which can only be called on the QuotaManager IO thread. These methods shouldn't be exposed to quota clients and origin operations. There should be public methods callable from the PBackground thread returning a MozPromise instead. Such methods will guarantee that proper directory locking is acquired before persistent origin initialization or temporary origin initialization is started. Differential Revision: https://phabricator.services.mozilla.com/D194005
2711 lines
84 KiB
C++
2711 lines
84 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/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/DirectoryLockInlines.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;
|
|
|
|
template <class Base>
|
|
class OpenStorageDirectoryHelper : public Base {
|
|
protected:
|
|
OpenStorageDirectoryHelper(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName)
|
|
: Base(std::move(aQuotaManager), aName) {}
|
|
|
|
RefPtr<BoolPromise> OpenStorageDirectory(
|
|
const Nullable<PersistenceType>& aPersistenceType,
|
|
const OriginScope& aOriginScope,
|
|
const Nullable<Client::Type>& aClientType, bool aExclusive,
|
|
DirectoryLockCategory aCategory = DirectoryLockCategory::None);
|
|
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
};
|
|
|
|
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 OpenStorageDirectoryHelper<NormalOriginOperationBase> {
|
|
const OriginMetadata mOriginMetadata;
|
|
int64_t mTimestamp;
|
|
|
|
public:
|
|
SaveOriginAccessTimeOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginMetadata& aOriginMetadata,
|
|
int64_t aTimestamp)
|
|
: OpenStorageDirectoryHelper(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 OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> {
|
|
public:
|
|
explicit ClearPrivateRepositoryOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: OpenStorageDirectoryHelper(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 OpenStorageDirectoryHelper<QuotaUsageRequestBase>,
|
|
public TraverseRepositoryHelper {
|
|
nsTArray<OriginUsage> mOriginUsages;
|
|
nsTHashMap<nsCStringHashKey, uint32_t> mOriginUsagesIndex;
|
|
|
|
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 OpenStorageDirectoryHelper<QuotaUsageRequestBase> {
|
|
const OriginUsageParams mParams;
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
UsageInfo mUsageInfo;
|
|
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 ResolvableNormalOriginOp<bool> {
|
|
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;
|
|
|
|
bool GetResolveValue() 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;
|
|
|
|
bool GetResolveValue() 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 ResolvableNormalOriginOp<bool> {
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
|
|
public:
|
|
InitTemporaryStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock);
|
|
|
|
private:
|
|
~InitTemporaryStorageOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitializeOriginRequestBase : public ResolvableNormalOriginOp<bool> {
|
|
protected:
|
|
const PrincipalInfo mPrincipalInfo;
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
bool mCreated;
|
|
|
|
InitializeOriginRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock);
|
|
|
|
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 PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock);
|
|
|
|
private:
|
|
~InitializePersistentOriginOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
};
|
|
|
|
class InitializeTemporaryOriginOp final : public InitializeOriginRequestBase {
|
|
const PersistenceType mPersistenceType;
|
|
|
|
public:
|
|
InitializeTemporaryOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
PersistenceType aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock);
|
|
|
|
private:
|
|
~InitializeTemporaryOriginOp() = default;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
};
|
|
|
|
class InitializeClientBase : public ResolvableNormalOriginOp<bool> {
|
|
protected:
|
|
const PrincipalInfo mPrincipalInfo;
|
|
ClientMetadata mClientMetadata;
|
|
RefPtr<UniversalDirectoryLock> mDirectoryLock;
|
|
const PersistenceType mPersistenceType;
|
|
const Client::Type mClientType;
|
|
bool mCreated;
|
|
|
|
InitializeClientBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName, PersistenceType aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
Client::Type aClientType);
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
private:
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class InitializePersistentClientOp : public InitializeClientBase {
|
|
public:
|
|
InitializePersistentClientOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo, Client::Type aClientType);
|
|
|
|
private:
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
};
|
|
|
|
class InitializeTemporaryClientOp : public InitializeClientBase {
|
|
public:
|
|
InitializeTemporaryClientOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
PersistenceType aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
Client::Type aClientType);
|
|
|
|
private:
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
};
|
|
|
|
class GetFullOriginMetadataOp
|
|
: public OpenStorageDirectoryHelper<QuotaRequestBase> {
|
|
const GetFullOriginMetadataParams mParams;
|
|
// XXX Consider wrapping with LazyInitializedOnce
|
|
OriginMetadata mOriginMetadata;
|
|
Maybe<FullOriginMetadata> mMaybeFullOriginMetadata;
|
|
|
|
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 OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> {
|
|
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 OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> {
|
|
Atomic<uint64_t> mIterations;
|
|
|
|
protected:
|
|
ClearRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const char* aName)
|
|
: OpenStorageDirectoryHelper(std::move(aQuotaManager), aName),
|
|
mIterations(0) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
void DeleteFiles(QuotaManager& aQuotaManager,
|
|
const OriginMetadata& aOriginMetadata,
|
|
const Nullable<Client::Type>& aClientType);
|
|
|
|
void DeleteFiles(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType,
|
|
const OriginScope& aOriginScope,
|
|
const Nullable<Client::Type>& aClientType);
|
|
|
|
private:
|
|
template <typename FileCollector>
|
|
void DeleteFilesInternal(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType,
|
|
const OriginScope& aOriginScope,
|
|
const Nullable<Client::Type>& aClientType,
|
|
const FileCollector& aFileCollector);
|
|
|
|
void DoStringify(nsACString& aData) override {
|
|
aData.Append("ClearRequestBase "_ns +
|
|
//
|
|
kStringifyStartInstance +
|
|
//
|
|
"Iterations:"_ns +
|
|
IntToCString(static_cast<uint64_t>(mIterations)) +
|
|
//
|
|
kStringifyEndInstance);
|
|
}
|
|
};
|
|
|
|
class ClearOriginOp final : public ClearRequestBase {
|
|
const PrincipalInfo mPrincipalInfo;
|
|
PrincipalMetadata mPrincipalMetadata;
|
|
const Nullable<PersistenceType> mPersistenceType;
|
|
const Nullable<Client::Type> mClientType;
|
|
|
|
public:
|
|
ClearOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const mozilla::Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
const mozilla::Maybe<Client::Type>& aClientType);
|
|
|
|
private:
|
|
~ClearOriginOp() = default;
|
|
|
|
nsresult DoInit(QuotaManager& aQuotaManager) override;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearStoragesForOriginPrefixOp final
|
|
: public OpenStorageDirectoryHelper<ClearRequestBase> {
|
|
const nsCString mPrefix;
|
|
const Nullable<PersistenceType> mPersistenceType;
|
|
|
|
public:
|
|
ClearStoragesForOriginPrefixOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo);
|
|
|
|
private:
|
|
~ClearStoragesForOriginPrefixOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() override;
|
|
};
|
|
|
|
class ClearDataOp final : public ClearRequestBase {
|
|
const OriginAttributesPattern mPattern;
|
|
|
|
public:
|
|
ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginAttributesPattern& aPattern);
|
|
|
|
private:
|
|
~ClearDataOp() = default;
|
|
|
|
RefPtr<BoolPromise> OpenDirectory() override;
|
|
|
|
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
|
|
|
|
bool GetResolveValue() override;
|
|
|
|
void CloseDirectory() 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 OpenStorageDirectoryHelper<QuotaRequestBase> {
|
|
const PrincipalInfo mPrincipalInfo;
|
|
|
|
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 OpenStorageDirectoryHelper<QuotaRequestBase> {
|
|
const EstimateParams mParams;
|
|
OriginMetadata mOriginMetadata;
|
|
std::pair<uint64_t, uint64_t> mUsageAndLimit;
|
|
|
|
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 OpenStorageDirectoryHelper<QuotaRequestBase>,
|
|
public TraverseRepositoryHelper {
|
|
// XXX Bug 1521541 will make each origin has it's own state.
|
|
nsTArray<nsCString> mOrigins;
|
|
|
|
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<ResolvableNormalOriginOp<bool>> CreateStorageInitializedOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) {
|
|
return MakeRefPtr<StorageInitializedOp>(std::move(aQuotaManager));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> 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<ResolvableNormalOriginOp<bool>> CreateInitTemporaryStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock) {
|
|
return MakeRefPtr<InitTemporaryStorageOp>(std::move(aQuotaManager),
|
|
std::move(aDirectoryLock));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializePersistentOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock) {
|
|
return MakeRefPtr<InitializePersistentOriginOp>(
|
|
std::move(aQuotaManager), aPrincipalInfo, std::move(aDirectoryLock));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializeTemporaryOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock) {
|
|
return MakeRefPtr<InitializeTemporaryOriginOp>(
|
|
std::move(aQuotaManager), aPersistenceType, aPrincipalInfo,
|
|
std::move(aDirectoryLock));
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializePersistentClientOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
|
const Client::Type aClientType) {
|
|
return MakeRefPtr<InitializePersistentClientOp>(std::move(aQuotaManager),
|
|
aPrincipalInfo, aClientType);
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializeTemporaryClientOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo,
|
|
const Client::Type aClientType) {
|
|
return MakeRefPtr<InitializeTemporaryClientOp>(
|
|
std::move(aQuotaManager), aPersistenceType, aPrincipalInfo, aClientType);
|
|
}
|
|
|
|
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<ResolvableNormalOriginOp<bool>> CreateClearOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
const Maybe<Client::Type>& aClientType) {
|
|
return MakeRefPtr<ClearOriginOp>(std::move(aQuotaManager), aPersistenceType,
|
|
aPrincipalInfo, aClientType);
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateClearStoragesForOriginPrefixOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo) {
|
|
return MakeRefPtr<ClearStoragesForOriginPrefixOp>(
|
|
std::move(aQuotaManager), aPersistenceType, aPrincipalInfo);
|
|
}
|
|
|
|
RefPtr<ResolvableNormalOriginOp<bool>> CreateClearDataOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginAttributesPattern& aPattern) {
|
|
return MakeRefPtr<ClearDataOp>(std::move(aQuotaManager), aPattern);
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
template <class Base>
|
|
RefPtr<BoolPromise> OpenStorageDirectoryHelper<Base>::OpenStorageDirectory(
|
|
const Nullable<PersistenceType>& aPersistenceType,
|
|
const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType,
|
|
bool aExclusive, const DirectoryLockCategory aCategory) {
|
|
return Base::mQuotaManager
|
|
->OpenStorageDirectory(aPersistenceType, aOriginScope, aClientType,
|
|
aExclusive, aCategory)
|
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
|
[self = RefPtr(this)](
|
|
UniversalDirectoryLockPromise::ResolveOrRejectValue&& aValue) {
|
|
if (aValue.IsReject()) {
|
|
return BoolPromise::CreateAndReject(aValue.RejectValue(),
|
|
__func__);
|
|
}
|
|
|
|
self->mDirectoryLock = std::move(aValue.ResolveValue());
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
});
|
|
}
|
|
|
|
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
|
|
|
|
for (const auto& lock : mLocks) {
|
|
lock->Drop();
|
|
}
|
|
mLocks.Clear();
|
|
}
|
|
|
|
RefPtr<BoolPromise> SaveOriginAccessTimeOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("SaveOriginAccessTimeOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(!QuotaManager::IsShuttingDown()), NS_ERROR_ABORT);
|
|
|
|
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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearPrivateRepositoryOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(PERSISTENCE_TYPE_PRIVATE),
|
|
OriginScope::FromNull(), Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
}
|
|
|
|
nsresult ClearPrivateRepositoryOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("ClearPrivateRepositoryOp::DoDirectoryWork", OTHER);
|
|
|
|
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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
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, DirectoryLockCategory::UninitStorage);
|
|
|
|
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.MaybeRecordQuotaManagerShutdownStep(
|
|
"ShutdownStorageOp::DoDirectoryWork -> ShutdownStorageInternal."_ns);
|
|
|
|
aQuotaManager.ShutdownStorageInternal();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ShutdownStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
DropDirectoryLockIfNotDropped(mDirectoryLock);
|
|
}
|
|
|
|
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)
|
|
: OpenStorageDirectoryHelper(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();
|
|
|
|
return OpenStorageDirectory(Nullable<PersistenceType>(),
|
|
OriginScope::FromNull(), Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult GetUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER);
|
|
|
|
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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
GetOriginUsageOp::GetOriginUsageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const UsageRequestParams& aParams)
|
|
: OpenStorageDirectoryHelper(std::move(aQuotaManager),
|
|
"dom::quota::GetOriginUsageOp"),
|
|
mParams(aParams.get_OriginUsageParams()) {
|
|
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();
|
|
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(),
|
|
OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult GetOriginUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
MOZ_ASSERT(mUsageInfo.TotalUsage().isNothing());
|
|
|
|
AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER);
|
|
|
|
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.EnsureTemporaryStorageIsInitializedInternal()));
|
|
|
|
// 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 database usage.
|
|
mUsageInfo += DatabaseUsageType(
|
|
Some(aQuotaManager.GetOriginUsage(mPrincipalMetadata)));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
mUsageInfo += usageInfoOrErr.unwrap();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
OriginUsageResponse usageResponse;
|
|
|
|
usageResponse.usageInfo() = mUsageInfo;
|
|
|
|
aResponse = usageResponse;
|
|
}
|
|
|
|
void GetOriginUsageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
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)
|
|
: ResolvableNormalOriginOp(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;
|
|
}
|
|
|
|
bool StorageInitializedOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mInitialized;
|
|
}
|
|
|
|
nsresult TemporaryStorageInitializedOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("TemporaryStorageInitializedOp::DoDirectoryWork", OTHER);
|
|
|
|
mInitialized = aQuotaManager.IsTemporaryStorageInitializedInternal();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool TemporaryStorageInitializedOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mInitialized;
|
|
}
|
|
|
|
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();
|
|
|
|
DropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
InitTemporaryStorageOp::InitTemporaryStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager),
|
|
"dom::quota::InitTemporaryStorageOp"),
|
|
mDirectoryLock(std::move(aDirectoryLock)) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitTemporaryStorageOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
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.EnsureTemporaryStorageIsInitializedInternal()));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitTemporaryStorageOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void InitTemporaryStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
DropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
InitializeOriginRequestBase::InitializeOriginRequestBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager), aName),
|
|
mPrincipalInfo(aPrincipalInfo),
|
|
mDirectoryLock(std::move(aDirectoryLock)),
|
|
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();
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
|
|
return BoolPromise::CreateAndResolve(true, __func__);
|
|
}
|
|
|
|
void InitializeOriginRequestBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
DropDirectoryLockIfNotDropped(mDirectoryLock);
|
|
}
|
|
|
|
InitializePersistentOriginOp::InitializePersistentOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock)
|
|
: InitializeOriginRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::InitializePersistentOriginOp",
|
|
aPrincipalInfo, std::move(aDirectoryLock)) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
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
|
|
.EnsurePersistentOriginIsInitializedInternal(
|
|
OriginMetadata{mPrincipalMetadata, PERSISTENCE_TYPE_PERSISTENT})
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitializePersistentOriginOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mCreated;
|
|
}
|
|
|
|
InitializeTemporaryOriginOp::InitializeTemporaryOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo,
|
|
RefPtr<UniversalDirectoryLock> aDirectoryLock)
|
|
: InitializeOriginRequestBase(std::move(aQuotaManager),
|
|
"dom::quota::InitializeTemporaryOriginOp",
|
|
aPrincipalInfo, std::move(aDirectoryLock)),
|
|
mPersistenceType(aPersistenceType) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
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.IsTemporaryStorageInitializedInternal()),
|
|
NS_ERROR_NOT_INITIALIZED);
|
|
|
|
QM_TRY_UNWRAP(mCreated,
|
|
(aQuotaManager
|
|
.EnsureTemporaryOriginIsInitializedInternal(
|
|
OriginMetadata{mPrincipalMetadata, mPersistenceType})
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitializeTemporaryOriginOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mCreated;
|
|
}
|
|
|
|
InitializeClientBase::InitializeClientBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName,
|
|
const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo,
|
|
Client::Type aClientType)
|
|
: ResolvableNormalOriginOp(std::move(aQuotaManager), aName),
|
|
mPrincipalInfo(aPrincipalInfo),
|
|
mPersistenceType(aPersistenceType),
|
|
mClientType(aClientType),
|
|
mCreated(false) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult InitializeClientBase::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
PrincipalMetadata principalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo));
|
|
|
|
principalMetadata.AssertInvariants();
|
|
|
|
mClientMetadata = {
|
|
OriginMetadata{std::move(principalMetadata), mPersistenceType},
|
|
mClientType};
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> InitializeClientBase::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal(
|
|
Nullable(mPersistenceType),
|
|
OriginScope::FromOrigin(mClientMetadata.mOrigin),
|
|
Nullable(mClientMetadata.mClientType), /* aExclusive */ false);
|
|
|
|
return mDirectoryLock->Acquire();
|
|
}
|
|
|
|
void InitializeClientBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
DropDirectoryLockIfNotDropped(mDirectoryLock);
|
|
}
|
|
|
|
InitializePersistentClientOp::InitializePersistentClientOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo, Client::Type aClientType)
|
|
: InitializeClientBase(
|
|
std::move(aQuotaManager), "dom::quota::InitializePersistentClientOp",
|
|
PERSISTENCE_TYPE_PERSISTENT, aPrincipalInfo, aClientType) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult InitializePersistentClientOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitializePersistentClientOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsStorageInitializedInternal()),
|
|
NS_ERROR_FAILURE);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsPersistentOriginInitializedInternal(
|
|
mClientMetadata.mOrigin)),
|
|
NS_ERROR_FAILURE);
|
|
|
|
QM_TRY_UNWRAP(
|
|
mCreated,
|
|
(aQuotaManager.EnsurePersistentClientIsInitialized(mClientMetadata)
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitializePersistentClientOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mCreated;
|
|
}
|
|
|
|
InitializeTemporaryClientOp::InitializeTemporaryClientOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo,
|
|
Client::Type aClientType)
|
|
: InitializeClientBase(std::move(aQuotaManager),
|
|
"dom::quota::InitializeTemporaryClientOp",
|
|
aPersistenceType, aPrincipalInfo, aClientType) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult InitializeTemporaryClientOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("InitializeTemporaryClientOp::DoDirectoryWork", OTHER);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsStorageInitializedInternal()),
|
|
NS_ERROR_FAILURE);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsTemporaryStorageInitializedInternal()),
|
|
NS_ERROR_FAILURE);
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsTemporaryOriginInitializedInternal(
|
|
mClientMetadata)),
|
|
NS_ERROR_FAILURE);
|
|
|
|
QM_TRY_UNWRAP(
|
|
mCreated,
|
|
(aQuotaManager.EnsureTemporaryClientIsInitialized(mClientMetadata)
|
|
.map([](const auto& res) { return res.second; })));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool InitializeTemporaryClientOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mCreated;
|
|
}
|
|
|
|
GetFullOriginMetadataOp::GetFullOriginMetadataOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const GetFullOriginMetadataParams& aParams)
|
|
: OpenStorageDirectoryHelper(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();
|
|
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult GetFullOriginMetadataOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("GetFullOriginMetadataOp::DoDirectoryWork", OTHER);
|
|
|
|
// 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.EnsureTemporaryStorageIsInitializedInternal()));
|
|
|
|
// 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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
ClearStorageOp::ClearStorageOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: OpenStorageDirectoryHelper(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();
|
|
|
|
return OpenStorageDirectory(Nullable<PersistenceType>(),
|
|
OriginScope::FromNull(), Nullable<Client::Type>(),
|
|
/* aExclusive */ true,
|
|
DirectoryLockCategory::UninitStorage);
|
|
}
|
|
|
|
nsresult ClearStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("ClearStorageOp::DoDirectoryWork", OTHER);
|
|
|
|
DeleteFiles(aQuotaManager);
|
|
|
|
aQuotaManager.RemoveQuota();
|
|
|
|
aQuotaManager.ShutdownStorageInternal();
|
|
|
|
DeleteStorageFile(aQuotaManager);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool ClearStorageOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearStorageOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager,
|
|
const OriginMetadata& aOriginMetadata,
|
|
const Nullable<Client::Type>& aClientType) {
|
|
AssertIsOnIOThread();
|
|
|
|
DeleteFilesInternal(
|
|
aQuotaManager, aOriginMetadata.mPersistenceType,
|
|
OriginScope::FromOrigin(aOriginMetadata.mOrigin), aClientType,
|
|
[&aQuotaManager, &aOriginMetadata](
|
|
const std::function<Result<Ok, nsresult>(nsCOMPtr<nsIFile>&&)>& aBody)
|
|
-> Result<Ok, nsresult> {
|
|
QM_TRY_UNWRAP(auto directory,
|
|
aQuotaManager.GetOriginDirectory(aOriginMetadata));
|
|
|
|
QM_TRY_INSPECT(const bool& exists,
|
|
MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists));
|
|
|
|
if (!exists) {
|
|
return Ok{};
|
|
}
|
|
|
|
QM_TRY_RETURN(aBody(std::move(directory)));
|
|
});
|
|
}
|
|
|
|
void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager,
|
|
PersistenceType aPersistenceType,
|
|
const OriginScope& aOriginScope,
|
|
const Nullable<Client::Type>& aClientType) {
|
|
AssertIsOnIOThread();
|
|
|
|
DeleteFilesInternal(
|
|
aQuotaManager, aPersistenceType, aOriginScope, aClientType,
|
|
[&aQuotaManager, &aPersistenceType](
|
|
const std::function<Result<Ok, nsresult>(nsCOMPtr<nsIFile>&&)>& aBody)
|
|
-> Result<Ok, nsresult> {
|
|
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 Ok{};
|
|
}
|
|
|
|
QM_TRY_RETURN(CollectEachFile(*directory, aBody));
|
|
});
|
|
}
|
|
|
|
template <typename FileCollector>
|
|
void ClearRequestBase::DeleteFilesInternal(
|
|
QuotaManager& aQuotaManager, PersistenceType aPersistenceType,
|
|
const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType,
|
|
const FileCollector& aFileCollector) {
|
|
AssertIsOnIOThread();
|
|
|
|
QM_TRY(MOZ_TO_RESULT(aQuotaManager.AboutToClearOrigins(
|
|
Nullable<PersistenceType>(aPersistenceType), aOriginScope,
|
|
aClientType)),
|
|
QM_VOID);
|
|
|
|
nsTArray<nsCOMPtr<nsIFile>> directoriesForRemovalRetry;
|
|
|
|
aQuotaManager.MaybeRecordQuotaManagerShutdownStep(
|
|
"ClearRequestBase: Starting deleting files"_ns);
|
|
|
|
QM_TRY(
|
|
aFileCollector([&aClientType, &originScope = aOriginScope,
|
|
aPersistenceType, &aQuotaManager,
|
|
&directoriesForRemovalRetry,
|
|
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 (!aClientType.IsNull()) {
|
|
nsAutoString clientDirectoryName;
|
|
QM_TRY(OkIf(Client::TypeToText(aClientType.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(
|
|
aQuotaManager.RemoveOriginDirectory(*file), [&](const auto&) {
|
|
directoriesForRemovalRetry.AppendElement(std::move(file));
|
|
});
|
|
|
|
const bool initialized =
|
|
aPersistenceType == PERSISTENCE_TYPE_PERSISTENT
|
|
? aQuotaManager.IsPersistentOriginInitializedInternal(
|
|
metadata.mOrigin)
|
|
: aQuotaManager.IsTemporaryStorageInitializedInternal();
|
|
|
|
// 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 (aClientType.IsNull()) {
|
|
aQuotaManager.RemoveQuotaForOrigin(aPersistenceType, metadata);
|
|
} else {
|
|
aQuotaManager.ResetUsageForClient(
|
|
ClientMetadata{metadata, aClientType.Value()});
|
|
}
|
|
}
|
|
|
|
aQuotaManager.OriginClearCompleted(aPersistenceType,
|
|
metadata.mOrigin, aClientType);
|
|
|
|
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;
|
|
}
|
|
|
|
mIterations++;
|
|
|
|
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(
|
|
aQuotaManager.RemoveOriginDirectory(*file),
|
|
([&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);
|
|
}
|
|
|
|
ClearOriginOp::ClearOriginOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const mozilla::Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
const mozilla::Maybe<Client::Type>& aClientType)
|
|
: ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearOriginOp"),
|
|
mPrincipalInfo(aPrincipalInfo),
|
|
mPersistenceType(aPersistenceType
|
|
? Nullable<PersistenceType>(*aPersistenceType)
|
|
: Nullable<PersistenceType>()),
|
|
mClientType(aClientType ? Nullable<Client::Type>(*aClientType)
|
|
: Nullable<Client::Type>()) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
nsresult ClearOriginOp::DoInit(QuotaManager& aQuotaManager) {
|
|
AssertIsOnOwningThread();
|
|
|
|
QM_TRY_UNWRAP(
|
|
mPrincipalMetadata,
|
|
aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo));
|
|
|
|
mPrincipalMetadata.AssertInvariants();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearOriginOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(
|
|
mPersistenceType, OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
mClientType,
|
|
/* aExclusive */ true);
|
|
}
|
|
|
|
nsresult ClearOriginOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER);
|
|
|
|
if (mPersistenceType.IsNull()) {
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
DeleteFiles(aQuotaManager, OriginMetadata(mPrincipalMetadata, type),
|
|
mClientType);
|
|
}
|
|
} else {
|
|
DeleteFiles(aQuotaManager,
|
|
OriginMetadata(mPrincipalMetadata, mPersistenceType.Value()),
|
|
mClientType);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool ClearOriginOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearOriginOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
ClearStoragesForOriginPrefixOp::ClearStoragesForOriginPrefixOp(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const Maybe<PersistenceType>& aPersistenceType,
|
|
const PrincipalInfo& aPrincipalInfo)
|
|
: OpenStorageDirectoryHelper(std::move(aQuotaManager),
|
|
"dom::quota::ClearStoragesForOriginPrefixOp"),
|
|
mPrefix(
|
|
QuotaManager::GetOriginFromValidatedPrincipalInfo(aPrincipalInfo)),
|
|
mPersistenceType(aPersistenceType
|
|
? Nullable<PersistenceType>(*aPersistenceType)
|
|
: Nullable<PersistenceType>()) {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> ClearStoragesForOriginPrefixOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(mPersistenceType,
|
|
OriginScope::FromPrefix(mPrefix),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
}
|
|
|
|
nsresult ClearStoragesForOriginPrefixOp::DoDirectoryWork(
|
|
QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ClearStoragesForOriginPrefixOp::DoDirectoryWork", OTHER);
|
|
|
|
if (mPersistenceType.IsNull()) {
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
DeleteFiles(aQuotaManager, type, OriginScope::FromPrefix(mPrefix),
|
|
Nullable<Client::Type>());
|
|
}
|
|
} else {
|
|
DeleteFiles(aQuotaManager, mPersistenceType.Value(),
|
|
OriginScope::FromPrefix(mPrefix), Nullable<Client::Type>());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool ClearStoragesForOriginPrefixOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearStoragesForOriginPrefixOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
ClearDataOp::ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const OriginAttributesPattern& aPattern)
|
|
: ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearDataOp"),
|
|
mPattern(aPattern) {}
|
|
|
|
RefPtr<BoolPromise> ClearDataOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(Nullable<PersistenceType>(),
|
|
OriginScope::FromPattern(mPattern),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ true);
|
|
}
|
|
|
|
nsresult ClearDataOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
|
|
AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER);
|
|
|
|
for (const PersistenceType type : kAllPersistenceTypes) {
|
|
DeleteFiles(aQuotaManager, type, OriginScope::FromPattern(mPattern),
|
|
Nullable<Client::Type>());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool ClearDataOp::GetResolveValue() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearDataOp::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
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();
|
|
|
|
DropDirectoryLockIfNotDropped(mDirectoryLock);
|
|
}
|
|
|
|
PersistRequestBase::PersistRequestBase(
|
|
MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const PrincipalInfo& aPrincipalInfo)
|
|
: OpenStorageDirectoryHelper(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();
|
|
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
|
OriginScope::FromOrigin(mPrincipalMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
void PersistRequestBase::CloseDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
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();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("PersistedOp::DoDirectoryWork", OTHER);
|
|
|
|
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();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
const OriginMetadata originMetadata = {mPrincipalMetadata,
|
|
PERSISTENCE_TYPE_DEFAULT};
|
|
|
|
AUTO_PROFILER_LABEL("PersistOp::DoDirectoryWork", OTHER);
|
|
|
|
// 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.IsTemporaryStorageInitializedInternal()) {
|
|
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.IsTemporaryStorageInitializedInternal()) {
|
|
aQuotaManager.PersistOrigin(originMetadata);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void PersistOp::GetResponse(RequestResponse& aResponse) {
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = PersistResponse();
|
|
}
|
|
|
|
EstimateOp::EstimateOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
|
|
const EstimateParams& aParams)
|
|
: OpenStorageDirectoryHelper(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.
|
|
return OpenStorageDirectory(
|
|
Nullable<PersistenceType>(mOriginMetadata.mPersistenceType),
|
|
OriginScope::FromOrigin(mOriginMetadata.mOrigin),
|
|
Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult EstimateOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("EstimateOp::DoDirectoryWork", OTHER);
|
|
|
|
// 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.EnsureTemporaryStorageIsInitializedInternal()));
|
|
|
|
// 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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
ListOriginsOp::ListOriginsOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager)
|
|
: OpenStorageDirectoryHelper(std::move(aQuotaManager),
|
|
"dom::quota::ListOriginsOp") {
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
RefPtr<BoolPromise> ListOriginsOp::OpenDirectory() {
|
|
AssertIsOnOwningThread();
|
|
|
|
return OpenStorageDirectory(Nullable<PersistenceType>(),
|
|
OriginScope::FromNull(), Nullable<Client::Type>(),
|
|
/* aExclusive */ false);
|
|
}
|
|
|
|
nsresult ListOriginsOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
|
|
AssertIsOnIOThread();
|
|
aQuotaManager.AssertStorageIsInitializedInternal();
|
|
|
|
AUTO_PROFILER_LABEL("ListOriginsOp::DoDirectoryWork", OTHER);
|
|
|
|
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();
|
|
|
|
SafeDropDirectoryLock(mDirectoryLock);
|
|
}
|
|
|
|
} // namespace mozilla::dom::quota
|