Backed out 5 changesets (bug 1639545, bug 1639548, bug 1818214) for multiple mochitest/marionette dom/indexedDB/test/... related test failures. CLOSED TREE

Backed out changeset 26d9a018ab8c (bug 1639545)
Backed out changeset 6814e78b09e7 (bug 1639545)
Backed out changeset dcaa7f92339c (bug 1818214)
Backed out changeset 2da5597a6ab0 (bug 1639548)
Backed out changeset b3251a6ea2ed (bug 1639548)
This commit is contained in:
Marian-Vasile Laza 2023-02-28 20:54:08 +02:00
Родитель c39ea4a713
Коммит 7747a471ee
18 изменённых файлов: 495 добавлений и 210 удалений

Просмотреть файл

@ -3076,6 +3076,7 @@ class FactoryOp
const CommonFactoryRequestParams mCommonParams;
OriginMetadata mOriginMetadata;
nsCString mDatabaseId;
nsCString mStorageId;
nsString mDatabaseFilePath;
int64_t mDirectoryLockId;
State mState;
@ -3086,6 +3087,8 @@ class FactoryOp
FlippedOnce<false> mInPrivateBrowsing;
public:
const nsACString& StorageId() const { return mStorageId; }
const nsACString& Origin() const {
AssertIsOnOwningThread();
@ -3580,9 +3583,120 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase,
const PreprocessResponse& aResponse) final;
};
} // namespace
Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsACString& aStorageId,
const nsAString& aDatabaseName,
const nsACString& keyStoreId) {
const auto& keyId = GenerateKeyId(aStorageId, aDatabaseName);
{
MutexAutoLock l{mMutex};
namespace {
auto dbKeyStore = mPrivateBrowsingInfoHashTable.Lookup(keyId);
if (!dbKeyStore) {
return Nothing();
}
return dbKeyStore->MaybeGet(keyStoreId);
}
}
CipherKey IndexedDBCipherKeyManager::Ensure(const nsACString& aStorageId,
const nsAString& aDatabaseName,
const nsACString& keyStoreId) {
const auto& keyId = GenerateKeyId(aStorageId, aDatabaseName);
{
MutexAutoLock l{mMutex};
auto& dbKeyStore = mPrivateBrowsingInfoHashTable.LookupOrInsert(keyId);
return dbKeyStore.LookupOrInsertWith(keyStoreId, [&] {
// Generate a new key if one corresponding to keyStoreID
// does not exists already.
auto keyOrErr = IndexedDBCipherStrategy::GenerateKey();
// Bug1800110 Propagate the error to the caller rather than asserting.
MOZ_RELEASE_ASSERT(keyOrErr.isOk());
// Add keyId against this aStorageId such that we could pull all the
// keyIds against a storageId later on.
auto& keyIds = mStorageIdAndKeyIdHashMap.LookupOrInsert(
aStorageId, nsTHashSet<nsCString>{});
keyIds.EnsureInserted(keyId);
return keyOrErr.unwrap();
});
}
}
bool IndexedDBCipherKeyManager::RemoveKey(const nsACString& aStorageId,
const nsAString& aDatabaseName) {
const auto& keyId = GenerateKeyId(aStorageId, aDatabaseName);
{
MutexAutoLock l{mMutex};
auto keyIds = mStorageIdAndKeyIdHashMap.Lookup(aStorageId);
if (keyIds) {
// In case, we are deleting a database;
// we need to remove it's keyId from it's storageId.
keyIds->Remove(keyId);
}
if (!mPrivateBrowsingInfoHashTable.Remove(keyId)) {
return false;
}
// we should remove this aStorageId from secondary cache if this was the
// only keyId in it.
if (keyIds->Count() == 0) {
mStorageIdAndKeyIdHashMap.Remove(aStorageId);
}
return true;
}
}
bool IndexedDBCipherKeyManager::RemoveAllKeysWithStorageId(
const nsACString& aStorageId) {
AssertIsOnIOThread();
{
MutexAutoLock l{mMutex};
const auto& keyIds = mStorageIdAndKeyIdHashMap.Lookup(aStorageId);
if (!keyIds) {
return false;
}
for (const auto& keyId : keyIds.Data()) {
mPrivateBrowsingInfoHashTable.Remove(keyId);
}
// origin is gone; we should remove it from secondary cache along with it's
// keyIds.
mStorageIdAndKeyIdHashMap.Remove(aStorageId);
}
return true;
}
uint32_t IndexedDBCipherKeyManager::Count() {
MutexAutoLock l{mMutex};
return mPrivateBrowsingInfoHashTable.Count();
}
nsCString IndexedDBCipherKeyManager::GenerateKeyId(
const nsACString& aStorageId, const nsAString& aDatabaseName) {
nsCString keyId;
keyId.Append(aStorageId);
keyId.Append("*");
keyId.Append(NS_ConvertUTF16toUTF8(aDatabaseName));
return keyId;
}
// XXX Maybe we can avoid a mutex here by moving all accesses to the background
// thread.
StaticAutoPtr<IndexedDBCipherKeyManager> gIndexedDBCipherKeyManager;
class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
friend class TransactionBase;
@ -3731,17 +3845,18 @@ void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
const auto& fileInfo = GetFileInfo();
const auto& fileManager = fileInfo.Manager();
const auto& fileMgr = fileInfo.Manager();
// No need to generate cipher keys if we are not in PBM
if (!fileManager.IsInPrivateBrowsingMode()) {
return;
}
// no need to generate cipher keys if we are not in PBM
if (!fileMgr.IsInPrivateBrowsingMode()) return;
nsCString keyId;
keyId.AppendInt(fileInfo.Id());
fileManager.MutableCipherKeyManagerRef().Ensure(keyId);
const auto& storageId = QuotaManager::StorageId::Serialize(
fileMgr.Origin(), fileMgr.Type(), Client::IDB);
gIndexedDBCipherKeyManager->Ensure(storageId, fileMgr.DatabaseName(), keyId);
}
ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
@ -5388,11 +5503,15 @@ RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase,
const nsCOMPtr<nsIFile>& aNativeFile,
const DatabaseFileInfo::IdType aId) {
if (aDatabase.IsInPrivateBrowsing()) {
nsCString keyId;
keyId.AppendInt(aId);
nsCString cipherKeyId;
cipherKeyId.AppendInt(aId);
const auto& key =
aDatabase.GetFileManager().MutableCipherKeyManagerRef().Get(keyId);
const auto& originMetadata = aDatabase.OriginMetadata();
const auto& storageID = QuotaManager::StorageId::Serialize(
originMetadata.mOrigin, originMetadata.mPersistenceType, Client::IDB);
const auto& key = gIndexedDBCipherKeyManager->Get(
storageID, aDatabase.GetFileManager().DatabaseName(), cipherKeyId);
MOZ_RELEASE_ASSERT(key.isSome());
return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key);
@ -6259,7 +6378,8 @@ struct ValuePopulateResponseHelper {
QM_TRY_UNWRAP(auto cloneInfo,
GetStructuredCloneReadInfoFromStatement(
aStmt, 2 + offset, 1 + offset, *aCursor.mFileManager));
aStmt, 2 + offset, 1 + offset, *aCursor.mFileManager,
aCursor.mDatabase->MaybeKeyRef()));
mCloneInfo.init(std::move(cloneInfo));
@ -11682,8 +11802,6 @@ nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
return Ok{};
}));
mInitialized.Flip();
return NS_OK;
}
@ -12386,6 +12504,17 @@ void QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
mgr->InvalidateFileManagers(aPersistenceType, aOrigin);
}
// Delete any cipher keys for this origin.
if (gIndexedDBCipherKeyManager != nullptr) {
const auto& storageId = QuotaManager::StorageId::Serialize(
aOrigin, aPersistenceType, GetType());
DebugOnly<bool> ret =
gIndexedDBCipherKeyManager->RemoveAllKeysWithStorageId(storageId);
MOZ_ASSERT(ret);
}
}
void QuotaClient::ReleaseIOThreadObjects() {
@ -14625,8 +14754,12 @@ nsresult FactoryOp::Open() {
const DatabaseMetadata& metadata = mCommonParams.metadata();
QuotaManager::GetStorageId(metadata.persistenceType(),
mOriginMetadata.mOrigin, Client::IDB, mDatabaseId);
// mDatabaseId is specifically more qualified than mStorageId.
// mStorageId identifies origin uniquely whereas mDatabaseId
// uniquely identifies databases uniquely within an origin.
mDatabaseId =
(mStorageId = QuotaManager::StorageId::Serialize(
mOriginMetadata.mOrigin, metadata.persistenceType(), Client::IDB));
mDatabaseId.Append('*');
mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
@ -14949,6 +15082,15 @@ nsresult FactoryOp::FinishOpen() {
QuotaManager* const quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
if (mInPrivateBrowsing) {
if (gIndexedDBCipherKeyManager == nullptr) {
gIndexedDBCipherKeyManager = new IndexedDBCipherKeyManager();
}
gIndexedDBCipherKeyManager->Ensure(mStorageId,
mCommonParams.metadata().name());
}
// Need to get database file path before opening the directory.
// XXX: For what reason?
QM_TRY_UNWRAP(mDatabaseFilePath,
@ -15230,22 +15372,10 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
CloneFileAndAppend(*dbDirectory, databaseFilenameBase +
kFileManagerDirectoryNameSuffix));
IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
MOZ_ASSERT(idm);
SafeRefPtr<DatabaseFileManager> fileManager = idm->GetFileManager(
persistenceType, mOriginMetadata.mOrigin, databaseName);
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
persistenceType, mOriginMetadata, databaseName, mDatabaseId,
mEnforcingQuota, mInPrivateBrowsing);
}
Maybe<const CipherKey> maybeKey =
mInPrivateBrowsing
? Some(fileManager->MutableCipherKeyManagerRef().Ensure())
: Nothing();
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(
mStorageId, mCommonParams.metadata().name())
: Nothing();
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
@ -15278,13 +15408,28 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
QM_TRY(OkIf(mMetadata->mCommonMetadata.version() <= mRequestedVersion),
NS_ERROR_DOM_INDEXEDDB_VERSION_ERR);
if (!fileManager->Initialized()) {
QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection)));
QM_TRY_UNWRAP(
mFileManager,
([this, persistenceType, &databaseName, &fmDirectory, &connection]()
-> mozilla::Result<SafeRefPtr<DatabaseFileManager>, nsresult> {
IndexedDatabaseManager* const mgr = IndexedDatabaseManager::Get();
MOZ_ASSERT(mgr);
idm->AddFileManager(fileManager.clonePtr());
}
SafeRefPtr<DatabaseFileManager> fileManager = mgr->GetFileManager(
persistenceType, mOriginMetadata.mOrigin, databaseName);
mFileManager = std::move(fileManager);
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
persistenceType, mOriginMetadata, databaseName, mDatabaseId,
mEnforcingQuota, mInPrivateBrowsing);
QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection)));
mgr->AddFileManager(fileManager.clonePtr());
}
return fileManager;
}()));
// Must close connection before dispatching otherwise we might race with the
// connection thread which needs to open the same database.
@ -15937,7 +16082,8 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
}
Maybe<const CipherKey> maybeKey =
mInPrivateBrowsing ? mFileManager->MutableCipherKeyManagerRef().Get()
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(
mStorageId, mCommonParams.metadata().name())
: Nothing();
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
@ -16244,26 +16390,10 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) {
return;
}
IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
MOZ_ASSERT(idm);
const PersistenceType persistenceType =
mCommonParams.metadata().persistenceType();
const nsAString& databaseName = mCommonParams.metadata().name();
SafeRefPtr<DatabaseFileManager> fileManager = idm->GetFileManager(
persistenceType, mOriginMetadata.mOrigin, databaseName);
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
persistenceType, mOriginMetadata, databaseName, mDatabaseId,
mEnforcingQuota, mInPrivateBrowsing);
}
const auto maybeKey =
mInPrivateBrowsing
? Some(fileManager->MutableCipherKeyManagerRef().Ensure())
: Nothing();
const auto maybeKey = mInPrivateBrowsing
? gIndexedDBCipherKeyManager->Get(
mStorageId, mCommonParams.metadata().name())
: Nothing();
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
@ -16511,6 +16641,8 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
const PersistenceType& persistenceType =
mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
const auto& databaseName = mDeleteDatabaseOp->mCommonParams.metadata().name();
QuotaManager* quotaManager =
mDeleteDatabaseOp->mEnforcingQuota ? QuotaManager::Get() : nullptr;
@ -16531,6 +16663,12 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
return rv;
}
if (mDeleteDatabaseOp->mInPrivateBrowsing) {
DebugOnly<bool> ok = gIndexedDBCipherKeyManager->RemoveKey(
mDeleteDatabaseOp->mStorageId, databaseName);
MOZ_ASSERT(ok);
}
rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -17676,7 +17814,8 @@ CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall(
QM_TRY_UNWRAP(auto cloneInfo, GetStructuredCloneReadInfoFromValueArray(
aValues,
/* aDataIndex */ 3,
/* aFileIdsIndex */ 2, *mOp->mFileManager));
/* aFileIdsIndex */ 2, *mOp->mFileManager,
mDatabase->MaybeKeyRef()));
const IndexMetadata& metadata = mOp->mMetadata;
const IndexOrObjectStoreId& objectStoreId = mOp->mObjectStoreId;
@ -18666,7 +18805,6 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
}
const DatabaseFileInfo& fileInfo = storedFileInfo.GetFileInfo();
const DatabaseFileManager& fileManager = fileInfo.Manager();
const auto file = fileHelper->GetFile(fileInfo);
QM_TRY(OkIf(file), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
@ -18679,30 +18817,40 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
nsCString fileKeyId;
fileKeyId.AppendInt(fileInfo.Id());
const auto& storageId = QuotaManager::StorageId::Serialize(
mOriginMetadata.mOrigin, mOriginMetadata.mPersistenceType,
Client::IDB);
const auto maybeKey =
fileManager.IsInPrivateBrowsingMode()
? fileManager.MutableCipherKeyManagerRef().Get(fileKeyId)
Transaction()
.GetDatabase()
.GetFileManager()
.IsInPrivateBrowsingMode()
? gIndexedDBCipherKeyManager->Get(
storageId, fileInfo.Manager().DatabaseName(), fileKeyId)
: Nothing();
QM_TRY(MOZ_TO_RESULT(fileHelper->CreateFileFromStream(
*file, *journalFile, *inputStream,
storedFileInfo.ShouldCompress(), maybeKey))
.mapErr([](const nsresult rv) {
if (NS_ERROR_GET_MODULE(rv) !=
NS_ERROR_MODULE_DOM_INDEXEDDB) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
return rv;
}),
QM_PROPAGATE,
([&fileManager, &file = *file,
&journalFile = *journalFile](const auto) {
// Try to remove the file if the copy failed.
QM_TRY(MOZ_TO_RESULT(
fileManager.SyncDeleteFile(file, journalFile)),
QM_VOID);
}));
QM_TRY(
MOZ_TO_RESULT(fileHelper->CreateFileFromStream(
*file, *journalFile, *inputStream,
storedFileInfo.ShouldCompress(), maybeKey))
.mapErr([](const nsresult rv) {
if (NS_ERROR_GET_MODULE(rv) !=
NS_ERROR_MODULE_DOM_INDEXEDDB) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
return rv;
}),
QM_PROPAGATE,
([this, &file = *file, &journalFile = *journalFile](const auto) {
// Try to remove the file if the copy failed.
QM_TRY(MOZ_TO_RESULT(Transaction()
.GetDatabase()
.GetFileManager()
.SyncDeleteFile(file, journalFile)),
QM_VOID);
}));
storedFileInfo.NotifyWriteSucceeded();
}
@ -18912,7 +19060,8 @@ nsresult ObjectStoreGetRequestOp::DoDatabaseWork(
*stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
QM_TRY_UNWRAP(auto cloneInfo,
GetStructuredCloneReadInfoFromStatement(
&stmt, 1, 0, mDatabase->GetFileManager()));
&stmt, 1, 0, mDatabase->GetFileManager(),
mDatabase->MaybeKeyRef()));
if (cloneInfo.HasPreprocessInfo()) {
mPreprocessInfoCount++;
@ -19390,7 +19539,8 @@ nsresult IndexGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) {
*stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
QM_TRY_UNWRAP(auto cloneInfo,
GetStructuredCloneReadInfoFromStatement(
&stmt, 1, 0, mDatabase->GetFileManager()));
&stmt, 1, 0, mDatabase->GetFileManager(),
mDatabase->MaybeKeyRef()));
if (cloneInfo.HasPreprocessInfo()) {
IDB_WARNING("Preprocessing for indexes not yet implemented!");

Просмотреть файл

@ -342,7 +342,8 @@ Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
uint32_t aBlobDataLength,
const DatabaseFileManager& aFileManager,
const nsAString& aFileIds) {
const nsAString& aFileIds,
const Maybe<CipherKey>& aMaybeKey) {
MOZ_ASSERT(!IsOnBackgroundThread());
AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromBlob", DOM);
@ -385,7 +386,7 @@ GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromExternalBlob(
uint64_t aIntData, const DatabaseFileManager& aFileManager,
const nsAString& aFileIds) {
const nsAString& aFileIds, const Maybe<CipherKey>& aMaybeKey) {
MOZ_ASSERT(!IsOnBackgroundThread());
AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromExternalBlob", DOM);
@ -413,15 +414,6 @@ GetStructuredCloneReadInfoFromExternalBlob(
const StructuredCloneFileParent& file = files[index];
MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eStructuredClone);
Maybe<CipherKey> maybeKey;
if (aFileManager.IsInPrivateBrowsingMode()) {
nsCString fileKeyId;
fileKeyId.AppendInt(file.FileInfo().Id());
maybeKey = aFileManager.MutableCipherKeyManagerRef().Get(fileKeyId);
}
auto data = JSStructuredCloneData{JS::StructuredCloneScope::DifferentProcess};
{
@ -431,13 +423,13 @@ GetStructuredCloneReadInfoFromExternalBlob(
QM_TRY_INSPECT(
const auto& fileInputStream,
NS_NewLocalFileInputStream(nativeFile)
.andThen([maybeKey](auto fileInputStream)
.andThen([aMaybeKey](auto fileInputStream)
-> Result<nsCOMPtr<nsIInputStream>, nsresult> {
if (maybeKey) {
if (aMaybeKey) {
return nsCOMPtr<nsIInputStream>{MakeRefPtr<
quota::DecryptingInputStream<IndexedDBCipherStrategy>>(
WrapNotNull(std::move(fileInputStream)),
kEncryptedStreamBlockSize, *maybeKey)};
kEncryptedStreamBlockSize, *aMaybeKey)};
}
return fileInputStream;
@ -455,7 +447,8 @@ template <typename T>
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromSource(T* aSource, uint32_t aDataIndex,
uint32_t aFileIdsIndex,
const DatabaseFileManager& aFileManager) {
const DatabaseFileManager& aFileManager,
const Maybe<CipherKey>& aMaybeKey) {
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(aSource);
@ -483,7 +476,7 @@ GetStructuredCloneReadInfoFromSource(T* aSource, uint32_t aDataIndex,
memcpy(&uintData, &intData, sizeof(uint64_t));
return GetStructuredCloneReadInfoFromExternalBlob(uintData, aFileManager,
fileIds);
fileIds, aMaybeKey);
}
case mozIStorageStatement::VALUE_TYPE_BLOB: {
@ -492,8 +485,8 @@ GetStructuredCloneReadInfoFromSource(T* aSource, uint32_t aDataIndex,
QM_TRY(MOZ_TO_RESULT(
aSource->GetSharedBlob(aDataIndex, &blobDataLength, &blobData)));
return GetStructuredCloneReadInfoFromBlob(blobData, blobDataLength,
aFileManager, fileIds);
return GetStructuredCloneReadInfoFromBlob(
blobData, blobDataLength, aFileManager, fileIds, aMaybeKey);
}
default:
@ -690,17 +683,20 @@ ReadCompressedNumber(const Span<const uint8_t> aSpan) {
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromValueArray(
mozIStorageValueArray* aValues, uint32_t aDataIndex, uint32_t aFileIdsIndex,
const DatabaseFileManager& aFileManager) {
return GetStructuredCloneReadInfoFromSource(aValues, aDataIndex,
aFileIdsIndex, aFileManager);
const DatabaseFileManager& aFileManager,
const Maybe<CipherKey>& aMaybeKey) {
return GetStructuredCloneReadInfoFromSource(
aValues, aDataIndex, aFileIdsIndex, aFileManager, aMaybeKey);
}
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromStatement(
mozIStorageStatement* aStatement, uint32_t aDataIndex,
uint32_t aFileIdsIndex, const DatabaseFileManager& aFileManager) {
return GetStructuredCloneReadInfoFromSource(aStatement, aDataIndex,
aFileIdsIndex, aFileManager);
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
uint32_t aDataIndex,
uint32_t aFileIdsIndex,
const DatabaseFileManager& aFileManager,
const Maybe<CipherKey>& aMaybeKey) {
return GetStructuredCloneReadInfoFromSource(
aStatement, aDataIndex, aFileIdsIndex, aFileManager, aMaybeKey);
}
Result<nsTArray<StructuredCloneFileParent>, nsresult>

Просмотреть файл

@ -19,6 +19,8 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/dom/indexedDB/Key.h"
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
#include "IndexedDBCipherKeyManager.h"
#include "nscore.h"
#include "nsISupports.h"
#include "nsStringFwd.h"
@ -100,12 +102,14 @@ ReadCompressedNumber(Span<const uint8_t> aSpan);
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromValueArray(
mozIStorageValueArray* aValues, uint32_t aDataIndex, uint32_t aFileIdsIndex,
const DatabaseFileManager& aFileManager);
const DatabaseFileManager& aFileManager, const Maybe<CipherKey>& aMaybeKey);
Result<StructuredCloneReadInfoParent, nsresult>
GetStructuredCloneReadInfoFromStatement(
mozIStorageStatement* aStatement, uint32_t aDataIndex,
uint32_t aFileIdsIndex, const DatabaseFileManager& aFileManager);
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
uint32_t aDataIndex,
uint32_t aFileIdsIndex,
const DatabaseFileManager& aFileManager,
const Maybe<CipherKey>& aMaybeKey);
Result<nsTArray<StructuredCloneFileParent>, nsresult>
DeserializeStructuredCloneFiles(const DatabaseFileManager& aFileManager,

Просмотреть файл

@ -8,8 +8,6 @@
#define DOM_INDEXEDDB_DATABASEFILEMANAGER_H_
#include "FileInfoManager.h"
#include "IndexedDBCipherKeyManager.h"
#include "mozilla/dom/FlippedOnce.h"
#include "mozilla/dom/quota/CommonMetadata.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/quota/UsageInfo.h"
@ -32,16 +30,12 @@ class DatabaseFileManager final
const nsString mDatabaseName;
const nsCString mDatabaseID;
mutable IndexedDBCipherKeyManager mCipherKeyManager;
LazyInitializedOnce<const nsString> mDirectoryPath;
LazyInitializedOnce<const nsString> mJournalDirectoryPath;
const bool mEnforcingQuota;
const bool mIsInPrivateBrowsingMode;
FlippedOnce<false> mInitialized;
// Lock protecting DatabaseFileManager.mFileInfos.
// It's s also used to atomically update DatabaseFileInfo.mRefCnt and
// DatabaseFileInfo.mDBRefCnt
@ -80,19 +74,10 @@ class DatabaseFileManager final
const nsACString& Origin() const { return mOriginMetadata.mOrigin; }
const nsAString& DatabaseName() const { return mDatabaseName; }
const nsCString& DatabaseID() const { return mDatabaseID; }
IndexedDBCipherKeyManager& MutableCipherKeyManagerRef() const {
return mCipherKeyManager;
}
auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; }
bool EnforcingQuota() const { return mEnforcingQuota; }
bool Initialized() const { return mInitialized; }
nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection);
[[nodiscard]] nsCOMPtr<nsIFile> GetDirectory();

Просмотреть файл

@ -1,35 +0,0 @@
/* -*- 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 "IndexedDBCipherKeyManager.h"
#include "mozilla/dom/quota/QuotaCommon.h"
namespace mozilla::dom::indexedDB {
Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsACString& aKeyId) {
auto lockedCipherKeys = mCipherKeys.Lock();
return lockedCipherKeys->MaybeGet(aKeyId);
}
CipherKey IndexedDBCipherKeyManager::Ensure(const nsACString& aKeyId) {
auto lockedCipherKeys = mCipherKeys.Lock();
return lockedCipherKeys->LookupOrInsertWith(aKeyId, [] {
// Generate a new key if one corresponding to keyStoreId does not exist
// already.
QM_TRY_RETURN(IndexedDBCipherStrategy::GenerateKey(), [](const auto&) {
// Bug1800110 Propagate the error to the caller rather than asserting.
MOZ_RELEASE_ASSERT(false);
return CipherKey{};
});
});
}
} // namespace mozilla::dom::indexedDB

Просмотреть файл

@ -4,15 +4,20 @@
* 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/. */
#ifndef DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
#define DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
#ifndef mozilla_dom_indexeddbcipherKeyManager_h
#define mozilla_dom_indexeddbcipherKeyManager_h
#include "mozilla/DataMutex.h"
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
#include "mozilla/DataMutex.h"
#include "nsHashKeys.h"
#include "nsTHashMap.h"
#include "nsTHashSet.h"
#include "mozilla/Mutex.h"
namespace mozilla::dom::indexedDB {
namespace {
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
using CipherKey = IndexedDBCipherStrategy::KeyType;
@ -20,25 +25,48 @@ class IndexedDBCipherKeyManager {
// This helper class is used by IndexedDB operations to store/retrieve cipher
// keys in private browsing mode. All data in IndexedDB must be encrypted
// using a cipher key and unique IV (Initialization Vector). While there's a
// separate cipher key for every blob file; the SQLite database gets encrypted
// using the commmon database key. All keys pertaining to a single IndexedDB
// database get stored together in a hashmap. So the hashmap can be used to
// to look up the common database key and blob keys using "default" and blob
// file ids respectively.
// separate cipher key for every blob file; any normal key/value pairs get
// encrypted using the commmon database key. All keys pertaining to a single
// database get stored together using the database id in a hashmap. We are
// using a 2-level hashmap here; keys get stored effectively at level 2.
// Looking up at level 1 using database id gives us access to hashmap of keys
// corresponding to that database which we could use to look up the common
// database key and blob keys using "default" and blob file ids respectively.
public:
IndexedDBCipherKeyManager() : mCipherKeys("IndexedDBCipherKeyManager"){};
using PrivateBrowsingInfoHashtable =
nsTHashMap<nsCStringHashKey, nsTHashMap<nsCStringHashKey, CipherKey>>;
Maybe<CipherKey> Get(const nsACString& aKeyId = "default"_ns);
IndexedDBCipherKeyManager() : mMutex("IndexedDBCipherKeyManager"){};
CipherKey Ensure(const nsACString& aKeyId = "default"_ns);
Maybe<CipherKey> Get(const nsACString& aStorageId,
const nsAString& aDatabaseName,
const nsACString& keyStoreId = "default"_ns);
CipherKey Ensure(const nsACString& aStorageId, const nsAString& aDatabaseName,
const nsACString& keyStoreId = "default"_ns);
bool RemoveKey(const nsACString& aStorageId, const nsAString& aDatabaseName);
bool RemoveAllKeysWithStorageId(const nsACString& aStorageId);
uint32_t Count();
private:
// XXX Maybe we can avoid a mutex here by moving all accesses to the
// background thread.
DataMutex<nsTHashMap<nsCStringHashKey, CipherKey>> mCipherKeys;
mozilla::Mutex mMutex;
PrivateBrowsingInfoHashtable mPrivateBrowsingInfoHashTable;
// mStorageIdAndDatabaseHashMap is a hashmap between a storageId and set of
// keyIds (corresponding to same origin represented by storageId). This is a
// secondary cache to track all the keyIds inside a origin such that it could
// be easier for operations like RemoveAllKeysWithStorageId to purge all keys
// corresponding to a origin much efficiently.
nsTHashMap<nsCStringHashKey, nsTHashSet<nsCString>> mStorageIdAndKeyIdHashMap;
nsCString GenerateKeyId(const nsACString& aStorageId,
const nsAString& aDatabaseName);
};
} // namespace
} // namespace mozilla::dom::indexedDB
#endif // DOM_INDEXEDDB_INDEXEDDBCIPHERKEYMANAGER_H_
#endif // IndexedDBCipherKeyManager_h

Просмотреть файл

@ -2897,8 +2897,9 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
return NS_ERROR_UNEXPECTED;
}
QM_TRY_UNWRAP(auto cloneInfo, GetStructuredCloneReadInfoFromValueArray(
aArguments, 1, 0, *mFileManager));
QM_TRY_UNWRAP(auto cloneInfo,
GetStructuredCloneReadInfoFromValueArray(
aArguments, 1, 0, *mFileManager, Nothing{}));
nsAutoString fileIds;
// XXX does this really need non-const cloneInfo?

Просмотреть файл

@ -9,8 +9,7 @@ with Files("**"):
MOCHITEST_MANIFESTS += [
"test/mochitest-intl-api.ini",
"test/mochitest-private.ini",
"test/mochitest-regular.ini",
"test/mochitest.ini",
]
BROWSER_CHROME_MANIFESTS += ["test/browser.ini"]
@ -69,7 +68,6 @@ UNIFIED_SOURCES += [
"IDBTransaction.cpp",
"IndexedDatabase.cpp",
"IndexedDatabaseManager.cpp",
"IndexedDBCipherKeyManager.cpp",
"IndexedDBCommon.cpp",
"KeyPath.cpp",
"ProfilerHelpers.cpp",

Просмотреть файл

@ -1,13 +0,0 @@
# 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/.
[DEFAULT]
prefs =
browser.privatebrowsing.autostart=true
dom.indexedDB.privateBrowsing.enabled=true
extensions.blocklist.enabled=false
dupe-manifest = true
[include:mochitest-common.ini]

Просмотреть файл

@ -1,14 +0,0 @@
# 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/.
[DEFAULT]
prefs =
extensions.blocklist.enabled=false
dupe-manifest = true
# Bug1819284: Run test_file_os_delete only for regular manifest.
[test_file_os_delete.html]
[include:mochitest-common.ini]

Просмотреть файл

@ -3,6 +3,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[DEFAULT]
prefs =
extensions.blocklist.enabled=false
support-files =
bfcache_page1.html
bfcache_page2.html
@ -160,6 +162,7 @@ skip-if = verify
[test_file_array.html]
[test_file_cross_database_copying.html]
[test_file_delete.html]
[test_file_os_delete.html]
[test_file_put_deleted.html]
[test_file_put_get_object.html]
[test_file_put_get_values.html]

Просмотреть файл

@ -271,6 +271,8 @@ static const uint32_t kUsageFileCookie = 0x420a420a;
*/
const uint32_t kFlushTimeoutMs = 5000;
const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
const bool kDefaultShadowWrites = false;
const uint32_t kDefaultSnapshotPrefill = 16384;
const uint32_t kDefaultSnapshotGradualPrefill = 4096;
@ -2602,6 +2604,7 @@ class ArchivedOriginScope {
};
class QuotaClient final : public mozilla::dom::quota::Client {
class Observer;
class MatchFunction;
static QuotaClient* sInstance;
@ -2611,6 +2614,8 @@ class QuotaClient final : public mozilla::dom::quota::Client {
public:
QuotaClient();
static nsresult Initialize();
static QuotaClient* GetInstance() {
AssertIsOnBackgroundThread();
@ -2676,6 +2681,23 @@ class QuotaClient final : public mozilla::dom::quota::Client {
ArchivedOriginScope* aArchivedOriginScope) const;
};
class QuotaClient::Observer final : public nsIObserver {
public:
static nsresult Initialize();
private:
Observer() { MOZ_ASSERT(NS_IsMainThread()); }
~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
nsresult Init();
nsresult Shutdown();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
class QuotaClient::MatchFunction final : public mozIStorageFunction {
OriginAttributesPattern mPattern;
@ -3175,6 +3197,8 @@ void InitializeLocalStorage() {
QM_WARNONLY_TRY(OkIf(ss));
}
QM_WARNONLY_TRY(QM_TO_RESULT(QuotaClient::Initialize()));
Preferences::RegisterCallbackAndCall(ShadowWritesPrefChangedCallback,
kShadowWritesPref);
@ -3494,6 +3518,22 @@ bool DeallocPBackgroundLSSimpleRequestParent(
return true;
}
bool RecvLSClearPrivateBrowsing() {
AssertIsOnBackgroundThread();
gPrivateDatastores = nullptr;
if (gDatastores) {
for (const auto& datastore : gDatastores->Values()) {
if (datastore->PrivateBrowsingId()) {
datastore->Clear(nullptr);
}
}
}
return true;
}
namespace localstorage {
already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
@ -8288,6 +8328,18 @@ QuotaClient::~QuotaClient() {
sInstance = nullptr;
}
// static
nsresult QuotaClient::Initialize() {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = Observer::Initialize();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
mozilla::dom::quota::Client::Type QuotaClient::GetType() {
return QuotaClient::LS;
}
@ -8890,6 +8942,87 @@ nsresult QuotaClient::PerformDelete(
return NS_OK;
}
// static
nsresult QuotaClient::Observer::Initialize() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Observer> observer = new Observer();
QM_TRY(MOZ_TO_RESULT(observer->Init()));
return NS_OK;
}
nsresult QuotaClient::Observer::Init() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return NS_ERROR_FAILURE;
}
nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = obs->AddObserver(this, kPrivateBrowsingObserverTopic, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
return rv;
}
return NS_OK;
}
nsresult QuotaClient::Observer::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return NS_ERROR_FAILURE;
}
MOZ_ALWAYS_SUCCEEDS(obs->RemoveObserver(this, kPrivateBrowsingObserverTopic));
MOZ_ALWAYS_SUCCEEDS(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
// In general, the instance will have died after the latter removal call, so
// it's not safe to do anything after that point.
// However, Shutdown is currently called from Observe which is called by the
// Observer Service which holds a strong reference to the observer while the
// Observe method is being called.
return NS_OK;
}
NS_IMPL_ISUPPORTS(QuotaClient::Observer, nsIObserver)
NS_IMETHODIMP
QuotaClient::Observer::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(aTopic, kPrivateBrowsingObserverTopic)) {
PBackgroundChild* const backgroundActor =
BackgroundChild::GetOrCreateForCurrentThread();
QM_TRY(OkIf(backgroundActor), NS_ERROR_FAILURE);
QM_TRY(OkIf(backgroundActor->SendLSClearPrivateBrowsing()),
NS_ERROR_FAILURE);
return NS_OK;
}
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
QM_TRY(MOZ_TO_RESULT(Shutdown()));
return NS_OK;
}
NS_WARNING("Unknown observer topic!");
return NS_OK;
}
NS_IMPL_ISUPPORTS(QuotaClient::MatchFunction, mozIStorageFunction)
NS_IMETHODIMP

Просмотреть файл

@ -75,6 +75,8 @@ bool RecvPBackgroundLSSimpleRequestConstructor(
bool DeallocPBackgroundLSSimpleRequestParent(
PBackgroundLSSimpleRequestParent* aActor);
bool RecvLSClearPrivateBrowsing();
namespace localstorage {
already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient();

Просмотреть файл

@ -2551,6 +2551,20 @@ bool RecvShutdownQuotaManager() {
return true;
}
nsCString QuotaManager::StorageId::Serialize(const nsACString& aOrigin,
PersistenceType aPType,
Client::Type aCType) {
nsAutoCString str;
str.AppendInt(aPType);
str.Append('*');
str.Append(aOrigin);
str.Append('*');
str.AppendInt(aCType);
return str;
}
QuotaManager::Observer* QuotaManager::Observer::sInstance = nullptr;
// static

Просмотреть файл

@ -88,6 +88,14 @@ class QuotaManager final : public BackgroundThreadObject {
class Observer;
public:
// StorageId uniquely identifies an origin by the combination
// of it's persistence type, name and quota client type.
class StorageId {
public:
static nsCString Serialize(const nsACString& origin, PersistenceType pType,
Client::Type cType);
};
QuotaManager(const nsAString& aBasePath, const nsAString& aStorageName);
NS_INLINE_DECL_REFCOUNTING(QuotaManager)

Просмотреть файл

@ -399,6 +399,20 @@ bool BackgroundParentImpl::DeallocPBackgroundLSSimpleRequestParent(
return mozilla::dom::DeallocPBackgroundLSSimpleRequestParent(aActor);
}
mozilla::ipc::IPCResult BackgroundParentImpl::RecvLSClearPrivateBrowsing() {
AssertIsInMainOrSocketProcess();
AssertIsOnBackgroundThread();
if (BackgroundParent::IsOtherProcessActor(this)) {
return IPC_FAIL_NO_REASON(this);
}
if (!mozilla::dom::RecvLSClearPrivateBrowsing()) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
BackgroundParentImpl::PBackgroundLocalStorageCacheParent*
BackgroundParentImpl::AllocPBackgroundLocalStorageCacheParent(
const PrincipalInfo& aPrincipalInfo, const nsACString& aOriginKey,

Просмотреть файл

@ -100,6 +100,8 @@ class BackgroundParentImpl : public PBackgroundParent {
bool DeallocPBackgroundLSSimpleRequestParent(
PBackgroundLSSimpleRequestParent* aActor) override;
mozilla::ipc::IPCResult RecvLSClearPrivateBrowsing() override;
PBackgroundLocalStorageCacheParent* AllocPBackgroundLocalStorageCacheParent(
const PrincipalInfo& aPrincipalInfo, const nsACString& aOriginKey,
const uint32_t& aPrivateBrowsingId) override;

Просмотреть файл

@ -171,6 +171,15 @@ parent:
*/
async PBackgroundLSSimpleRequest(LSSimpleRequestParams params);
/**
* Asynchronously propagates the "last-pb-context-exited" observer
* notification to LocalStorage NextGen implementation so it can clear
* retained private-browsing in-memory Datastores. Using a (same-process)
* IPC message avoids the need for the main-thread nsIObserver to have a
* reference to the PBackground thread and directly dispatch a runnable to it.
*/
async LSClearPrivateBrowsing();
async PBackgroundLocalStorageCache(PrincipalInfo principalInfo,
nsCString originKey,
uint32_t privateBrowsingId);