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; }
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; }
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; }