gecko-dev/dom/indexedDB/IndexedDatabaseManager.cpp
Jan Varga e12ce7d680 Bug 1561876 - Remove support for de-serializing WebAssembly.Modules in IDB; r=asuth
This patch removes support for de-serialization of WebAssembly.Modules. The
preprocessing which was added just for WebAssembly.Modules is not removed since
it can be reused for more efficient de-serialization of big structured clones
which are stored as standalone files. (standalone files can be read and
uncompressed in content process instead of the parent process). So this patch
also adjusts the preprocessing to support that. However the preprocessing is not
fully implemented (we lack support for indexes and cursors) and there's a
theoretical problem with ordering of IDB requests when preprocessing is
involved, so this feature is kept behind a pref for now.

Differential Revision: https://phabricator.services.mozilla.com/D36879

--HG--
rename : dom/indexedDB/test/unit/test_wasm_recompile.js => dom/indexedDB/test/unit/test_wasm_get_values.js
rename : dom/indexedDB/test/unit/wasm_recompile_profile.zip => dom/indexedDB/test/unit/wasm_get_values_profile.zip
2019-07-03 12:23:33 +02:00

917 lines
28 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 "IndexedDatabaseManager.h"
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
#include "nsIConsoleService.h"
#include "nsIDOMWindow.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "jsapi.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "mozilla/Logging.h"
#include "FileManager.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBKeyRange.h"
#include "IDBRequest.h"
#include "ProfilerHelpers.h"
#include "ScriptErrorHelper.h"
#include "nsCharSeparatedTokenizer.h"
#include "unicode/locid.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/IDBMutableFileBinding.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 {
namespace dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::ipc;
class FileManagerInfo {
public:
already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType,
const nsAString& aName) const;
void AddFileManager(FileManager* aFileManager);
bool HasFileManagers() const {
AssertIsOnIOThread();
return !mPersistentStorageFileManagers.IsEmpty() ||
!mTemporaryStorageFileManagers.IsEmpty() ||
!mDefaultStorageFileManagers.IsEmpty();
}
void InvalidateAllFileManagers() const;
void InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
void InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
const nsAString& aName);
private:
nsTArray<RefPtr<FileManager> >& GetArray(PersistenceType aPersistenceType);
const nsTArray<RefPtr<FileManager> >& GetImmutableArray(
PersistenceType aPersistenceType) const {
return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
}
nsTArray<RefPtr<FileManager> > mPersistentStorageFileManagers;
nsTArray<RefPtr<FileManager> > mTemporaryStorageFileManagers;
nsTArray<RefPtr<FileManager> > mDefaultStorageFileManagers;
};
} // namespace indexedDB
using namespace mozilla::dom::indexedDB;
namespace {
NS_DEFINE_IID(kIDBPrivateRequestIID, PRIVATE_IDBREQUEST_IID);
const uint32_t kDeleteTimeoutMs = 1000;
// 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 maximal size of a serialized object to be transfered through IPC.
const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize;
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
const char kPrefFileHandle[] = "dom.fileHandle.enabled";
const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold";
const char kPrefMaxSerilizedMsgSize[] =
IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize";
const char kPrefErrorEventToSelfError[] =
IDB_PREF_BRANCH_ROOT "errorEventToSelfError";
const char kPreprocessingPref[] = IDB_PREF_BRANCH_ROOT "preprocessing";
#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";
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
const char kPrefLoggingProfiler[] =
IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
#endif
#undef IDB_PREF_LOGGING_BRANCH_ROOT
#undef IDB_PREF_BRANCH_ROOT
StaticRefPtr<IndexedDatabaseManager> gDBManager;
Atomic<bool> gInitialized(false);
Atomic<bool> gClosed(false);
Atomic<bool> gTestingMode(false);
Atomic<bool> gExperimentalFeaturesEnabled(false);
Atomic<bool> gFileHandleEnabled(false);
Atomic<bool> gPrefErrorEventToSelfError(false);
Atomic<int32_t> gDataThresholdBytes(0);
Atomic<int32_t> gMaxSerializedMsgSize(0);
Atomic<bool> gPreprocessingEnabled(false);
void AtomicBoolPrefChangedCallback(const char* aPrefName,
Atomic<bool>* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aClosure);
*aClosure = Preferences::GetBool(aPrefName);
}
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 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);
}
} // namespace
IndexedDatabaseManager::IndexedDatabaseManager()
: mFileMutex("IndexedDatabaseManager.mFileMutex"),
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!");
if (IsClosed()) {
NS_ERROR("Calling GetOrCreate() after shutdown!");
return nullptr;
}
if (!gDBManager) {
sIsMainProcess = XRE_IsParentProcess();
RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
nsresult rv = instance->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
if (gInitialized.exchange(true)) {
NS_ERROR("Initialized more than once?!");
}
gDBManager = instance;
ClearOnShutdown(&gDBManager);
}
return gDBManager;
}
// static
IndexedDatabaseManager* IndexedDatabaseManager::Get() {
// Does not return an owning reference.
return gDBManager;
}
nsresult IndexedDatabaseManager::Init() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kTestingPref, &gTestingMode);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefFileHandle, &gFileHandleEnabled);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefErrorEventToSelfError,
&gPrefErrorEventToSelfError);
// 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);
#ifdef MOZ_GECKO_PROFILER
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
#endif
Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback,
kDataThresholdPref);
Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
kPrefMaxSerilizedMsgSize);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPreprocessingPref,
&gPreprocessingEnabled);
nsAutoCString acceptLang;
Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
// Split values on commas.
nsCCharSeparatedTokenizer langTokenizer(acceptLang, ',');
while (langTokenizer.hasMoreTokens()) {
nsAutoCString lang(langTokenizer.nextToken());
icu::Locale locale = icu::Locale::createCanonical(lang.get());
if (!locale.isBogus()) {
// icu::Locale::getBaseName is always ASCII as per BCP 47
mLocale.AssignASCII(locale.getBaseName());
break;
}
}
if (mLocale.IsEmpty()) {
mLocale.AssignLiteral("en_US");
}
return NS_OK;
}
void IndexedDatabaseManager::Destroy() {
// 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.exchange(true)) {
NS_ERROR("Shutdown more than once?!");
}
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, kTestingPref,
&gTestingMode);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefFileHandle, &gFileHandleEnabled);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefErrorEventToSelfError,
&gPrefErrorEventToSelfError);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
#ifdef MOZ_GECKO_PROFILER
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
#endif
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
Preferences::UnregisterCallback(DataThresholdPrefChangedCallback,
kDataThresholdPref);
Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback,
kPrefMaxSerilizedMsgSize);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPreprocessingPref, &gPreprocessingEnabled);
delete this;
}
// static
nsresult IndexedDatabaseManager::CommonPostHandleEvent(
EventChainPostVisitor& aVisitor, IDBFactory* aFactory) {
MOZ_ASSERT(aVisitor.mDOMEvent);
MOZ_ASSERT(aFactory);
if (!gPrefErrorEventToSelfError) {
return NS_OK;
}
if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
if (!aVisitor.mDOMEvent->IsTrusted()) {
return NS_OK;
}
nsAutoString type;
aVisitor.mDOMEvent->GetType(type);
MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
if (!type.EqualsLiteral("error")) {
return NS_OK;
}
nsCOMPtr<EventTarget> eventTarget = aVisitor.mDOMEvent->GetTarget();
MOZ_ASSERT(eventTarget);
// Only mess with events that were originally targeted to an IDBRequest.
RefPtr<IDBRequest> request;
if (NS_FAILED(eventTarget->QueryInterface(kIDBPrivateRequestIID,
getter_AddRefs(request))) ||
!request) {
return NS_OK;
}
RefPtr<DOMException> error = request->GetErrorAfterResult();
nsString errorName;
if (error) {
error->GetName(errorName);
}
RootedDictionary<ErrorEventInit> init(RootingCx());
request->GetCallerLocation(init.mFilename, &init.mLineno, &init.mColno);
init.mMessage = errorName;
init.mCancelable = true;
init.mBubbles = true;
nsEventStatus status = nsEventStatus_eIgnore;
if (NS_IsMainThread()) {
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(eventTarget->GetOwnerGlobal());
if (window) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
MOZ_ASSERT(sgo);
if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) {
status = nsEventStatus_eIgnore;
}
} else {
// We don't fire error events at any global for non-window JS on the main
// thread.
}
} else {
// Not on the main thread, must be in a worker.
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
MOZ_ASSERT(globalScope);
RefPtr<ErrorEvent> errorEvent = ErrorEvent::Constructor(
globalScope, nsDependentString(kErrorEventType), init);
MOZ_ASSERT(errorEvent);
errorEvent->SetTrusted(true);
auto* target = static_cast<EventTarget*>(globalScope.get());
if (NS_WARN_IF(NS_FAILED(EventDispatcher::DispatchDOMEvent(
target,
/* aWidgetEvent */ nullptr, errorEvent,
/* aPresContext */ nullptr, &status)))) {
status = nsEventStatus_eIgnore;
}
}
if (status == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
// Log the error to the error console.
ScriptErrorHelper::Dump(errorName, init.mFilename, init.mLineno, init.mColno,
nsIScriptError::errorFlag, aFactory->IsChrome(),
aFactory->InnerWindowID());
return NS_OK;
}
// static
bool IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(js::GetObjectClass(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::GetConstructorObject(aCx) ||
!IDBCursorWithValue_Binding::GetConstructorObject(aCx) ||
!IDBDatabase_Binding::GetConstructorObject(aCx) ||
!IDBFactory_Binding::GetConstructorObject(aCx) ||
!IDBIndex_Binding::GetConstructorObject(aCx) ||
!IDBKeyRange_Binding::GetConstructorObject(aCx) ||
!IDBLocaleAwareKeyRange_Binding::GetConstructorObject(aCx) ||
!IDBMutableFile_Binding::GetConstructorObject(aCx) ||
!IDBObjectStore_Binding::GetConstructorObject(aCx) ||
!IDBOpenDBRequest_Binding::GetConstructorObject(aCx) ||
!IDBRequest_Binding::GetConstructorObject(aCx) ||
!IDBTransaction_Binding::GetConstructorObject(aCx) ||
!IDBVersionChangeEvent_Binding::GetConstructorObject(aCx)) {
return false;
}
return true;
}
// static
bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
JS::Handle<JSObject*> aGlobal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
if (NS_WARN_IF(!global)) {
return false;
}
RefPtr<IDBFactory> factory;
if (NS_FAILED(
IDBFactory::CreateForMainThreadJS(global, getter_AddRefs(factory)))) {
return 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() { return gClosed; }
#ifdef DEBUG
// static
bool IndexedDatabaseManager::IsMainProcess() {
NS_ASSERTION(gDBManager,
"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(gDBManager,
"GetLoggingMode called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingMode;
}
// static
mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() {
MOZ_ASSERT(gDBManager,
"GetLoggingModule called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingModule;
}
#endif // DEBUG
// static
bool IndexedDatabaseManager::InTestingMode() {
MOZ_ASSERT(gDBManager,
"InTestingMode() called before indexedDB has been initialized!");
return gTestingMode;
}
// static
bool IndexedDatabaseManager::FullSynchronous() {
MOZ_ASSERT(gDBManager,
"FullSynchronous() called before indexedDB has been initialized!");
return sFullSynchronousMode;
}
// static
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled() {
if (NS_IsMainThread()) {
if (NS_WARN_IF(!GetOrCreate())) {
return false;
}
} else {
MOZ_ASSERT(Get(),
"ExperimentalFeaturesEnabled() called off the main thread "
"before indexedDB has been initialized!");
}
return gExperimentalFeaturesEnabled;
}
// static
bool IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
JSObject* aGlobal) {
// If, in the child process, properties of the global object are enumerated
// before the chrome registry (and thus the value of |intl.accept_languages|)
// is ready, calling IndexedDatabaseManager::Init will permanently break
// that preference. We can retrieve gExperimentalFeaturesEnabled without
// actually going through IndexedDatabaseManager.
// See Bug 1198093 comment 14 for detailed explanation.
MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) {
MOZ_ASSERT(NS_IsMainThread());
static bool featureRetrieved = false;
if (!featureRetrieved) {
gExperimentalFeaturesEnabled = Preferences::GetBool(kPrefExperimental);
featureRetrieved = true;
}
return gExperimentalFeaturesEnabled;
}
return ExperimentalFeaturesEnabled();
}
// static
bool IndexedDatabaseManager::IsFileHandleEnabled() {
MOZ_ASSERT(gDBManager,
"IsFileHandleEnabled() called before indexedDB has been "
"initialized!");
return gFileHandleEnabled;
}
// static
uint32_t IndexedDatabaseManager::DataThreshold() {
MOZ_ASSERT(gDBManager,
"DataThreshold() called before indexedDB has been initialized!");
return gDataThresholdBytes;
}
// static
uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() {
MOZ_ASSERT(
gDBManager,
"MaxSerializedMsgSize() called before indexedDB has been initialized!");
MOZ_ASSERT(gMaxSerializedMsgSize > 0);
return gMaxSerializedMsgSize;
}
// static
bool IndexedDatabaseManager::PreprocessingEnabled() {
MOZ_ASSERT(gDBManager,
"PreprocessingEnabled() called before indexedDB has been "
"initialized!");
return gPreprocessingEnabled;
}
void IndexedDatabaseManager::ClearBackgroundActor() {
MOZ_ASSERT(NS_IsMainThread());
mBackgroundActor = nullptr;
}
already_AddRefed<FileManager> IndexedDatabaseManager::GetFileManager(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName) {
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return nullptr;
}
RefPtr<FileManager> fileManager =
info->GetFileManager(aPersistenceType, aDatabaseName);
return fileManager.forget();
}
void IndexedDatabaseManager::AddFileManager(FileManager* aFileManager) {
AssertIsOnIOThread();
NS_ASSERTION(aFileManager, "Null file manager!");
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
info = new FileManagerInfo();
mFileManagerInfos.Put(aFileManager->Origin(), info);
}
info->AddFileManager(aFileManager);
}
void IndexedDatabaseManager::InvalidateAllFileManagers() {
AssertIsOnIOThread();
for (auto iter = mFileManagerInfos.ConstIter(); !iter.Done(); iter.Next()) {
auto value = iter.Data();
MOZ_ASSERT(value);
value->InvalidateAllFileManagers();
}
mFileManagerInfos.Clear();
}
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, int32_t* aSliceRefCnt, bool* aResult) {
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!InTestingMode())) {
return NS_ERROR_UNEXPECTED;
}
if (!mBackgroundActor) {
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE;
}
BackgroundUtilsChild* actor = new BackgroundUtilsChild(this);
// We don't set event target for BackgroundUtilsChild because:
// 1. BackgroundUtilsChild is a singleton.
// 2. SendGetFileReferences is a sync operation to be returned asap if
// unlabeled.
// 3. The rest operations like DeleteMe/__delete__ only happens at shutdown.
// Hence, we should keep it unlabeled.
mBackgroundActor = static_cast<BackgroundUtilsChild*>(
bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor));
}
if (NS_WARN_IF(!mBackgroundActor)) {
return NS_ERROR_FAILURE;
}
if (!mBackgroundActor->SendGetFileReferences(
aPersistenceType, nsCString(aOrigin), nsString(aDatabaseName),
aFileId, aRefCnt, aDBRefCnt, aSliceRefCnt, aResult)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult IndexedDatabaseManager::FlushPendingFileDeletions() {
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!InTestingMode())) {
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;
}
// static
void IndexedDatabaseManager::LoggingModePrefChangedCallback(
const char* /* aPrefName */, void* /* aClosure */) {
MOZ_ASSERT(NS_IsMainThread());
if (!Preferences::GetBool(kPrefLoggingEnabled)) {
sLoggingMode = Logging_Disabled;
return;
}
bool useProfiler =
#if defined(DEBUG) || defined(MOZ_GECKO_PROFILER)
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
#else
false;
#endif
const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
if (useProfiler) {
sLoggingMode = logDetails ? Logging_DetailedProfilerMarks
: Logging_ConciseProfilerMarks;
} else {
sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
}
}
// static
const nsCString& IndexedDatabaseManager::GetLocale() {
IndexedDatabaseManager* idbManager = Get();
MOZ_ASSERT(idbManager, "IDBManager is not ready!");
return idbManager->mLocale;
}
already_AddRefed<FileManager> FileManagerInfo::GetFileManager(
PersistenceType aPersistenceType, const nsAString& aName) const {
AssertIsOnIOThread();
const nsTArray<RefPtr<FileManager> >& managers =
GetImmutableArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
const RefPtr<FileManager>& fileManager = managers[i];
if (fileManager->DatabaseName() == aName) {
RefPtr<FileManager> result = fileManager;
return result.forget();
}
}
return nullptr;
}
void FileManagerInfo::AddFileManager(FileManager* aFileManager) {
AssertIsOnIOThread();
nsTArray<RefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
managers.AppendElement(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();
}
}
void FileManagerInfo::InvalidateAndRemoveFileManagers(
PersistenceType aPersistenceType) {
AssertIsOnIOThread();
nsTArray<RefPtr<FileManager> >& 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();
nsTArray<RefPtr<FileManager> >& managers = GetArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
RefPtr<FileManager>& fileManager = managers[i];
if (fileManager->DatabaseName() == aName) {
fileManager->Invalidate();
managers.RemoveElementAt(i);
return;
}
}
}
nsTArray<RefPtr<FileManager> >& 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_INVALID:
default:
MOZ_CRASH("Bad storage type value!");
}
}
} // namespace dom
} // namespace mozilla