void IDBDatabase::didAbortTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::didAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); ASSERT(currentThread() == originThreadID()); if (transaction.isVersionChange()) { ASSERT(transaction.originalDatabaseInfo()); ASSERT(m_info.version() == transaction.originalDatabaseInfo()->version()); m_closePending = true; maybeCloseInServer(); } didCommitOrAbortTransaction(transaction); }
void TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aDatabase, "Null pointer!"); // Get list of transactions for this database id DatabaseTransactionInfo* dbTransactionInfo; if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) { // If there are no running transactions, there can't be any pending ones return; } nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions; // Collect any running transactions nsTArray<TransactionInfo>& transactionsInProgress = dbTransactionInfo->transactions; PRUint32 transactionCount = transactionsInProgress.Length(); NS_ASSERTION(transactionCount, "Should never be 0!"); for (PRUint32 index = 0; index < transactionCount; index++) { // See if any transaction belongs to this IDBDatabase instance IDBTransaction* transaction = transactionsInProgress[index].transaction; if (transaction->Database() == aDatabase) { transactions.AppendElement(transaction); } } // Collect any pending transactions. for (PRUint32 index = 0; index < mDelayedDispatchQueue.Length(); index++) { // See if any transaction belongs to this IDBDatabase instance IDBTransaction* transaction = mDelayedDispatchQueue[index].transaction; if (transaction->Database() == aDatabase) { transactions.AppendElement(transaction); } } // Abort transactions. Do this after collecting the transactions in case // calling Abort() modifies the data structures we're iterating above. for (PRUint32 index = 0; index < transactions.Length(); index++) { // This can fail, for example if the transaction is in the process of // being comitted. That is expected and fine, so we ignore any returned // errors. transactions[index]->Abort(); } }
bool IDBConnectionProxy::hasRecordOfTransaction(const IDBTransaction& transaction) const { ASSERT(m_transactionMapLock.isLocked()); auto identifier = transaction.info().identifier(); return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier); }
IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction) : IDBActiveDOMObject(&context) , m_transaction(&transaction) , m_resourceIdentifier(transaction.connectionProxy()) , m_pendingCursor(&cursor) , m_connectionProxy(transaction.database().connectionProxy()) { suspendIfNeeded(); WTF::switchOn(cursor.source(), [this] (const auto& value) { this->m_source = IDBRequest::Source { value }; } ); m_result = NullResultType::Empty; cursor.setRequest(*this); }
void IDBDatabase::didStartTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::didStartTransaction"); ASSERT(!m_versionChangeTransaction); m_activeTransactions.set(transaction.info().identifier(), &transaction); }
IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction) : IDBOpenDBRequest(&context) , m_transaction(&transaction) , m_connection(transaction.serverConnection()) , m_resourceIdentifier(transaction.serverConnection()) , m_pendingCursor(&cursor) { suspendIfNeeded(); cursor.setRequest(*this); auto* cursorSource = cursor.source(); ASSERT(cursorSource); ASSERT(cursorSource->type() == IDBAny::Type::IDBObjectStore || cursorSource->type() == IDBAny::Type::IDBIndex); m_source = cursorSource; }
static v8::Handle<v8::Value> dispatchEventCallback(const v8::Arguments& args) { INC_STATS("DOM.IDBTransaction.dispatchEvent"); IDBTransaction* imp = V8IDBTransaction::toNative(args.Holder()); ExceptionCode ec = 0; { EXCEPTION_BLOCK(Event*, evt, V8Event::HasInstance(MAYBE_MISSING_PARAMETER(args, 0, MissingIsUndefined)) ? V8Event::toNative(v8::Handle<v8::Object>::Cast(MAYBE_MISSING_PARAMETER(args, 0, MissingIsUndefined))) : 0); bool result = imp->dispatchEvent(evt, ec); if (UNLIKELY(ec)) goto fail; return v8Boolean(result); } fail: V8Proxy::setDOMException(ec); return v8::Handle<v8::Value>(); }
static v8::Handle<v8::Value> objectStoreCallback(const v8::Arguments& args) { INC_STATS("DOM.IDBTransaction.objectStore"); IDBTransaction* imp = V8IDBTransaction::toNative(args.Holder()); ExceptionCode ec = 0; { STRING_TO_V8PARAMETER_EXCEPTION_BLOCK(V8Parameter<>, name, MAYBE_MISSING_PARAMETER(args, 0, MissingIsUndefined)); RefPtr<IDBObjectStore> result = imp->objectStore(name, ec); if (UNLIKELY(ec)) goto fail; return toV8(result.release()); } fail: V8Proxy::setDOMException(ec); return v8::Handle<v8::Value>(); }
NS_IMETHODIMP IDBIndex::OpenKeyCursor(const jsval& aKey, PRUint16 aDirection, JSContext* aCx, PRUint8 aOptionalArgCount, nsIIDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } nsresult rv; nsRefPtr<IDBKeyRange> keyRange; if (aOptionalArgCount) { rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); NS_ENSURE_SUCCESS(rv, rv); if (aOptionalArgCount >= 2) { if (aDirection != nsIIDBCursor::NEXT && aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE && aDirection != nsIIDBCursor::PREV && aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) { return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR; } } else { aDirection = nsIIDBCursor::NEXT; } } nsRefPtr<IDBRequest> request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<OpenKeyCursorHelper> helper = new OpenKeyCursorHelper(transaction, request, this, keyRange, aDirection); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); request.forget(_retval); return NS_OK; }
void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); if (m_versionChangeTransaction == &transaction) m_versionChangeTransaction = nullptr; #ifndef NDBEBUG unsigned count = 0; if (m_activeTransactions.contains(transaction.info().identifier())) ++count; if (m_committingTransactions.contains(transaction.info().identifier())) ++count; if (m_abortingTransactions.contains(transaction.info().identifier())) ++count; ASSERT(count == 1); #endif m_activeTransactions.remove(transaction.info().identifier()); m_committingTransactions.remove(transaction.info().identifier()); m_abortingTransactions.remove(transaction.info().identifier()); if (m_closePending) maybeCloseInServer(); }
void IDBDatabase::didCommitTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::didCommitTransaction"); if (m_versionChangeTransaction == &transaction) m_info.setVersion(transaction.info().newVersion()); didCommitOrAbortTransaction(transaction); }
void IDBDatabase::stop() { LOG(IndexedDB, "IDBDatabase::stop - %" PRIu64, m_databaseConnectionIdentifier); Vector<IDBResourceIdentifier> transactionIdentifiers; transactionIdentifiers.reserveInitialCapacity(m_activeTransactions.size()); for (auto& id : m_activeTransactions.keys()) transactionIdentifiers.uncheckedAppend(id); for (auto& id : transactionIdentifiers) { IDBTransaction* transaction = m_activeTransactions.get(id); if (transaction) transaction->stop(); } close(); }
NS_IMETHODIMP IDBIndex::GetAllKeys(nsIVariant* aKey, PRUint32 aLimit, PRUint8 aOptionalArgCount, nsIIDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } nsresult rv; Key key; if (aOptionalArgCount && NS_FAILED(IDBObjectStore::GetKeyFromVariant(aKey, key))) { PRUint16 type; rv = aKey->GetDataType(&type); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); if (type != nsIDataType::VTYPE_EMPTY) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } } if (aOptionalArgCount < 2) { aLimit = PR_UINT32_MAX; } nsRefPtr<IDBRequest> request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<GetAllKeysHelper> helper = new GetAllKeysHelper(transaction, request, this, key, aLimit); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); request.forget(_retval); return NS_OK; }
void IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv) { AssertIsOnOwningThread(); IDBTransaction* transaction = mObjectStore->Transaction(); if (transaction->GetMode() != IDBTransaction::VERSION_CHANGE || mDeletedMetadata) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (aName == mMetadata->name()) { return; } // Cache logging string of this index before renaming. const LoggingString loggingOldIndex(this); const int64_t indexId = Id(); nsresult rv = transaction->Database()->RenameIndex(mObjectStore->Id(), indexId, aName); if (NS_FAILED(rv)) { aRv.Throw(rv); return; } // Don't do this in the macro because we always need to increment the serial // number to keep in sync with the parent. const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "rename(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.rename()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), loggingOldIndex.get(), IDB_LOG_STRINGIFY(this)); transaction->RenameIndex(mObjectStore, indexId, aName); }
void IDBDatabase::stop() { LOG(IndexedDB, "IDBDatabase::stop - %" PRIu64, m_databaseConnectionIdentifier); ASSERT(&originThread() == &Thread::current()); removeAllEventListeners(); Vector<IDBResourceIdentifier> transactionIdentifiers; transactionIdentifiers.reserveInitialCapacity(m_activeTransactions.size()); for (auto& id : m_activeTransactions.keys()) transactionIdentifiers.uncheckedAppend(id); for (auto& id : transactionIdentifiers) { IDBTransaction* transaction = m_activeTransactions.get(id); if (transaction) transaction->stop(); } close(); }
NS_IMETHODIMP IDBIndex::GetAllKeys(const jsval& aKey, PRUint32 aLimit, JSContext* aCx, PRUint8 aOptionalArgCount, nsIIDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } nsresult rv; nsRefPtr<IDBKeyRange> keyRange; if (aOptionalArgCount) { rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); NS_ENSURE_SUCCESS(rv, rv); } if (aOptionalArgCount < 2 || aLimit == 0) { aLimit = PR_UINT32_MAX; } nsRefPtr<IDBRequest> request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<GetAllKeysHelper> helper = new GetAllKeysHelper(transaction, request, this, keyRange, aLimit); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); request.forget(_retval); return NS_OK; }
void IDBDatabase::willAbortTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::willAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data()); auto refTransaction = m_activeTransactions.take(transaction.info().identifier()); ASSERT(refTransaction); m_abortingTransactions.set(transaction.info().identifier(), WTFMove(refTransaction)); if (transaction.isVersionChange()) { ASSERT(transaction.originalDatabaseInfo()); m_info = *transaction.originalDatabaseInfo(); m_closePending = true; } }
void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction) { LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction"); if (m_versionChangeTransaction == &transaction) m_versionChangeTransaction = nullptr; #ifndef NDBEBUG unsigned count = 0; if (m_activeTransactions.contains(transaction.info().identifier())) ++count; if (m_committingTransactions.contains(transaction.info().identifier())) ++count; if (m_abortingTransactions.contains(transaction.info().identifier())) ++count; ASSERT(count == 1); #endif m_activeTransactions.remove(transaction.info().identifier()); m_committingTransactions.remove(transaction.info().identifier()); m_abortingTransactions.remove(transaction.info().identifier()); }
already_AddRefed<IDBObjectStore> IDBDatabase::CreateObjectStore( const nsAString& aName, const IDBObjectStoreParameters& aOptionalParameters, ErrorResult& aRv) { AssertIsOnOwningThread(); IDBTransaction* transaction = IDBTransaction::GetCurrent(); if (!transaction || transaction->Database() != this || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } KeyPath keyPath(0); if (NS_FAILED(KeyPath::Parse(aOptionalParameters.mKeyPath, &keyPath))) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores(); for (uint32_t count = objectStores.Length(), index = 0; index < count; index++) { if (aName == objectStores[index].metadata().name()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); return nullptr; } } if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return nullptr; } const ObjectStoreSpec* oldSpecElements = objectStores.IsEmpty() ? nullptr : objectStores.Elements(); ObjectStoreSpec* newSpec = objectStores.AppendElement(); newSpec->metadata() = ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName), keyPath, aOptionalParameters.mAutoIncrement); if (oldSpecElements && oldSpecElements != objectStores.Elements()) { MOZ_ASSERT(objectStores.Length() > 1); // Array got moved, update the spec pointers for all live objectStores and // indexes. RefreshSpec(/* aMayDelete */ false); } RefPtr<IDBObjectStore> objectStore = transaction->CreateObjectStore(*newSpec); MOZ_ASSERT(objectStore); // Don't do this in the macro because we always need to increment the serial // number to keep in sync with the parent. const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).createObjectStore(%s)", "IndexedDB %s: C T[%lld] R[%llu]: " "IDBDatabase.createObjectStore()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(objectStore)); return objectStore.forget(); }
bool IDBConnectionToServer::hasRecordOfTransaction(const IDBTransaction& transaction) const { auto identifier = transaction.info().identifier(); return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier); }
IDBTransaction* IDBTransaction::create(ExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) { IDBTransaction* transaction = adoptRefCountedGarbageCollected(new IDBTransaction(context, id, Vector<String>(), WebIDBDatabase::TransactionVersionChange, db, openDBRequest, previousMetadata)); transaction->suspendIfNeeded(); return transaction; }
IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) { IDBTransaction* transaction = new IDBTransaction(scriptState, id, HashSet<String>(), WebIDBTransactionModeVersionChange, db, openDBRequest, previousMetadata); transaction->suspendIfNeeded(); return transaction; }
already_AddRefed<IDBRequest> IDBIndex::OpenCursorInternal(bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aRange, IDBCursorDirection aDirection, ErrorResult& aRv) { AssertIsOnOwningThread(); if (mDeletedMetadata) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } RefPtr<IDBKeyRange> keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } int64_t objectStoreId = mObjectStore->Id(); int64_t indexId = Id(); OptionalKeyRange optionalKeyRange; if (keyRange) { SerializedKeyRange serializedKeyRange; keyRange->ToSerialized(serializedKeyRange); optionalKeyRange = Move(serializedKeyRange); } else { optionalKeyRange = void_t(); } IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); OpenCursorParams params; if (aKeysOnly) { IndexOpenKeyCursorParams openParams; openParams.objectStoreId() = objectStoreId; openParams.indexId() = indexId; openParams.optionalKeyRange() = Move(optionalKeyRange); openParams.direction() = direction; params = Move(openParams); } else { IndexOpenCursorParams openParams; openParams.objectStoreId() = objectStoreId; openParams.indexId() = indexId; openParams.optionalKeyRange() = Move(optionalKeyRange); openParams.direction() = direction; params = Move(openParams); } RefPtr<IDBRequest> request = GenerateRequest(this); MOZ_ASSERT(request); if (aKeysOnly) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "openKeyCursor(%s, %s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.openKeyCursor()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(direction)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "openCursor(%s, %s)", "IndexedDB %s: C T[%lld] R[%llu]: " "IDBObjectStore.openKeyCursor()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(direction)); } BackgroundCursorChild* actor = new BackgroundCursorChild(request, this, direction); mObjectStore->Transaction()->OpenCursor(actor, params); return request.forget(); }
already_AddRefed<IDBRequest> IDBIndex::GetAllInternal(bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aKey, const Optional<uint32_t>& aLimit, ErrorResult& aRv) { AssertIsOnOwningThread(); if (mDeletedMetadata) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } RefPtr<IDBKeyRange> keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } const int64_t objectStoreId = mObjectStore->Id(); const int64_t indexId = Id(); OptionalKeyRange optionalKeyRange; if (keyRange) { SerializedKeyRange serializedKeyRange; keyRange->ToSerialized(serializedKeyRange); optionalKeyRange = serializedKeyRange; } else { optionalKeyRange = void_t(); } const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; RequestParams params; if (aKeysOnly) { params = IndexGetAllKeysParams(objectStoreId, indexId, optionalKeyRange, limit); } else { params = IndexGetAllParams(objectStoreId, indexId, optionalKeyRange, limit); } RefPtr<IDBRequest> request = GenerateRequest(this); MOZ_ASSERT(request); if (aKeysOnly) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "getAllKeys(%s, %s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAllKeys()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "getAll(%s, %s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAll()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit)); } transaction->StartRequest(request, params); return request.forget(); }
already_AddRefed<IDBRequest> IDBIndex::GetInternal(bool aKeyOnly, JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv) { AssertIsOnOwningThread(); if (mDeletedMetadata) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } RefPtr<IDBKeyRange> keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } if (!keyRange) { // Must specify a key or keyRange for get() and getKey(). aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return nullptr; } const int64_t objectStoreId = mObjectStore->Id(); const int64_t indexId = Id(); SerializedKeyRange serializedKeyRange; keyRange->ToSerialized(serializedKeyRange); RequestParams params; if (aKeyOnly) { params = IndexGetKeyParams(objectStoreId, indexId, serializedKeyRange); } else { params = IndexGetParams(objectStoreId, indexId, serializedKeyRange); } RefPtr<IDBRequest> request = GenerateRequest(this); MOZ_ASSERT(request); if (aKeyOnly) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "getKey(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getKey()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "get(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.get()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction->Database()), IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange)); } transaction->StartRequest(request, params); return request.forget(); }
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; }
void IDBConnectionProxy::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction& transaction) { callConnectionOnMainThread(&IDBConnectionToServer::didFinishHandlingVersionChangeTransaction, databaseConnectionIdentifier, transaction.info().identifier()); }
void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) { AssertIsOnOwningThread(); IDBTransaction* transaction = IDBTransaction::GetCurrent(); if (!transaction || transaction->Database() != this || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } if (!transaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } nsTArray<ObjectStoreSpec>& specArray = mSpec->objectStores(); int64_t objectStoreId = 0; for (uint32_t specCount = specArray.Length(), specIndex = 0; specIndex < specCount; specIndex++) { const ObjectStoreMetadata& metadata = specArray[specIndex].metadata(); MOZ_ASSERT(metadata.id()); if (aName == metadata.name()) { objectStoreId = metadata.id(); // Must do this before altering the metadata array! transaction->DeleteObjectStore(objectStoreId); specArray.RemoveElementAt(specIndex); RefreshSpec(/* aMayDelete */ false); break; } } if (!objectStoreId) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return; } // Don't do this in the macro because we always need to increment the serial // number to keep in sync with the parent. const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).deleteObjectStore(\"%s\")", "IndexedDB %s: C T[%lld] R[%llu]: " "IDBDatabase.deleteObjectStore()", IDB_LOG_ID_STRING(), transaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(transaction), NS_ConvertUTF16toUTF8(aName).get()); }
void IDBDatabase::AbortTransactions(bool aShouldWarn) { AssertIsOnOwningThread(); class MOZ_STACK_CLASS Helper final { typedef nsAutoTArray<RefPtr<IDBTransaction>, 20> StrongTransactionArray; typedef nsAutoTArray<IDBTransaction*, 20> WeakTransactionArray; public: static void AbortTransactions(IDBDatabase* aDatabase, const bool aShouldWarn) { MOZ_ASSERT(aDatabase); aDatabase->AssertIsOnOwningThread(); nsTHashtable<nsPtrHashKey<IDBTransaction>>& transactionTable = aDatabase->mTransactions; if (!transactionTable.Count()) { return; } StrongTransactionArray transactionsToAbort; transactionsToAbort.SetCapacity(transactionTable.Count()); for (auto iter = transactionTable.Iter(); !iter.Done(); iter.Next()) { IDBTransaction* transaction = iter.Get()->GetKey(); MOZ_ASSERT(transaction); transaction->AssertIsOnOwningThread(); // Transactions that are already done can simply be ignored. Otherwise // there is a race here and it's possible that the transaction has not // been successfully committed yet so we will warn the user. if (!transaction->IsDone()) { transactionsToAbort.AppendElement(transaction); } } MOZ_ASSERT(transactionsToAbort.Length() <= transactionTable.Count()); if (transactionsToAbort.IsEmpty()) { return; } // We want to abort transactions as soon as possible so we iterate the // transactions once and abort them all first, collecting the transactions // that need to have a warning issued along the way. Those that need a // warning will be a subset of those that are aborted, so we don't need // additional strong references here. WeakTransactionArray transactionsThatNeedWarning; for (RefPtr<IDBTransaction>& transaction : transactionsToAbort) { MOZ_ASSERT(transaction); MOZ_ASSERT(!transaction->IsDone()); if (aShouldWarn) { switch (transaction->GetMode()) { // We ignore transactions that could not have written any data. case IDBTransaction::READ_ONLY: break; // We warn for any transactions that could have written data. case IDBTransaction::READ_WRITE: case IDBTransaction::READ_WRITE_FLUSH: case IDBTransaction::VERSION_CHANGE: transactionsThatNeedWarning.AppendElement(transaction); break; default: MOZ_CRASH("Unknown mode!"); } } transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation"; for (IDBTransaction* transaction : transactionsThatNeedWarning) { MOZ_ASSERT(transaction); nsString filename; uint32_t lineNo, column; transaction->GetCallerLocation(filename, &lineNo, &column); aDatabase->LogWarning(kWarningMessage, filename, lineNo, column); } }
NS_IMETHODIMP IDBIndex::OpenKeyCursor(nsIIDBKeyRange* aKeyRange, PRUint16 aDirection, PRUint8 aOptionalArgCount, nsIIDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } nsresult rv; Key lowerKey, upperKey; PRBool lowerOpen = PR_FALSE, upperOpen = PR_FALSE; if (aKeyRange) { nsCOMPtr<nsIVariant> variant; rv = aKeyRange->GetLower(getter_AddRefs(variant)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey); if (NS_FAILED(rv)) { return rv; } rv = aKeyRange->GetUpper(getter_AddRefs(variant)); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey); if (NS_FAILED(rv)) { return rv; } rv = aKeyRange->GetLowerOpen(&lowerOpen); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = aKeyRange->GetUpperOpen(&upperOpen); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } if (aOptionalArgCount >= 2) { if (aDirection != nsIIDBCursor::NEXT && aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE && aDirection != nsIIDBCursor::PREV && aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) { return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR; } } else { aDirection = nsIIDBCursor::NEXT; } nsRefPtr<IDBRequest> request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<OpenKeyCursorHelper> helper = new OpenKeyCursorHelper(transaction, request, this, lowerKey, upperKey, lowerOpen, upperOpen, aDirection); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); request.forget(_retval); return NS_OK; }