Bug 1784966: Encrypt IDB files and data in PBM, r=dom-storage-reviewers,jari,webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D158597
This commit is contained in:
Harveer Singh 2022-10-27 12:57:16 +00:00
parent a2219dd11c
commit 8802995594
11 changed files with 363 additions and 83 deletions

View File

@ -32,6 +32,7 @@
#include "IndexedDBCommon.h"
#include "IndexedDatabaseInlines.h"
#include "IndexedDatabaseManager.h"
#include "IndexedDBCipherKeyManager.h"
#include "KeyPath.h"
#include "MainThreadUtils.h"
#include "ProfilerHelpers.h"
@ -2384,7 +2385,6 @@ class Database final
bool IsInPrivateBrowsing() const {
AssertIsOnBackgroundThread();
return mInPrivateBrowsing;
}
@ -3750,6 +3750,47 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase,
const PreprocessResponse& aResponse) final;
};
Maybe<CipherKey> IndexedDBCipherKeyManager::Get(const nsCString& aDatabaseID,
const nsCString& keyStoreID) {
auto lockedPrivateBrowsingInfoHashTable =
mPrivateBrowsingInfoHashTable.Lock();
auto dbKeyStore = lockedPrivateBrowsingInfoHashTable->Lookup(aDatabaseID);
if (!dbKeyStore) {
return Nothing();
}
return dbKeyStore->MaybeGet(keyStoreID);
}
CipherKey IndexedDBCipherKeyManager::Ensure(const nsCString& aDatabaseID,
const nsCString& keyStoreID) {
auto lockedPrivateBrowsingInfoHashTable =
mPrivateBrowsingInfoHashTable.Lock();
auto& dbKeyStore =
lockedPrivateBrowsingInfoHashTable->LookupOrInsert(aDatabaseID);
return dbKeyStore.LookupOrInsertWith(keyStoreID, [] {
// XXX Generate key using proper random data, such that we can ensure
// the use of unique IVs per key by discriminating by database's file
// id & offset.
auto keyOrErr = IndexedDBCipherStrategy::GenerateKey();
// XXX Propagate the error to the caller rather than asserting.
return keyOrErr.unwrap();
});
}
bool IndexedDBCipherKeyManager::Remove(const nsCString& aDatabaseID) {
auto lockedPrivateBrowsingInfoHashTable =
mPrivateBrowsingInfoHashTable.Lock();
return lockedPrivateBrowsingInfoHashTable->Remove(aDatabaseID);
}
// 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;
@ -3765,7 +3806,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
#ifdef DEBUG
const StructuredCloneFileBase::FileType mType;
#endif
void EnsureCipherKey();
void AssertInvariants() const;
explicit StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo);
@ -3899,6 +3940,18 @@ void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
}
}
void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
const auto& fileInfo = GetFileInfo();
const auto& fileMgr = fileInfo.Manager();
// no need to generate cipher keys if we are not in PBM
if (!fileMgr.IsInPrivateBrowsingMode()) return;
nsCString keyId;
keyId.AppendInt(fileInfo.Id());
gIndexedDBCipherKeyManager->Ensure(fileMgr.DatabaseID(), keyId);
}
ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
SafeRefPtr<DatabaseFileInfo> aFileInfo)
: mFileInfo{WrapNotNull(std::move(aFileInfo))},
@ -3911,6 +3964,7 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
AssertIsOnBackgroundThread();
AssertInvariants();
EnsureCipherKey();
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
}
@ -3926,6 +3980,7 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
AssertIsOnBackgroundThread();
AssertInvariants();
EnsureCipherKey();
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
}
@ -3942,6 +3997,7 @@ ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
AssertIsOnBackgroundThread();
AssertInvariants();
EnsureCipherKey();
MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
}
@ -5555,19 +5611,26 @@ class EncryptedFileBlobImpl final : public FileBlobImpl {
}
private:
const CipherKey& mKey;
const CipherKey mKey;
};
RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase,
const nsCOMPtr<nsIFile>& aNativeFile,
const DatabaseFileInfo::IdType aId) {
const auto& maybeKey = aDatabase.MaybeKeyRef();
if (maybeKey) {
return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *maybeKey);
if (aDatabase.IsInPrivateBrowsing()) {
nsCString cipherKeyId;
cipherKeyId.AppendInt(aId);
const auto& key =
gIndexedDBCipherKeyManager->Get(aDatabase.Id(), cipherKeyId);
MOZ_RELEASE_ASSERT(key.isSome());
return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key);
}
auto impl = MakeRefPtr<FileBlobImpl>(aNativeFile);
impl->SetFileId(aId);
return impl;
}
@ -6012,12 +6075,6 @@ using DatabaseActorHashtable =
StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
using PrivateBrowsingInfoHashtable = nsTHashMap<nsCStringHashKey, CipherKey>;
// XXX Maybe we can avoid a mutex here by moving all accesses to the background
// thread.
StaticAutoPtr<DataMutex<PrivateBrowsingInfoHashtable>>
gPrivateBrowsingInfoHashtable;
StaticRefPtr<ConnectionPool> gConnectionPool;
StaticRefPtr<FileHandleThreadPool> gFileHandleThreadPool;
@ -6051,9 +6108,8 @@ void IncreaseBusyCount() {
MOZ_ASSERT(!gLiveDatabaseHashtable);
gLiveDatabaseHashtable = new DatabaseActorHashtable();
MOZ_ASSERT(!gPrivateBrowsingInfoHashtable);
gPrivateBrowsingInfoHashtable = new DataMutex<PrivateBrowsingInfoHashtable>(
"gPrivateBrowsingInfoHashtable");
MOZ_ASSERT(!gIndexedDBCipherKeyManager);
gIndexedDBCipherKeyManager = new IndexedDBCipherKeyManager();
MOZ_ASSERT(!gLoggingInfoHashtable);
gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
@ -6101,11 +6157,10 @@ void DecreaseBusyCount() {
MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
gLiveDatabaseHashtable = nullptr;
MOZ_ASSERT(gPrivateBrowsingInfoHashtable);
MOZ_ASSERT(gIndexedDBCipherKeyManager);
// XXX After we add the private browsing session end listener, we can assert
// this.
// MOZ_ASSERT(!gPrivateBrowsingInfoHashtable->Count());
gPrivateBrowsingInfoHashtable = nullptr;
gIndexedDBCipherKeyManager = nullptr;
MOZ_ASSERT(gFactoryOps);
MOZ_ASSERT(gFactoryOps->IsEmpty());
@ -12064,11 +12119,14 @@ DatabaseFileManager::MutexType DatabaseFileManager::sMutex;
DatabaseFileManager::DatabaseFileManager(
PersistenceType aPersistenceType,
const quota::OriginMetadata& aOriginMetadata,
const nsAString& aDatabaseName, bool aEnforcingQuota)
const nsAString& aDatabaseName, const nsCString& aDatabaseID,
bool aEnforcingQuota, bool aIsInPrivateBrowsingMode)
: mPersistenceType(aPersistenceType),
mOriginMetadata(aOriginMetadata),
mDatabaseName(aDatabaseName),
mEnforcingQuota(aEnforcingQuota) {}
mDatabaseID(aDatabaseID),
mEnforcingQuota(aEnforcingQuota),
mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
mozIStorageConnection& aConnection) {
@ -15219,22 +15277,7 @@ nsresult FactoryOp::Open() {
MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed);
if (mInPrivateBrowsing) {
const auto lockedPrivateBrowsingInfoHashtable =
gPrivateBrowsingInfoHashtable->Lock();
lockedPrivateBrowsingInfoHashtable->LookupOrInsertWith(mDatabaseId, [] {
IndexedDBCipherStrategy cipherStrategy;
// XXX Generate key using proper random data, such that we can ensure
// the use of unique IVs per key by discriminating by database's file
// id & offset.
auto keyOrErr = cipherStrategy.GenerateKey();
// XXX Propagate the error to the caller rather than asserting.
MOZ_RELEASE_ASSERT(keyOrErr.isOk());
return keyOrErr.unwrap();
});
gIndexedDBCipherKeyManager->Ensure(mDatabaseId);
}
mState = State::FinishOpen;
@ -15842,19 +15885,11 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
CloneFileAndAppend(*dbDirectory, databaseFilenameBase +
kFileManagerDirectoryNameSuffix));
Maybe<const CipherKey> maybeKey;
if (mInPrivateBrowsing) {
CipherKey key;
Maybe<const CipherKey> maybeKey =
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId)
: Nothing();
{
const auto lockedPrivateBrowsingInfoHashtable =
gPrivateBrowsingInfoHashtable->Lock();
MOZ_ALWAYS_TRUE(
lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key));
}
maybeKey.emplace(std::move(key));
}
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
QM_TRY_UNWRAP(
NotNull<nsCOMPtr<mozIStorageConnection>> connection,
@ -15897,7 +15932,8 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
persistenceType, mOriginMetadata, databaseName, mEnforcingQuota);
persistenceType, mOriginMetadata, databaseName, mDatabaseId,
mEnforcingQuota, mInPrivateBrowsing);
QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection)));
@ -16557,19 +16593,11 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
mMetadata = info->mMetadata.clonePtr();
}
Maybe<const CipherKey> maybeKey;
if (mInPrivateBrowsing) {
CipherKey key;
Maybe<const CipherKey> maybeKey =
mInPrivateBrowsing ? gIndexedDBCipherKeyManager->Get(mDatabaseId)
: Nothing();
{
const auto lockedPrivateBrowsingInfoHashtable =
gPrivateBrowsingInfoHashtable->Lock();
MOZ_ALWAYS_TRUE(
lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key));
}
maybeKey.emplace(std::move(key));
}
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
// XXX Shouldn't Manager() return already_AddRefed when
// PBackgroundIDBFactoryParent is declared refcounted?
@ -16872,16 +16900,11 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) {
return;
}
const auto maybeKey = [this]() -> Maybe<const CipherKey> {
CipherKey key;
const auto maybeKey = mInPrivateBrowsing
? gIndexedDBCipherKeyManager->Get(mDatabaseId)
: Nothing();
const auto lockedPrivateBrowsingInfoHashtable =
gPrivateBrowsingInfoHashtable->Lock();
if (!lockedPrivateBrowsingInfoHashtable->Get(mDatabaseId, &key)) {
return Nothing{};
}
return Some(std::move(key));
}();
MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
// Pass -1 as the directoryLockId to disable quota checking, since we might
// temporarily exceed quota before deleting the database.
@ -17152,6 +17175,11 @@ nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
return rv;
}
if (mDeleteDatabaseOp->mInPrivateBrowsing) {
MOZ_ASSERT(
gIndexedDBCipherKeyManager->Remove(mDeleteDatabaseOp->mDatabaseId));
}
rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -19527,11 +19555,22 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
QM_TRY(OkIf(journalFile), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
IDB_REPORT_INTERNAL_ERR_LAMBDA);
nsCString fileKeyId;
fileKeyId.AppendInt(fileInfo.Id());
const auto maybeKey =
Transaction()
.GetDatabase()
.GetFileManager()
.IsInPrivateBrowsingMode()
? gIndexedDBCipherKeyManager->Get(
Transaction().GetDatabase().Id(), fileKeyId)
: Nothing();
QM_TRY(
MOZ_TO_RESULT(fileHelper->CreateFileFromStream(
*file, *journalFile, *inputStream,
storedFileInfo.ShouldCompress(),
Transaction().GetDatabase().MaybeKeyRef()))
storedFileInfo.ShouldCompress(), maybeKey))
.mapErr([](const nsresult rv) {
if (NS_ERROR_GET_MODULE(rv) !=
NS_ERROR_MODULE_DOM_INDEXEDDB) {

View File

@ -20,6 +20,7 @@
#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"
@ -39,9 +40,6 @@ struct StructuredCloneReadInfoParent;
extern const nsLiteralString kJournalDirectoryName;
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
using CipherKey = IndexedDBCipherStrategy::KeyType;
// At the moment, the encrypted stream block size is assumed to be unchangeable
// between encrypting and decrypting blobs. This assumptions holds as long as we
// only encrypt in private browsing mode, but when we support encryption for

View File

@ -28,11 +28,13 @@ class DatabaseFileManager final
const PersistenceType mPersistenceType;
const quota::OriginMetadata mOriginMetadata;
const nsString mDatabaseName;
const nsCString mDatabaseID;
LazyInitializedOnce<const nsString> mDirectoryPath;
LazyInitializedOnce<const nsString> mJournalDirectoryPath;
const bool mEnforcingQuota;
const bool mIsInPrivateBrowsingMode;
// Lock protecting DatabaseFileManager.mFileInfos.
// It's s also used to atomically update DatabaseFileInfo.mRefCnt and
@ -59,7 +61,9 @@ class DatabaseFileManager final
DatabaseFileManager(PersistenceType aPersistenceType,
const quota::OriginMetadata& aOriginMetadata,
const nsAString& aDatabaseName, bool aEnforcingQuota);
const nsAString& aDatabaseName,
const nsCString& aDatabaseID, bool aEnforcingQuota,
bool aIsInPrivateBrowsingMode);
PersistenceType Type() const { return mPersistenceType; }
@ -70,7 +74,8 @@ class DatabaseFileManager final
const nsACString& Origin() const { return mOriginMetadata.mOrigin; }
const nsAString& DatabaseName() const { return mDatabaseName; }
const nsCString& DatabaseID() const { return mDatabaseID; }
auto IsInPrivateBrowsingMode() const { return mIsInPrivateBrowsingMode; }
bool EnforcingQuota() const { return mEnforcingQuota; }
nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection);

View File

@ -0,0 +1,52 @@
/* -*- 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/. */
#ifndef mozilla_dom_indexeddbcipherKeyManager_h
#define mozilla_dom_indexeddbcipherKeyManager_h
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
#include "mozilla/DataMutex.h"
#include "nsTHashMap.h"
namespace mozilla::dom::indexedDB {
namespace {
using IndexedDBCipherStrategy = quota::IPCStreamCipherStrategy;
using CipherKey = IndexedDBCipherStrategy::KeyType;
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; 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:
using PrivateBrowsingInfoHashtable =
nsTHashMap<nsCStringHashKey, nsTHashMap<nsCStringHashKey, CipherKey>>;
IndexedDBCipherKeyManager()
: mPrivateBrowsingInfoHashTable("IndexedDBCipherKeyManager"){};
Maybe<CipherKey> Get(const nsCString& aDatabaseID,
const nsCString& keyStoreID = "default"_ns);
CipherKey Ensure(const nsCString& aDatabaseID,
const nsCString& keyStoreID = "default"_ns);
bool Remove(const nsCString& aDatabaseID);
private:
DataMutex<PrivateBrowsingInfoHashtable> mPrivateBrowsingInfoHashTable;
};
} // namespace
} // namespace mozilla::dom::indexedDB
#endif // IndexedDBCipherKeyManager_h

