1
0
Fork 0
firefox/dom/indexedDB/IndexedDatabaseManager.cpp
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

933 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- 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 "IndexedDatabaseManager.h"
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "jsapi.h"
#include "js/Object.h" // JS::GetClass
#include "js/PropertyAndElement.h" // JS_DefineProperty
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/quota/Assertions.h"
#include "mozilla/dom/quota/PromiseUtils.h"
#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/intl/LocaleCanonicalizer.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsContentUtils.h"
#include "mozilla/Logging.h"
#include "ActorsChild.h"
#include "DatabaseFileManager.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBKeyRange.h"
#include "IDBRequest.h"
#include "IndexedDBCommon.h"
#include "ProfilerHelpers.h"
#include "ScriptErrorHelper.h"
#include "nsCharSeparatedTokenizer.h"
// Bindings for ResolveConstructors
#include "mozilla/dom/IDBCursorBinding.h"
#include "mozilla/dom/IDBDatabaseBinding.h"
#include "mozilla/dom/IDBFactoryBinding.h"
#include "mozilla/dom/IDBIndexBinding.h"
#include "mozilla/dom/IDBKeyRangeBinding.h"
#include "mozilla/dom/IDBObjectStoreBinding.h"
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
#include "mozilla/dom/IDBRequestBinding.h"
#include "mozilla/dom/IDBTransactionBinding.h"
#include "mozilla/dom/IDBVersionChangeEventBinding.h"
#define IDB_STR "indexedDB"
namespace mozilla::dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::ipc;
class FileManagerInfo {
public:
[[nodiscard]] SafeRefPtr<DatabaseFileManager> GetFileManager(
PersistenceType aPersistenceType, const nsAString& aName) const;
[[nodiscard]] SafeRefPtr<DatabaseFileManager>
GetFileManagerByDatabaseFilePath(PersistenceType aPersistenceType,
const nsAString& aDatabaseFilePath) const;
const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetFileManagers(
PersistenceType aPersistenceType) const;
void AddFileManager(SafeRefPtr<DatabaseFileManager> aFileManager);
bool HasFileManagers() const {
AssertIsOnIOThread();
return !mPersistentStorageFileManagers.IsEmpty() ||
!mTemporaryStorageFileManagers.IsEmpty() ||
!mDefaultStorageFileManagers.IsEmpty() ||
!mPrivateStorageFileManagers.IsEmpty();
}
void InvalidateAllFileManagers() const;
void InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
void InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
const nsAString& aName);
private:
nsTArray<SafeRefPtr<DatabaseFileManager>>& GetArray(
PersistenceType aPersistenceType);
const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetImmutableArray(
PersistenceType aPersistenceType) const {
return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
}
nsTArray<SafeRefPtr<DatabaseFileManager>> mPersistentStorageFileManagers;
nsTArray<SafeRefPtr<DatabaseFileManager>> mTemporaryStorageFileManagers;
nsTArray<SafeRefPtr<DatabaseFileManager>> mDefaultStorageFileManagers;
nsTArray<SafeRefPtr<DatabaseFileManager>> mPrivateStorageFileManagers;
};
} // namespace indexedDB
using namespace mozilla::dom::indexedDB;
namespace {
// The threshold we use for structured clone data storing.
// Anything smaller than the threshold is compressed and stored in the database.
// Anything larger is compressed and stored outside the database.
const int32_t kDefaultDataThresholdBytes = 1024 * 1024; // 1MB
// The maximum size of a structured clone that can be transferred through IPC.
// Originally planned as 1024 MB, but adjusted to 1042 MB based on the
// following considerations:
// - The largest model at https://whisper.ggerganov.com is 1030 MB.
// - Chrome's limit varies between 10301050 MB depending on the platform.
// Keeping the limit at 1042 MB ensures better compatibility with both use
// cases.
//
// This limit might be increased after bug 1944231 and bug 1942995 are fixed.
const int32_t kDefaultMaxStructuredCloneSize = 1042 * 1024 * 1024; // 1042 MB
// The maximal size of a serialized object to be transfered through IPC.
const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize;
// The maximum number of records to preload (in addition to the one requested by
// the child).
//
// TODO: The current number was chosen for no particular reason. Telemetry
// should be added to determine whether this is a reasonable number for an
// overwhelming majority of cases.
const int32_t kDefaultMaxPreloadExtraRecords = 64;
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold";
const char kPrefMaxStructuredCloneSize[] =
IDB_PREF_BRANCH_ROOT "maxStructuredCloneSize";
const char kPrefMaxSerilizedMsgSize[] =
IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize";
const char kPrefMaxPreloadExtraRecords[] =
IDB_PREF_BRANCH_ROOT "maxPreloadExtraRecords";
#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
const char kPrefLoggingProfiler[] =
IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
#undef IDB_PREF_LOGGING_BRANCH_ROOT
#undef IDB_PREF_BRANCH_ROOT
StaticMutex gDBManagerMutex;
StaticRefPtr<IndexedDatabaseManager> gDBManager MOZ_GUARDED_BY(gDBManagerMutex);
bool gInitialized MOZ_GUARDED_BY(gDBManagerMutex) = false;
bool gClosed MOZ_GUARDED_BY(gDBManagerMutex) = false;
Atomic<int32_t> gDataThresholdBytes(0);
Atomic<int32_t> gMaxStructuredCloneSize(0);
Atomic<int32_t> gMaxSerializedMsgSize(0);
Atomic<int32_t> gMaxPreloadExtraRecords(0);
void DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
MOZ_ASSERT(!aClosure);
int32_t dataThresholdBytes =
Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes);
// The magic -1 is for use only by tests that depend on stable blob file id's.
if (dataThresholdBytes == -1) {
dataThresholdBytes = INT32_MAX;
}
gDataThresholdBytes = dataThresholdBytes;
}
void MaxStructuredCloneSizePrefChangeCallback(const char* aPrefName,
void* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxStructuredCloneSize));
MOZ_ASSERT(!aClosure);
gMaxStructuredCloneSize =
Preferences::GetInt(aPrefName, kDefaultMaxStructuredCloneSize);
MOZ_ASSERT(gMaxStructuredCloneSize > 0);
}
void MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName,
void* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize));
MOZ_ASSERT(!aClosure);
gMaxSerializedMsgSize =
Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize);
MOZ_ASSERT(gMaxSerializedMsgSize > 0);
}
void MaxPreloadExtraRecordsPrefChangeCallback(const char* aPrefName,
void* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxPreloadExtraRecords));
MOZ_ASSERT(!aClosure);
gMaxPreloadExtraRecords =
Preferences::GetInt(aPrefName, kDefaultMaxPreloadExtraRecords);
MOZ_ASSERT(gMaxPreloadExtraRecords >= 0);
// TODO: We could also allow setting a negative value to preload all available
// records, but this doesn't seem to be too useful in general, and it would
// require adaptations in ActorsParent.cpp
}
auto DatabaseNameMatchPredicate(const nsAString* const aName) {
MOZ_ASSERT(aName);
return [aName](const auto& fileManager) {
return fileManager->DatabaseName() == *aName;
};
}
auto DatabaseFilePathMatchPredicate(const nsAString* const aDatabaseFilePath) {
MOZ_ASSERT(aDatabaseFilePath);
return [aDatabaseFilePath](const auto& fileManager) {
return fileManager->DatabaseFilePath() == *aDatabaseFilePath;
};
}
} // namespace
IndexedDatabaseManager::IndexedDatabaseManager()
: mLocaleInitialized(false), mBackgroundActor(nullptr) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
IndexedDatabaseManager::~IndexedDatabaseManager() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mBackgroundActor) {
mBackgroundActor->SendDeleteMeInternal();
MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
}
}
bool IndexedDatabaseManager::sIsMainProcess = false;
bool IndexedDatabaseManager::sFullSynchronousMode = false;
mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB");
Atomic<IndexedDatabaseManager::LoggingMode>
IndexedDatabaseManager::sLoggingMode(
IndexedDatabaseManager::Logging_Disabled);
// static
IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
StaticMutexAutoLock lock(gDBManagerMutex);
if (gClosed) {
NS_ERROR("Calling GetOrCreate() after shutdown!");
return nullptr;
}
if (!gDBManager) {
sIsMainProcess = XRE_IsParentProcess();
if (gInitialized) {
NS_ERROR("Initialized more than once?!");
}
RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
{
StaticMutexAutoUnlock unlock(gDBManagerMutex);
QM_TRY(MOZ_TO_RESULT(instance->Init()), nullptr);
}
gDBManager = instance;
ClearOnShutdown(&gDBManager);
gInitialized = true;
}
return gDBManager;
}
// static
IndexedDatabaseManager* IndexedDatabaseManager::Get() {
StaticMutexAutoLock lock(gDBManagerMutex);
// Does not return an owning reference.
return gDBManager;
}
// static
already_AddRefed<IndexedDatabaseManager>
IndexedDatabaseManager::FactoryCreate() {
RefPtr<IndexedDatabaseManager> indexedDatabaseManager = GetOrCreate();
return indexedDatabaseManager.forget();
}
nsresult IndexedDatabaseManager::Init() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
// guarantees (unlike synchronous = OFF) atomicity and consistency, but not
// necessarily durability in situations such as power loss. This preference
// allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
// durability, but with an extra fsync() and the corresponding performance
// hit.
sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback,
kDataThresholdPref);
Preferences::RegisterCallbackAndCall(MaxStructuredCloneSizePrefChangeCallback,
kPrefMaxStructuredCloneSize);
Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
kPrefMaxSerilizedMsgSize);
Preferences::RegisterCallbackAndCall(MaxPreloadExtraRecordsPrefChangeCallback,
kPrefMaxPreloadExtraRecords);
return NS_OK;
}
void IndexedDatabaseManager::Destroy() {
{
StaticMutexAutoLock lock(gDBManagerMutex);
// Setting the closed flag prevents the service from being recreated.
// Don't set it though if there's no real instance created.
if (gInitialized && gClosed) {
NS_ERROR("Shutdown more than once?!");
}
gClosed = true;
}
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
Preferences::UnregisterCallback(DataThresholdPrefChangedCallback,
kDataThresholdPref);
Preferences::UnregisterCallback(MaxStructuredCloneSizePrefChangeCallback,
kPrefMaxStructuredCloneSize);
Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback,
kPrefMaxSerilizedMsgSize);
delete this;
}
nsresult IndexedDatabaseManager::EnsureBackgroundActor() {
if (mBackgroundActor) {
return NS_OK;
}
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE;
}
{
BackgroundUtilsChild* actor = new BackgroundUtilsChild(this);
mBackgroundActor = static_cast<BackgroundUtilsChild*>(
bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor));
if (NS_WARN_IF(!mBackgroundActor)) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
// static
bool IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(
JS::GetClass(JS::CurrentGlobalOrNull(aCx))->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
// We need to ensure that the manager has been created already here so that we
// load preferences that may control which properties are exposed.
if (NS_WARN_IF(!GetOrCreate())) {
return false;
}
if (!IDBCursor_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBCursorWithValue_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBDatabase_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBFactory_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBIndex_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBKeyRange_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBObjectStore_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBOpenDBRequest_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBRequest_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBTransaction_Binding::CreateAndDefineOnGlobal(aCx) ||
!IDBVersionChangeEvent_Binding::CreateAndDefineOnGlobal(aCx)) {
return false;
}
return true;
}
// static
bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
JS::Handle<JSObject*> aGlobal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(JS::GetClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
if (NS_WARN_IF(!global)) {
return false;
}
QM_TRY_UNWRAP(auto factory, IDBFactory::CreateForMainThreadJS(global), false);
MOZ_ASSERT(factory, "This should never fail for chrome!");
JS::Rooted<JS::Value> indexedDB(aCx);
js::AssertSameCompartment(aCx, aGlobal);
if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) {
return false;
}
return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
}
// static
bool IndexedDatabaseManager::IsClosed() {
StaticMutexAutoLock lock(gDBManagerMutex);
return gClosed;
}
#ifdef DEBUG
// static
bool IndexedDatabaseManager::IsMainProcess() {
NS_ASSERTION(Get(),
"IsMainProcess() called before indexedDB has been initialized!");
NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess,
"XRE_GetProcessType changed its tune!");
return sIsMainProcess;
}
// static
IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() {
MOZ_ASSERT(Get(),
"GetLoggingMode called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingMode;
}
// static
mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() {
MOZ_ASSERT(Get(),
"GetLoggingModule called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingModule;
}
#endif // DEBUG
// static
bool IndexedDatabaseManager::FullSynchronous() {
MOZ_ASSERT(Get(),
"FullSynchronous() called before indexedDB has been initialized!");
return sFullSynchronousMode;
}
// static
uint32_t IndexedDatabaseManager::DataThreshold() {
MOZ_ASSERT(Get(),
"DataThreshold() called before indexedDB has been initialized!");
return gDataThresholdBytes;
}
// static
uint32_t IndexedDatabaseManager::MaxStructuredCloneSize() {
MOZ_ASSERT(
Get(),
"MaxStructuredCloneSize() called before indexedDB has been initialized!");
MOZ_ASSERT(gMaxStructuredCloneSize > 0);
return gMaxStructuredCloneSize;
}
// static
uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() {
MOZ_ASSERT(
Get(),
"MaxSerializedMsgSize() called before indexedDB has been initialized!");
MOZ_ASSERT(gMaxSerializedMsgSize > 0);
return gMaxSerializedMsgSize;
}
// static
int32_t IndexedDatabaseManager::MaxPreloadExtraRecords() {
MOZ_ASSERT(Get(),
"MaxPreloadExtraRecords() called before indexedDB has been "
"initialized!");
return gMaxPreloadExtraRecords;
}
void IndexedDatabaseManager::ClearBackgroundActor() {
MOZ_ASSERT(NS_IsMainThread());
mBackgroundActor = nullptr;
}
SafeRefPtr<DatabaseFileManager> IndexedDatabaseManager::GetFileManager(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName) {
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return nullptr;
}
return info->GetFileManager(aPersistenceType, aDatabaseName);
}
SafeRefPtr<DatabaseFileManager>
IndexedDatabaseManager::GetFileManagerByDatabaseFilePath(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseFilePath) {
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return nullptr;
}
return info->GetFileManagerByDatabaseFilePath(aPersistenceType,
aDatabaseFilePath);
}
const nsTArray<SafeRefPtr<DatabaseFileManager>>&
IndexedDatabaseManager::GetFileManagers(PersistenceType aPersistenceType,
const nsACString& aOrigin) {
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
static nsTArray<SafeRefPtr<DatabaseFileManager>> emptyArray;
return emptyArray;
}
return info->GetFileManagers(aPersistenceType);
}
void IndexedDatabaseManager::AddFileManager(
SafeRefPtr<DatabaseFileManager> aFileManager) {
AssertIsOnIOThread();
MOZ_ASSERT(aFileManager);
const auto& origin = aFileManager->Origin();
mFileManagerInfos.GetOrInsertNew(origin)->AddFileManager(
std::move(aFileManager));
}
void IndexedDatabaseManager::InvalidateAllFileManagers() {
AssertIsOnIOThread();
for (const auto& fileManagerInfo : mFileManagerInfos.Values()) {
fileManagerInfo->InvalidateAllFileManagers();
}
mFileManagerInfos.Clear();
}
void IndexedDatabaseManager::InvalidateFileManagers(
PersistenceType aPersistenceType) {
AssertIsOnIOThread();
for (auto iter = mFileManagerInfos.Iter(); !iter.Done(); iter.Next()) {
iter.Data()->InvalidateAndRemoveFileManagers(aPersistenceType);
if (!iter.Data()->HasFileManagers()) {
iter.Remove();
}
}
}
void IndexedDatabaseManager::InvalidateFileManagers(
PersistenceType aPersistenceType, const nsACString& aOrigin) {
AssertIsOnIOThread();
MOZ_ASSERT(!aOrigin.IsEmpty());
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return;
}
info->InvalidateAndRemoveFileManagers(aPersistenceType);
if (!info->HasFileManagers()) {
mFileManagerInfos.Remove(aOrigin);
}
}
void IndexedDatabaseManager::InvalidateFileManager(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName) {
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return;
}
info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
if (!info->HasFileManagers()) {
mFileManagerInfos.Remove(aOrigin);
}
}
nsresult IndexedDatabaseManager::BlockAndGetFileReferences(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName, int64_t aFileId, int32_t* aRefCnt,
int32_t* aDBRefCnt, bool* aResult) {
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
return NS_ERROR_UNEXPECTED;
}
QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor()));
if (!mBackgroundActor->SendGetFileReferences(
aPersistenceType, nsCString(aOrigin), nsString(aDatabaseName),
aFileId, aRefCnt, aDBRefCnt, aResult)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult IndexedDatabaseManager::FlushPendingFileDeletions() {
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
return NS_ERROR_UNEXPECTED;
}
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE;
}
if (!bgActor->SendFlushPendingFileDeletions()) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMPL_ADDREF(IndexedDatabaseManager)
NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIIndexedDatabaseManager)
NS_IMETHODIMP
IndexedDatabaseManager::DoMaintenance(JSContext* aContext, Promise** _retval) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(_retval);
if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
return NS_ERROR_UNEXPECTED;
}
QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor()));
RefPtr<Promise> promise;
nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBackgroundActor->SendDoMaintenance()->Then(
GetCurrentSerialEventTarget(), __func__,
[promise](const PBackgroundIndexedDBUtilsChild::DoMaintenancePromise::
ResolveOrRejectValue& aValue) {
if (aValue.IsReject()) {
promise->MaybeReject(NS_ERROR_FAILURE);
return;
}
if (NS_FAILED(aValue.ResolveValue())) {
promise->MaybeReject(aValue.ResolveValue());
return;
}
promise->MaybeResolveWithUndefined();
});
promise.forget(_retval);
return NS_OK;
}
// static
void IndexedDatabaseManager::LoggingModePrefChangedCallback(
const char* /* aPrefName */, void* /* aClosure */) {
MOZ_ASSERT(NS_IsMainThread());
if (!Preferences::GetBool(kPrefLoggingEnabled)) {
sLoggingMode = Logging_Disabled;
return;
}
bool useProfiler = Preferences::GetBool(kPrefLoggingProfiler);
#if !defined(MOZ_GECKO_PROFILER)
if (useProfiler) {
NS_WARNING(
"IndexedDB cannot create profiler marks because this build does "
"not have profiler extensions enabled!");
useProfiler = false;
}
#endif
const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
if (useProfiler) {
sLoggingMode = logDetails ? Logging_DetailedProfilerMarks
: Logging_ConciseProfilerMarks;
} else {
sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
}
}
nsresult IndexedDatabaseManager::EnsureLocale() {
AssertIsOnMainThread();
if (mLocaleInitialized) {
return NS_OK;
}
nsAutoCString acceptLang;
Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
// Split values on commas.
for (const auto& lang :
nsCCharSeparatedTokenizer(acceptLang, ',').ToRange()) {
mozilla::intl::LocaleCanonicalizer::Vector asciiString{};
auto result = mozilla::intl::LocaleCanonicalizer::CanonicalizeICULevel1(
PromiseFlatCString(lang).get(), asciiString);
if (result.isOk()) {
mLocale.AssignASCII(asciiString);
break;
}
}
if (mLocale.IsEmpty()) {
mLocale.AssignLiteral("en_US");
}
mLocaleInitialized = true;
return NS_OK;
}
// static
const nsCString& IndexedDatabaseManager::GetLocale() {
IndexedDatabaseManager* idbManager = Get();
MOZ_ASSERT(idbManager, "IDBManager is not ready!");
MOZ_ASSERT(!idbManager->mLocale.IsEmpty());
return idbManager->mLocale;
}
SafeRefPtr<DatabaseFileManager> FileManagerInfo::GetFileManager(
PersistenceType aPersistenceType, const nsAString& aName) const {
AssertIsOnIOThread();
const auto& managers = GetImmutableArray(aPersistenceType);
const auto end = managers.cend();
const auto foundIt =
std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName));
return foundIt != end ? foundIt->clonePtr() : nullptr;
}
SafeRefPtr<DatabaseFileManager>
FileManagerInfo::GetFileManagerByDatabaseFilePath(
PersistenceType aPersistenceType,
const nsAString& aDatabaseFilePath) const {
AssertIsOnIOThread();
const auto& managers = GetImmutableArray(aPersistenceType);
const auto end = managers.cend();
const auto foundIt =
std::find_if(managers.cbegin(), end,
DatabaseFilePathMatchPredicate(&aDatabaseFilePath));
return foundIt != end ? foundIt->clonePtr() : nullptr;
}
const nsTArray<SafeRefPtr<DatabaseFileManager>>&
FileManagerInfo::GetFileManagers(PersistenceType aPersistenceType) const {
AssertIsOnIOThread();
return GetImmutableArray(aPersistenceType);
}
void FileManagerInfo::AddFileManager(
SafeRefPtr<DatabaseFileManager> aFileManager) {
AssertIsOnIOThread();
nsTArray<SafeRefPtr<DatabaseFileManager>>& managers =
GetArray(aFileManager->Type());
NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
managers.AppendElement(std::move(aFileManager));
}
void FileManagerInfo::InvalidateAllFileManagers() const {
AssertIsOnIOThread();
uint32_t i;
for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
mPersistentStorageFileManagers[i]->Invalidate();
}
for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
mTemporaryStorageFileManagers[i]->Invalidate();
}
for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
mDefaultStorageFileManagers[i]->Invalidate();
}
for (i = 0; i < mPrivateStorageFileManagers.Length(); i++) {
mPrivateStorageFileManagers[i]->Invalidate();
}
}
void FileManagerInfo::InvalidateAndRemoveFileManagers(
PersistenceType aPersistenceType) {
AssertIsOnIOThread();
nsTArray<SafeRefPtr<DatabaseFileManager>>& managers =
GetArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
managers[i]->Invalidate();
}
managers.Clear();
}
void FileManagerInfo::InvalidateAndRemoveFileManager(
PersistenceType aPersistenceType, const nsAString& aName) {
AssertIsOnIOThread();
auto& managers = GetArray(aPersistenceType);
const auto end = managers.cend();
const auto foundIt =
std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName));
if (foundIt != end) {
(*foundIt)->Invalidate();
managers.RemoveElementAt(foundIt.GetIndex());
}
}
nsTArray<SafeRefPtr<DatabaseFileManager>>& FileManagerInfo::GetArray(
PersistenceType aPersistenceType) {
switch (aPersistenceType) {
case PERSISTENCE_TYPE_PERSISTENT:
return mPersistentStorageFileManagers;
case PERSISTENCE_TYPE_TEMPORARY:
return mTemporaryStorageFileManagers;
case PERSISTENCE_TYPE_DEFAULT:
return mDefaultStorageFileManagers;
case PERSISTENCE_TYPE_PRIVATE:
return mPrivateStorageFileManagers;
case PERSISTENCE_TYPE_INVALID:
default:
MOZ_CRASH("Bad storage type value!");
}
}
} // namespace mozilla::dom