NS_IMETHODIMP IDBDatabase::DeleteObjectStore(const nsAString& aName) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction->Mode() != nsIIDBTransaction::VERSION_CHANGE) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } DatabaseInfo* info = transaction->DBInfo(); ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); if (!objectStoreInfo) { return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; } nsRefPtr<DeleteObjectStoreHelper> helper = new DeleteObjectStoreHelper(transaction, objectStoreInfo->id); nsresult rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); transaction->RemoveObjectStore(aName); return NS_OK; }
/** * Determines whether this databaseInfo refers to a different database than * another one * * The databases can be identical (i. e. this method returns false) even if * some values are different, for example, if they are not active because they * belong to a different database type than selected. * * @param other * @return */ bool DatabaseInfo::different (const DatabaseInfo &other) { if (server !=other.server ) return true; if (effectivePort ()!=other.effectivePort ()) return true; if (username !=other.username ) return true; if (password !=other.password ) return true; if (database !=other.database ) return true; return false; }
NS_IMETHODIMP IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); DatabaseInfo* info = Info(); nsAutoTArray<nsString, 10> objectStoreNames; if (!info->GetObjectStoreNames(objectStoreNames)) { NS_WARNING("Couldn't get names!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } nsRefPtr<nsDOMStringList> list(new nsDOMStringList()); PRUint32 count = objectStoreNames.Length(); for (PRUint32 index = 0; index < count; index++) { NS_ENSURE_TRUE(list->Add(objectStoreNames[index]), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } list.forget(aObjectStores); return NS_OK; }
NS_IMETHODIMP IDBTransaction::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr<nsDOMStringList> list(new nsDOMStringList()); nsTArray<nsString> stackArray; nsTArray<nsString>* arrayOfNames; if (mMode == IDBTransaction::FULL_LOCK) { DatabaseInfo* info; if (!DatabaseInfo::Get(mDatabase->Id(), &info)) { NS_ERROR("This should never fail!"); return NS_ERROR_UNEXPECTED; } if (!info->GetObjectStoreNames(stackArray)) { NS_ERROR("Out of memory!"); return NS_ERROR_OUT_OF_MEMORY; } arrayOfNames = &stackArray; } else { arrayOfNames = &mObjectStoreNames; } PRUint32 count = arrayOfNames->Length(); for (PRUint32 index = 0; index < count; index++) { NS_ENSURE_TRUE(list->Add(arrayOfNames->ElementAt(index)), NS_ERROR_OUT_OF_MEMORY); } list.forget(aObjectStores); return NS_OK; }
nsresult OpenDatabaseHelper::EnsureSuccessResult() { DatabaseInfo* dbInfo; if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) { NS_ASSERTION(dbInfo->referenceCount, "Bad reference count!"); ++dbInfo->referenceCount; #ifdef DEBUG { NS_ASSERTION(dbInfo->name == mName && dbInfo->version == mCurrentVersion && dbInfo->id == mDatabaseId && dbInfo->filePath == mDatabaseFilePath, "Metadata mismatch!"); PRUint32 objectStoreCount = mObjectStores.Length(); for (PRUint32 index = 0; index < objectStoreCount; index++) { nsAutoPtr<ObjectStoreInfo>& info = mObjectStores[index]; NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!"); ObjectStoreInfo* otherInfo; NS_ASSERTION(ObjectStoreInfo::Get(mDatabaseId, info->name, &otherInfo), "ObjectStore not known!"); NS_ASSERTION(info->name == otherInfo->name && info->id == otherInfo->id && info->keyPath == otherInfo->keyPath && info->autoIncrement == otherInfo->autoIncrement && info->databaseId == otherInfo->databaseId, "Metadata mismatch!"); NS_ASSERTION(dbInfo->ContainsStoreName(info->name), "Object store names out of date!"); NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(), "Bad index length!"); PRUint32 indexCount = info->indexes.Length(); for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) { const IndexInfo& indexInfo = info->indexes[indexIndex]; const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex]; NS_ASSERTION(indexInfo.id == otherIndexInfo.id, "Bad index id!"); NS_ASSERTION(indexInfo.name == otherIndexInfo.name, "Bad index name!"); NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath, "Bad index keyPath!"); NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, "Bad index unique value!"); NS_ASSERTION(indexInfo.autoIncrement == otherIndexInfo.autoIncrement, "Bad index autoIncrement value!"); } } } #endif } else { nsAutoPtr<DatabaseInfo> newInfo(new DatabaseInfo()); newInfo->name = mName; newInfo->id = mDatabaseId; newInfo->filePath = mDatabaseFilePath; newInfo->referenceCount = 1; if (!DatabaseInfo::Put(newInfo)) { NS_ERROR("Failed to add to hash!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } dbInfo = newInfo.forget(); nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mCurrentVersion, mObjectStores); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); } dbInfo->nextObjectStoreId = mLastObjectStoreId + 1; dbInfo->nextIndexId = mLastIndexId + 1; nsRefPtr<IDBDatabase> database = IDBDatabase::Create(mOpenDBRequest->ScriptContext(), mOpenDBRequest->Owner(), dbInfo, mASCIIOrigin); if (!database) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!"); mDatabase.swap(database); return NS_OK; }
nsresult IDBTransaction::AbortInternal(nsresult aAbortCode, already_AddRefed<nsIDOMDOMError> aError) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsCOMPtr<nsIDOMDOMError> error = aError; if (IsFinished()) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } if (mActorChild) { NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); mActorChild->SendAbort(aAbortCode); } bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL; mAbortCode = aAbortCode; mReadyState = IDBTransaction::DONE; mError = error.forget(); if (GetMode() == IDBTransaction::VERSION_CHANGE) { // If a version change transaction is aborted, we must revert the world // back to its previous state. mDatabase->RevertToPreviousState(); DatabaseInfo* dbInfo = mDatabase->Info(); for (PRUint32 i = 0; i < mCreatedObjectStores.Length(); i++) { nsRefPtr<IDBObjectStore>& objectStore = mCreatedObjectStores[i]; ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); if (!info) { info = new ObjectStoreInfo(*objectStore->Info()); info->indexes.Clear(); } objectStore->SetInfo(info); } for (PRUint32 i = 0; i < mDeletedObjectStores.Length(); i++) { nsRefPtr<IDBObjectStore>& objectStore = mDeletedObjectStores[i]; ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); if (!info) { info = new ObjectStoreInfo(*objectStore->Info()); info->indexes.Clear(); } objectStore->SetInfo(info); } // and then the db must be closed mDatabase->Close(); } // Fire the abort event if there are no outstanding requests. Otherwise the // abort event will be fired when all outstanding requests finish. if (needToCommitOrRollback) { return CommitOrRollback(); } return NS_OK; }
NS_IMETHODIMP IDBDatabase::Transaction(const jsval& aStoreNames, PRUint16 aMode, JSContext* aCx, PRUint8 aOptionalArgCount, nsIIDBTransaction** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (IndexedDatabaseManager::IsShuttingDown()) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (mClosed) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } if (mRunningVersionChange) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } if (aOptionalArgCount) { if (aMode != nsIIDBTransaction::READ_WRITE && aMode != nsIIDBTransaction::READ_ONLY) { return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR; } } else { aMode = nsIIDBTransaction::READ_ONLY; } nsresult rv; nsTArray<nsString> storesToOpen; if (!JSVAL_IS_PRIMITIVE(aStoreNames)) { JSObject* obj = JSVAL_TO_OBJECT(aStoreNames); // See if this is a JS array. if (JS_IsArrayObject(aCx, obj)) { jsuint length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!length) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } storesToOpen.SetCapacity(length); for (jsuint index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; if (!JS_GetElement(aCx, obj, index, &val) || !(jsstr = JS_ValueToString(aCx, val)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } storesToOpen.AppendElement(str); } NS_ASSERTION(!storesToOpen.IsEmpty(), "Must have something here or else code below will " "misbehave!"); } else { // Perhaps some kind of wrapped object? nsIXPConnect* xpc = nsContentUtils::XPConnect(); NS_ASSERTION(xpc, "This should never be null!"); nsCOMPtr<nsIXPConnectWrappedNative> wrapper; rv = xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrapper)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (wrapper) { nsISupports* wrappedObject = wrapper->Native(); NS_ENSURE_TRUE(wrappedObject, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); // We only accept DOMStringList. nsCOMPtr<nsIDOMDOMStringList> list = do_QueryInterface(wrappedObject); if (list) { PRUint32 length; rv = list->GetLength(&length); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (!length) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } storesToOpen.SetCapacity(length); for (PRUint32 index = 0; index < length; index++) { nsString* item = storesToOpen.AppendElement(); NS_ASSERTION(item, "This should never fail!"); rv = list->Item(index, *item); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } NS_ASSERTION(!storesToOpen.IsEmpty(), "Must have something here or else code below will " "misbehave!"); } } } } // If our list is empty here then the argument must have been an object that // we don't support or a primitive. Either way we convert to a string. if (storesToOpen.IsEmpty()) { JSString* jsstr; nsDependentJSString str; if (!(jsstr = JS_ValueToString(aCx, aStoreNames)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } storesToOpen.AppendElement(str); } // Now check to make sure the object store names we collected actually exist. DatabaseInfo* info = Info(); for (PRUint32 index = 0; index < storesToOpen.Length(); index++) { if (!info->ContainsStoreName(storesToOpen[index])) { return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; } } nsRefPtr<IDBTransaction> transaction = IDBTransaction::Create(this, storesToOpen, aMode, false); NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); transaction.forget(_retval); return NS_OK; }
NS_IMETHODIMP IDBDatabase::CreateObjectStore(const nsAString& aName, const jsval& aOptions, JSContext* aCx, nsIIDBObjectStore** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction->Mode() != nsIIDBTransaction::VERSION_CHANGE) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } DatabaseInfo* databaseInfo = transaction->DBInfo(); mozilla::dom::IDBObjectStoreParameters params; nsString keyPath; keyPath.SetIsVoid(true); nsTArray<nsString> keyPathArray; if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) { nsresult rv = params.Init(aCx, &aOptions); NS_ENSURE_SUCCESS(rv, rv); // Get keyPath jsval val = params.keyPath; if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) { if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) { JSObject* obj = JSVAL_TO_OBJECT(val); jsuint length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!length) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPathArray.SetCapacity(length); for (jsuint index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; if (!JS_GetElement(aCx, obj, index, &val) || !(jsstr = JS_ValueToString(aCx, val)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!IDBObjectStore::IsValidKeyPath(aCx, str)) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPathArray.AppendElement(str); } NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!"); } else { JSString* jsstr; nsDependentJSString str; if (!(jsstr = JS_ValueToString(aCx, val)) || !str.init(aCx, jsstr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } if (!IDBObjectStore::IsValidKeyPath(aCx, str)) { return NS_ERROR_DOM_SYNTAX_ERR; } keyPath = str; } } } if (databaseInfo->ContainsStoreName(aName)) { return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; } if (params.autoIncrement && ((!keyPath.IsVoid() && keyPath.IsEmpty()) || !keyPathArray.IsEmpty())) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } nsRefPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo()); newInfo->name = aName; newInfo->id = databaseInfo->nextObjectStoreId++; newInfo->keyPath = keyPath; newInfo->keyPathArray = keyPathArray; newInfo->nextAutoIncrementId = params.autoIncrement ? 1 : 0; newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; if (!databaseInfo->PutObjectStore(newInfo)) { NS_WARNING("Put failed!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } // Don't leave this in the hash if we fail below! AutoRemoveObjectStore autoRemove(databaseInfo, aName); nsRefPtr<IDBObjectStore> objectStore = transaction->GetOrCreateObjectStore(aName, newInfo); NS_ENSURE_TRUE(objectStore, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<CreateObjectStoreHelper> helper = new CreateObjectStoreHelper(transaction, objectStore); nsresult rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); autoRemove.forget(); objectStore.forget(_retval); return NS_OK; }