View File

@ -2862,7 +2862,8 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
// purpose is to store file ids without adding more complexity or code
// duplication.
auto fileManager = MakeSafeRefPtr<DatabaseFileManager>(
PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, false);
PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, ""_ns, false,
false);
nsresult rv = fileManager->Init(aFMDirectory, aConnection);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -0,0 +1,3 @@
[DEFAULT]
[test_IDB_encryption_PBM.py]

View File

@ -0,0 +1,139 @@
# 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/.
import os
import re
from marionette_driver import Wait
from marionette_harness import MarionetteTestCase
INDEXED_DB_PBM_PREF = "dom.indexedDB.privateBrowsing.enabled"
class IDBEncryptionPBM(MarionetteTestCase):
"""
Bug1784966: Ensure IDB data gets encrypted in Private Browsing Mode.
We need to ensure data inside both sqlite fields and blob files under
*.sqllite gets encrypted.
"""
def setUp(self):
super(IDBEncryptionPBM, self).setUp()
self.IDBName = "IDBTest"
self.IDBStoreName = "IDBTestStore"
self.IDBVersion = 1
self.IDBValue = "test_IDB_Encryption_PBM"
self.profilePath = self.marionette.instance.profile.profile
self.defaultPrefValue = self.marionette.get_pref(INDEXED_DB_PBM_PREF)
self.marionette.set_pref(INDEXED_DB_PBM_PREF, True)
# Navigate by opening a new private window
pbmWindowHandle = self.marionette.open(type="window", private=True)["handle"]
self.marionette.switch_to_window(pbmWindowHandle)
self.marionette.navigate(
self.marionette.absolute_url("dom/indexedDB/basicIDB_PBM.html")
)
self.idbStoragePath = self.getIDBStoragePath()
def tearDown(self):
super(IDBEncryptionPBM, self).setUp()
self.marionette.set_pref(INDEXED_DB_PBM_PREF, self.defaultPrefValue)
# closes the new private window we opened in the setUp and referred by 'pbmWindowHandle'
self.marionette.close()
def test_ensure_encrypted_blob(self):
self.marionette.execute_script(
"""
const [idb, store, key, value] = arguments;
const blobValue = new Blob([value], {type:'text/plain'});
window.wrappedJSObject.addDataIntoIDB(idb, store, key, blobValue);
""",
script_args=(self.IDBName, self.IDBStoreName, "blobKey", self.IDBValue),
)
Wait(self.marionette, timeout=60).until(
lambda _: self.sqliteWALReleased(),
message="WAL still around even after 60s",
)
idbBlobDir = self.findDirObj(self.idbStoragePath, ".files", False)
if idbBlobDir is None:
raise Exception(f"unable to find '.files' dir in {self.idbStoragePath}")
# seems like there's a timing issue here. There are sometimes no blob file
# even after WAL is released. Allowing some buffer time and ensuring blob file
# exists before validating it's contents
idbBlobPath = os.path.join(idbBlobDir, "1")
Wait(self.marionette, timeout=10).until(
lambda _: os.path.exists(idbBlobPath),
message="Blob file does not exist even after waiting 10s",
)
foundRawValue = False
with open(idbBlobPath, "rb") as f_binary:
foundRawValue = (
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
)
self.assertFalse(foundRawValue, "Blob file did not get encrypted")
def test_ensure_encrpted_sqlite_data(self):
self.marionette.execute_script(
"""
const [idb, store, key, value] = arguments;
window.wrappedJSObject.addDataIntoIDB(idb, store, key, value);
""",
script_args=(self.IDBName, self.IDBStoreName, "textKey", self.IDBValue),
)
Wait(self.marionette, timeout=60).until(
lambda _: self.sqliteWALReleased(),
message="WAL still around even after 60s",
)
sqliteDBFile = self.findDirObj(self.idbStoragePath, ".sqlite", True)
self.assertIsNotNone(
sqliteDBFile, f"unable to find .sqlite file in {self.idbStoragePath}"
)
foundRawValue = False
with open(sqliteDBFile, "rb") as f_binary:
foundRawValue = (
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
)
self.assertFalse(foundRawValue, "sqlite data did not get encrypted")
def getIDBStoragePath(self):
origin = (
self.marionette.absolute_url("")[:-1].replace(":", "+").replace("/", "+")
)
# origin directory under storage is suffice'd with '^privateBrowsingId=1' for PBM
originDir = origin + "^privateBrowsingId=1"
return os.path.join(self.profilePath, "storage", "default", originDir, "idb")
def findDirObj(self, path, pattern, isFile):
for obj in os.scandir(path):
if obj.path.endswith(pattern) and (obj.is_file() == isFile):
return obj.path
return None
def sqliteWALReleased(self):
"""
checks if .sqlite-wal has been cleared or not.
returns False if idbStoragePath does not exist
"""
if not os.path.exists(self.idbStoragePath):
return False
return self.findDirObj(self.idbStoragePath, ".sqlite-wal", True) is None

