michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "IDBFactory.h" michael@0: michael@0: #include "nsIFile.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIXPCScriptable.h" michael@0: michael@0: #include michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/IDBFactoryBinding.h" michael@0: #include "mozilla/dom/PBrowserChild.h" michael@0: #include "mozilla/dom/quota/OriginOrPatternString.h" michael@0: #include "mozilla/dom/quota/QuotaManager.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/storage.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsCharSeparatedTokenizer.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsXPCOMCID.h" michael@0: michael@0: #include "AsyncConnectionHelper.h" michael@0: #include "CheckPermissionsHelper.h" michael@0: #include "DatabaseInfo.h" michael@0: #include "IDBDatabase.h" michael@0: #include "IDBEvents.h" michael@0: #include "IDBKeyRange.h" michael@0: #include "IndexedDatabaseManager.h" michael@0: #include "Key.h" michael@0: #include "ProfilerHelpers.h" michael@0: #include "ReportInternalError.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "ipc/IndexedDBChild.h" michael@0: michael@0: #define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled" michael@0: michael@0: USING_INDEXEDDB_NAMESPACE michael@0: USING_QUOTA_NAMESPACE michael@0: michael@0: using mozilla::dom::ContentChild; michael@0: using mozilla::dom::ContentParent; michael@0: using mozilla::dom::IDBOpenDBOptions; michael@0: using mozilla::dom::NonNull; michael@0: using mozilla::dom::Optional; michael@0: using mozilla::dom::TabChild; michael@0: using mozilla::ErrorResult; michael@0: using mozilla::Preferences; michael@0: michael@0: namespace { michael@0: michael@0: struct ObjectStoreInfoMap michael@0: { michael@0: ObjectStoreInfoMap() michael@0: : id(INT64_MIN), info(nullptr) { } michael@0: michael@0: int64_t id; michael@0: ObjectStoreInfo* info; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: IDBFactory::IDBFactory() michael@0: : mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY), michael@0: mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr), michael@0: mContentParent(nullptr), mRootedOwningObject(false) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: IDBFactory::~IDBFactory() michael@0: { michael@0: NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); michael@0: if (mActorChild) { michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: mActorChild->Send__delete__(mActorChild); michael@0: NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); michael@0: } michael@0: if (mRootedOwningObject) { michael@0: mOwningObject = nullptr; michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::Create(nsPIDOMWindow* aWindow, michael@0: const nsACString& aGroup, michael@0: const nsACString& aASCIIOrigin, michael@0: ContentParent* aContentParent, michael@0: IDBFactory** aFactory) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(), michael@0: "Non-chrome may not supply their own origin!"); michael@0: michael@0: IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (aWindow->IsOuterWindow()) { michael@0: aWindow = aWindow->GetCurrentInnerWindow(); michael@0: IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: michael@0: // Make sure that the manager is up before we do anything here since lots of michael@0: // decisions depend on which process we're running in. michael@0: indexedDB::IndexedDatabaseManager* mgr = michael@0: indexedDB::IndexedDatabaseManager::GetOrCreate(); michael@0: IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCString group(aGroup); michael@0: nsCString origin(aASCIIOrigin); michael@0: StoragePrivilege privilege; michael@0: PersistenceType defaultPersistenceType; michael@0: if (origin.IsEmpty()) { michael@0: NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!"); michael@0: michael@0: rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege, michael@0: &defaultPersistenceType); michael@0: } michael@0: else { michael@0: rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege, michael@0: &defaultPersistenceType); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: // Not allowed. michael@0: *aFactory = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr factory = new IDBFactory(); michael@0: factory->mGroup = group; michael@0: factory->mASCIIOrigin = origin; michael@0: factory->mPrivilege = privilege; michael@0: factory->mDefaultPersistenceType = defaultPersistenceType; michael@0: factory->mWindow = aWindow; michael@0: factory->mContentParent = aContentParent; michael@0: michael@0: if (!IndexedDatabaseManager::IsMainProcess()) { michael@0: TabChild* tabChild = TabChild::GetFrom(aWindow); michael@0: IDB_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: IndexedDBChild* actor = new IndexedDBChild(origin); michael@0: michael@0: bool allowed; michael@0: tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed); michael@0: michael@0: if (!allowed) { michael@0: actor->Send__delete__(actor); michael@0: *aFactory = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: actor->SetFactory(factory); michael@0: } michael@0: michael@0: factory.forget(aFactory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::Create(JSContext* aCx, michael@0: JS::Handle aOwningObject, michael@0: ContentParent* aContentParent, michael@0: IDBFactory** aFactory) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aCx, "Null context!"); michael@0: NS_ASSERTION(aOwningObject, "Null object!"); michael@0: NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, michael@0: "Not a global object!"); michael@0: NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); michael@0: michael@0: // Make sure that the manager is up before we do anything here since lots of michael@0: // decisions depend on which process we're running in. michael@0: IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); michael@0: IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsCString group; michael@0: nsCString origin; michael@0: StoragePrivilege privilege; michael@0: PersistenceType defaultPersistenceType; michael@0: QuotaManager::GetInfoForChrome(&group, &origin, &privilege, michael@0: &defaultPersistenceType); michael@0: michael@0: nsRefPtr factory = new IDBFactory(); michael@0: factory->mGroup = group; michael@0: factory->mASCIIOrigin = origin; michael@0: factory->mPrivilege = privilege; michael@0: factory->mDefaultPersistenceType = defaultPersistenceType; michael@0: factory->mOwningObject = aOwningObject; michael@0: factory->mContentParent = aContentParent; michael@0: michael@0: mozilla::HoldJSObjects(factory.get()); michael@0: factory->mRootedOwningObject = true; michael@0: michael@0: if (!IndexedDatabaseManager::IsMainProcess()) { michael@0: ContentChild* contentChild = ContentChild::GetSingleton(); michael@0: IDB_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: IndexedDBChild* actor = new IndexedDBChild(origin); michael@0: michael@0: contentChild->SendPIndexedDBConstructor(actor); michael@0: michael@0: actor->SetFactory(factory); michael@0: } michael@0: michael@0: factory.forget(aFactory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::Create(ContentParent* aContentParent, michael@0: IDBFactory** aFactory) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); michael@0: NS_ASSERTION(aContentParent, "Null ContentParent!"); michael@0: michael@0: NS_ASSERTION(!nsContentUtils::GetCurrentJSContext(), "Should be called from C++"); michael@0: michael@0: // We need to get this information before we push a null principal to avoid michael@0: // IsCallerChrome() assertion in quota manager. michael@0: nsCString group; michael@0: nsCString origin; michael@0: StoragePrivilege privilege; michael@0: PersistenceType defaultPersistenceType; michael@0: QuotaManager::GetInfoForChrome(&group, &origin, &privilege, michael@0: &defaultPersistenceType); michael@0: michael@0: nsCOMPtr principal = michael@0: do_CreateInstance("@mozilla.org/nullprincipal;1"); michael@0: NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); michael@0: michael@0: AutoSafeJSContext cx; michael@0: michael@0: nsIXPConnect* xpc = nsContentUtils::XPConnect(); michael@0: NS_ASSERTION(xpc, "This should never be null!"); michael@0: michael@0: nsCOMPtr globalHolder; michael@0: nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS::Rooted global(cx, globalHolder->GetJSObject()); michael@0: NS_ENSURE_STATE(global); michael@0: michael@0: // The CreateSandbox call returns a proxy to the actual sandbox object. We michael@0: // don't need a proxy here. michael@0: global = js::UncheckedUnwrap(global); michael@0: michael@0: JSAutoCompartment ac(cx, global); michael@0: michael@0: nsRefPtr factory = new IDBFactory(); michael@0: factory->mGroup = group; michael@0: factory->mASCIIOrigin = origin; michael@0: factory->mPrivilege = privilege; michael@0: factory->mDefaultPersistenceType = defaultPersistenceType; michael@0: factory->mOwningObject = global; michael@0: factory->mContentParent = aContentParent; michael@0: michael@0: mozilla::HoldJSObjects(factory.get()); michael@0: factory->mRootedOwningObject = true; michael@0: michael@0: factory.forget(aFactory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, michael@0: PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin) michael@0: { michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: nsCOMPtr fileUrl = do_QueryInterface(uri); michael@0: NS_ASSERTION(fileUrl, "This should always succeed!"); michael@0: michael@0: nsAutoCString type; michael@0: PersistenceTypeToText(aPersistenceType, type); michael@0: michael@0: rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + michael@0: NS_LITERAL_CSTRING("&group=") + aGroup + michael@0: NS_LITERAL_CSTRING("&origin=") + aOrigin); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return fileUrl.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBFactory::GetConnection(const nsAString& aDatabaseFilePath, michael@0: PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aOrigin) michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")), michael@0: "Bad file path!"); michael@0: michael@0: nsCOMPtr dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); michael@0: NS_ENSURE_TRUE(dbFile, nullptr); michael@0: michael@0: nsresult rv = dbFile->InitWithPath(aDatabaseFilePath); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: bool exists; michael@0: rv = dbFile->Exists(&exists); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: NS_ENSURE_TRUE(exists, nullptr); michael@0: michael@0: nsCOMPtr dbFileUrl = michael@0: GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin); michael@0: NS_ENSURE_TRUE(dbFileUrl, nullptr); michael@0: michael@0: nsCOMPtr ss = michael@0: do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE(ss, nullptr); michael@0: michael@0: nsCOMPtr connection; michael@0: rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: rv = SetDefaultPragmas(connection); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return connection.forget(); michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::SetDefaultPragmas(mozIStorageConnection* aConnection) michael@0: { michael@0: NS_ASSERTION(aConnection, "Null connection!"); michael@0: michael@0: static const char query[] = michael@0: #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) michael@0: // Switch the journaling mode to TRUNCATE to avoid changing the directory michael@0: // structure at the conclusion of every transaction for devices with slower michael@0: // file systems. michael@0: "PRAGMA journal_mode = TRUNCATE; " michael@0: #endif michael@0: // We use foreign keys in lots of places. michael@0: "PRAGMA foreign_keys = ON; " michael@0: // The "INSERT OR REPLACE" statement doesn't fire the update trigger, michael@0: // instead it fires only the insert trigger. This confuses the update michael@0: // refcount function. This behavior changes with enabled recursive triggers, michael@0: // so the statement fires the delete trigger first and then the insert michael@0: // trigger. michael@0: "PRAGMA recursive_triggers = ON;"; michael@0: michael@0: nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: inline michael@0: bool michael@0: IgnoreWhitespace(char16_t c) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, michael@0: const nsACString& aDatabaseId, michael@0: uint64_t* aVersion, michael@0: ObjectStoreInfoArray& aObjectStores) michael@0: { michael@0: AssertIsOnIOThread(); michael@0: NS_ASSERTION(aConnection, "Null pointer!"); michael@0: michael@0: aObjectStores.Clear(); michael@0: michael@0: // Load object store names and ids. michael@0: nsCOMPtr stmt; michael@0: nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT name, id, key_path, auto_increment " michael@0: "FROM object_store" michael@0: ), getter_AddRefs(stmt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoTArray infoMap; michael@0: michael@0: bool hasResult; michael@0: while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { michael@0: nsRefPtr* element = michael@0: aObjectStores.AppendElement(new ObjectStoreInfo()); michael@0: michael@0: ObjectStoreInfo* info = element->get(); michael@0: michael@0: rv = stmt->GetString(0, info->name); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: info->id = stmt->AsInt64(1); michael@0: michael@0: int32_t columnType; michael@0: nsresult rv = stmt->GetTypeOfIndex(2, &columnType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // NB: We don't have to handle the NULL case, since that is the default michael@0: // for a new KeyPath. michael@0: if (columnType != mozIStorageStatement::VALUE_TYPE_NULL) { michael@0: NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT, michael@0: "Should be a string"); michael@0: nsString keyPathSerialization; michael@0: rv = stmt->GetString(2, keyPathSerialization); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: info->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); michael@0: } michael@0: michael@0: info->nextAutoIncrementId = stmt->AsInt64(3); michael@0: info->comittedAutoIncrementId = info->nextAutoIncrementId; michael@0: michael@0: info->autoIncrement = !!info->nextAutoIncrementId; michael@0: michael@0: ObjectStoreInfoMap* mapEntry = infoMap.AppendElement(); michael@0: NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: mapEntry->id = info->id; michael@0: mapEntry->info = info; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Load index information michael@0: rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT object_store_id, id, name, key_path, unique_index, multientry " michael@0: "FROM object_store_index" michael@0: ), getter_AddRefs(stmt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { michael@0: int64_t objectStoreId = stmt->AsInt64(0); michael@0: michael@0: ObjectStoreInfo* objectStoreInfo = nullptr; michael@0: for (uint32_t index = 0; index < infoMap.Length(); index++) { michael@0: if (infoMap[index].id == objectStoreId) { michael@0: objectStoreInfo = infoMap[index].info; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!objectStoreInfo) { michael@0: NS_ERROR("Index for nonexistant object store!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement(); michael@0: NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: indexInfo->id = stmt->AsInt64(1); michael@0: michael@0: rv = stmt->GetString(2, indexInfo->name); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsString keyPathSerialization; michael@0: rv = stmt->GetString(3, keyPathSerialization); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // XXX bent wants to assert here michael@0: indexInfo->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); michael@0: indexInfo->unique = !!stmt->AsInt32(4); michael@0: indexInfo->multiEntry = !!stmt->AsInt32(5); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Load version information. michael@0: rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT version " michael@0: "FROM database" michael@0: ), getter_AddRefs(stmt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = stmt->ExecuteStep(&hasResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!hasResult) { michael@0: NS_ERROR("Database has no version!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: int64_t version = 0; michael@0: rv = stmt->GetInt64(0, &version); michael@0: michael@0: *aVersion = std::max(version, 0); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, michael@0: uint64_t aVersion, michael@0: ObjectStoreInfoArray& aObjectStores) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aDatabaseInfo, "Null pointer!"); michael@0: michael@0: ObjectStoreInfoArray objectStores; michael@0: objectStores.SwapElements(aObjectStores); michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: nsTArray existingNames; michael@0: aDatabaseInfo->GetObjectStoreNames(existingNames); michael@0: NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo"); michael@0: } michael@0: #endif michael@0: michael@0: aDatabaseInfo->version = aVersion; michael@0: michael@0: for (uint32_t index = 0; index < objectStores.Length(); index++) { michael@0: nsRefPtr& info = objectStores[index]; michael@0: michael@0: if (!aDatabaseInfo->PutObjectStore(info)) { michael@0: NS_WARNING("Out of memory!"); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: if (tmp->mOwningObject) { michael@0: tmp->mOwningObject = nullptr; michael@0: } michael@0: if (tmp->mRootedOwningObject) { michael@0: mozilla::DropJSObjects(tmp); michael@0: tmp->mRootedOwningObject = false; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: nsresult michael@0: IDBFactory::OpenInternal(const nsAString& aName, michael@0: int64_t aVersion, michael@0: PersistenceType aPersistenceType, michael@0: const nsACString& aGroup, michael@0: const nsACString& aASCIIOrigin, michael@0: StoragePrivilege aPrivilege, michael@0: bool aDeleting, michael@0: IDBOpenDBRequest** _retval) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); michael@0: michael@0: AutoJSContext cx; michael@0: nsCOMPtr window; michael@0: JS::Rooted scriptOwner(cx); michael@0: michael@0: if (mWindow) { michael@0: window = mWindow; michael@0: scriptOwner = michael@0: static_cast(window.get())->FastGetGlobalJSObject(); michael@0: } michael@0: else { michael@0: scriptOwner = mOwningObject; michael@0: } michael@0: michael@0: if (aPrivilege == Chrome) { michael@0: // Chrome privilege, ignore the persistence type parameter. michael@0: aPersistenceType = PERSISTENCE_TYPE_PERSISTENT; michael@0: } michael@0: michael@0: nsRefPtr request = michael@0: IDBOpenDBRequest::Create(this, window, scriptOwner); michael@0: IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (IndexedDatabaseManager::IsMainProcess()) { michael@0: nsRefPtr openHelper = michael@0: new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion, michael@0: aPersistenceType, aDeleting, mContentParent, michael@0: aPrivilege); michael@0: michael@0: rv = openHelper->Init(); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) { michael@0: openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: rv = openHelper->WaitForOpenAllowed(); michael@0: } michael@0: else { michael@0: if (mPrivilege != Chrome && michael@0: aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { michael@0: nsRefPtr permissionHelper = michael@0: new CheckPermissionsHelper(openHelper, window); michael@0: michael@0: QuotaManager* quotaManager = QuotaManager::Get(); michael@0: NS_ASSERTION(quotaManager, "This should never be null!"); michael@0: michael@0: rv = quotaManager-> michael@0: WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin), michael@0: Nullable(aPersistenceType), michael@0: openHelper->Id(), permissionHelper); michael@0: } michael@0: else { michael@0: // Chrome and temporary storage doesn't need to check the permission. michael@0: rv = openHelper->WaitForOpenAllowed(); michael@0: } michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: else if (aDeleting) { michael@0: nsCString databaseId; michael@0: QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB, michael@0: aName, databaseId); michael@0: MOZ_ASSERT(!databaseId.IsEmpty()); michael@0: michael@0: IndexedDBDeleteDatabaseRequestChild* actor = michael@0: new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId); michael@0: michael@0: mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor( michael@0: actor, michael@0: nsString(aName), michael@0: aPersistenceType); michael@0: } michael@0: else { michael@0: IndexedDBDatabaseChild* dbActor = michael@0: static_cast( michael@0: mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName), michael@0: aVersion, michael@0: aPersistenceType)); michael@0: michael@0: dbActor->SetRequest(request); michael@0: } michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: { michael@0: NS_ConvertUTF16toUTF8 profilerName(aName); michael@0: if (aDeleting) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", michael@0: "MT IDBFactory.deleteDatabase()", michael@0: request->GetSerialNumber(), profilerName.get()); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", michael@0: "MT IDBFactory.open()", michael@0: request->GetSerialNumber(), profilerName.get(), michael@0: aVersion); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: request.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: JSObject* michael@0: IDBFactory::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBFactoryBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::DeleteDatabase(const nsAString& aName, michael@0: const IDBOpenDBOptions& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: return Open(nullptr, aName, Optional(), aOptions.mStorage, true, michael@0: aRv); michael@0: } michael@0: michael@0: int16_t michael@0: IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, michael@0: JS::Handle aSecond, ErrorResult& aRv) michael@0: { michael@0: Key first, second; michael@0: nsresult rv = first.SetFromJSVal(aCx, aFirst); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return 0; michael@0: } michael@0: michael@0: rv = second.SetFromJSVal(aCx, aSecond); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return 0; michael@0: } michael@0: michael@0: if (first.IsUnset() || second.IsUnset()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return 0; michael@0: } michael@0: michael@0: return Key::CompareKeys(first, second); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, michael@0: uint64_t aVersion, ErrorResult& aRv) michael@0: { michael@0: // Just to be on the extra-safe side michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return Open(aPrincipal, aName, Optional(aVersion), michael@0: Optional(), false, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, michael@0: const IDBOpenDBOptions& aOptions, ErrorResult& aRv) michael@0: { michael@0: // Just to be on the extra-safe side michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false, michael@0: aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, michael@0: const IDBOpenDBOptions& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: // Just to be on the extra-safe side michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return Open(aPrincipal, aName, Optional(), aOptions.mStorage, true, michael@0: aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName, michael@0: const Optional& aVersion, michael@0: const Optional& aStorageType, michael@0: bool aDelete, ErrorResult& aRv) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCString group; michael@0: nsCString origin; michael@0: StoragePrivilege privilege; michael@0: PersistenceType defaultPersistenceType; michael@0: if (aPrincipal) { michael@0: rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin, michael@0: &privilege, michael@0: &defaultPersistenceType); michael@0: if (NS_FAILED(rv)) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: } michael@0: else { michael@0: group = mGroup; michael@0: origin = mASCIIOrigin; michael@0: privilege = mPrivilege; michael@0: defaultPersistenceType = mDefaultPersistenceType; michael@0: } michael@0: michael@0: uint64_t version = 0; michael@0: if (!aDelete && aVersion.WasPassed()) { michael@0: if (aVersion.Value() < 1) { michael@0: aRv.ThrowTypeError(MSG_INVALID_VERSION); michael@0: return nullptr; michael@0: } michael@0: version = aVersion.Value(); michael@0: } michael@0: michael@0: PersistenceType persistenceType = michael@0: PersistenceTypeFromStorage(aStorageType, defaultPersistenceType); michael@0: michael@0: nsRefPtr request; michael@0: rv = OpenInternal(aName, version, persistenceType, group, origin, privilege, michael@0: aDelete, getter_AddRefs(request)); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return nullptr; michael@0: } michael@0: michael@0: return request.forget(); michael@0: }