View File

@ -650,7 +650,7 @@ UniquePtr<sqlite3_vfs> ConstructObfuscatingVFS(const char* aBaseVFSName) {
const sqlite3_vfs obfs_vfs = {
pOrig->iVersion, /* iVersion */
static_cast<int>(pOrig->szOsFile + sizeof(ObfsFile)), /* szOsFile */
1024, /* mxPathname */
pOrig->mxPathname, /* mxPathname */
nullptr, /* pNext */
GetObfuscatingVFSName(), /* zName */
pOrig, /* pAppData */

View File

@ -797,13 +797,15 @@ nsresult Connection::initialize(nsIFileURL* aFileURL,
nsAutoCString query;
rv = aFileURL->GetQuery(query);
NS_ENSURE_SUCCESS(rv, rv);
const char* const vfs =
URLParams::Parse(query,
[](const nsAString& aName, const nsAString& aValue) {
return aName.EqualsLiteral("key");
})
!URLParams::Parse(query,
[](const nsAString& aName, const nsAString& aValue) {
return !aName.EqualsLiteral("key");
})
? GetObfuscatingVFSName()
: GetTelemetryVFSName(exclusive);
int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, vfs);
if (srv != SQLITE_OK) {
mDBConn = nullptr;

View File

@ -3,6 +3,7 @@
# DOM tests
[include:../../../../../dom/cache/test/marionette/manifest.ini]
[include:../../../../../dom/indexedDB/test/marionette/manifest.ini]
[include:../../../../../dom/workers/test/marionette/manifest.ini]
# browser tests

View File

@ -0,0 +1,40 @@
<html>
<head>
<script>
async function ensureIDB(name, ver, store) {
return new Promise((resolve, reject) => {
let createObjectStore = (db, store) => {
db.createObjectStore(store);
};
var req = indexedDB.open(name, ver);
req.onerror = reject;
req.onsuccess = (event) => {
resolve(req.result);
};
req.onupgradeneeded = function (event) {
let db = event.target.result;
createObjectStore(db, store);
};
});
};
async function addDataIntoIDB(idb, store, key, value) {
let db = await ensureIDB(idb, 1, store);
await (new Promise((resolve, reject) => {
var transaction = db.transaction([store], "readwrite");
var put = transaction.objectStore(store).put(value, key);
put.onsuccess = resolve();
}));
closeIDB(db)
};
function closeIDB(db) {
db.close();
}
</script>
</head>
</html>