IDBRequest* IDBCursor::update(ScriptState* scriptState, const ScriptValue& value, ExceptionState& exceptionState) { IDB_TRACE("IDBCursor::update"); if (!m_gotValue) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); return nullptr; } if (isKeyCursor()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage); return nullptr; } if (isDeleted()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); return nullptr; } if (m_transaction->isFinished() || m_transaction->isFinishing()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); return nullptr; } if (!m_transaction->isActive()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); return nullptr; } if (m_transaction->isReadOnly()) { exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction."); return nullptr; } IDBObjectStore* objectStore = effectiveObjectStore(); return objectStore->put(scriptState, WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState); }
void IDBTransaction::revertDatabaseMetadata() { DCHECK_NE(m_state, Active); if (!isVersionChange()) return; // Mark stores created by this transaction as deleted. for (auto& objectStore : m_objectStoreMap.values()) { const int64_t objectStoreId = objectStore->id(); if (!objectStore->isNewlyCreated()) { DCHECK(m_oldStoreMetadata.contains(objectStore)); continue; } DCHECK(!m_oldStoreMetadata.contains(objectStore)); m_database->revertObjectStoreCreation(objectStoreId); objectStore->markDeleted(); } for (auto& it : m_oldStoreMetadata) { IDBObjectStore* objectStore = it.key; RefPtr<IDBObjectStoreMetadata> oldMetadata = it.value; m_database->revertObjectStoreMetadata(oldMetadata); objectStore->revertMetadata(oldMetadata); } for (auto& index : m_deletedIndexes) index->objectStore()->revertDeletedIndexMetadata(*index); for (auto& oldMedata : m_deletedObjectStores) m_database->revertObjectStoreMetadata(std::move(oldMedata)); // We only need to revert the database's own metadata because we have reverted // the metadata for the database's object stores above. m_database->setDatabaseMetadata(m_oldDatabaseMetadata); }
void IDBTransaction::finished() { #if DCHECK_IS_ON() DCHECK(!m_finishCalled); m_finishCalled = true; #endif // DCHECK_IS_ON() m_database->transactionFinished(this); // Remove references to the IDBObjectStore and IDBIndex instances held by // this transaction, so Oilpan can garbage-collect the instances that aren't // used by JavaScript. for (auto& it : m_objectStoreMap) { IDBObjectStore* objectStore = it.value; if (!isVersionChange() || objectStore->isNewlyCreated()) { DCHECK(!m_oldStoreMetadata.contains(objectStore)); objectStore->clearIndexCache(); } else { // We'll call clearIndexCache() on this store in the loop below. DCHECK(m_oldStoreMetadata.contains(objectStore)); } } m_objectStoreMap.clear(); for (auto& it : m_oldStoreMetadata) { IDBObjectStore* objectStore = it.key; objectStore->clearIndexCache(); } m_oldStoreMetadata.clear(); m_deletedIndexes.clear(); m_deletedObjectStores.clear(); }
void IDBDatabase::renameObjectStore(IDBObjectStore& objectStore, const String& newName) { ASSERT(&originThread() == &Thread::current()); ASSERT(m_versionChangeTransaction); ASSERT(m_info.hasObjectStore(objectStore.info().name())); m_info.renameObjectStore(objectStore.info().identifier(), newName); m_versionChangeTransaction->renameObjectStore(objectStore, newName); }
void IDBTransaction::objectStoreDeleted(const String& name) { ASSERT(m_state != Finished); ASSERT(isVersionChange()); IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); if (it != m_objectStoreMap.end()) { IDBObjectStore* objectStore = it->value; m_objectStoreMap.remove(name); objectStore->markDeleted(); m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); m_deletedObjectStores.add(objectStore); } }
IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState) { if (isFinished()) { exceptionState.throwDOMException( InvalidStateError, IDBDatabase::transactionFinishedErrorMessage); return nullptr; } IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); if (it != m_objectStoreMap.end()) return it->value; if (!isVersionChange() && !m_scope.contains(name)) { exceptionState.throwDOMException( NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); return nullptr; } int64_t objectStoreId = m_database->findObjectStoreId(name); if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { DCHECK(isVersionChange()); exceptionState.throwDOMException( NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); return nullptr; } DCHECK(m_database->metadata().objectStores.contains(objectStoreId)); RefPtr<IDBObjectStoreMetadata> objectStoreMetadata = m_database->metadata().objectStores.get(objectStoreId); DCHECK(objectStoreMetadata.get()); IDBObjectStore* objectStore = IDBObjectStore::create(std::move(objectStoreMetadata), this); DCHECK(!m_objectStoreMap.contains(name)); m_objectStoreMap.set(name, objectStore); if (isVersionChange()) { DCHECK(!objectStore->isNewlyCreated()) << "Object store IDs are not assigned sequentially"; RefPtr<IDBObjectStoreMetadata> backupMetadata = objectStore->metadata().createCopy(); m_oldStoreMetadata.set(objectStore, std::move(backupMetadata)); } return objectStore; }
IDBRequest* IDBCursor::update(ScriptState* scriptState, ScriptValue& value, ExceptionState& exceptionState) { IDB_TRACE("IDBCursor::update"); if (!m_gotValue) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage); return 0; } if (isKeyCursor()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage); return 0; } if (isDeleted()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage); return 0; } if (m_transaction->isFinished() || m_transaction->isFinishing()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage); return 0; } if (!m_transaction->isActive()) { exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage); return 0; } if (m_transaction->isReadOnly()) { exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction."); return 0; } IDBObjectStore* objectStore = effectiveObjectStore(); const IDBKeyPath& keyPath = objectStore->metadata().keyPath; const bool usesInLineKeys = !keyPath.isNull(); if (usesInLineKeys) { IDBKey* keyPathKey = createIDBKeyFromScriptValueAndKeyPath(scriptState->isolate(), value, keyPath); if (!keyPathKey || !keyPathKey->isEqual(m_primaryKey.get())) { exceptionState.throwDOMException(DataError, "The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key."); return 0; } } return objectStore->put(scriptState, blink::WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState); }
ScriptValue IDBCursor::value(ScriptState* scriptState) { ASSERT(isCursorWithValue()); IDBObjectStore* objectStore = effectiveObjectStore(); const IDBObjectStoreMetadata& metadata = objectStore->metadata(); IDBAny* value; if (metadata.autoIncrement && !metadata.keyPath.isNull()) { value = IDBAny::create(m_value, m_blobs->getInfo(), m_primaryKey, metadata.keyPath); #if ENABLE(ASSERT) assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobs->getInfo(), m_primaryKey, metadata.keyPath); #endif } else { value = IDBAny::create(m_value, m_blobs->getInfo()); } m_valueDirty = false; ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value); return scriptValue; }
Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ScriptExecutionContext& context, IDBObjectStore& objectStore) { LOG(IndexedDB, "IDBTransaction::requestClearObjectStore"); ASSERT(isActive()); Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); uint64_t objectStoreIdentifier = objectStore.info().identifier(); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier); scheduleOperation(WTFMove(operation)); return request; }
ScriptValue IDBCursor::value(ScriptState* scriptState) { ASSERT(isCursorWithValue()); IDBObjectStore* objectStore = effectiveObjectStore(); const IDBObjectStoreMetadata& metadata = objectStore->metadata(); IDBAny* value; if (!m_value) { value = IDBAny::createUndefined(); } else if (metadata.autoIncrement && !metadata.keyPath.isNull()) { RefPtr<IDBValue> idbValue = IDBValue::create(m_value.get(), m_primaryKey, metadata.keyPath); #if ENABLE(ASSERT) assertPrimaryKeyValidOrInjectable(scriptState, idbValue.get()); #endif value = IDBAny::create(idbValue.release()); } else { value = IDBAny::create(m_value); } m_valueDirty = false; ScriptValue scriptValue = ScriptValue::from(scriptState, value); return scriptValue; }
void IDBTransaction::objectStoreDeleted(const int64_t objectStoreId, const String& name) { DCHECK_NE(m_state, Finished) << "A finished transaction deleted an object store"; DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) << "A non-versionchange transaction deleted an object store"; IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); if (it == m_objectStoreMap.end()) { // No IDBObjectStore instance was created for the deleted store in this // transaction. This happens if IDBDatabase.deleteObjectStore() is called // with the name of a store that wasn't instantated. We need to be able to // revert the metadata change if the transaction aborts, in order to return // correct values from IDB{Database, Transaction}.objectStoreNames. DCHECK(m_database->metadata().objectStores.contains(objectStoreId)); RefPtr<IDBObjectStoreMetadata> metadata = m_database->metadata().objectStores.get(objectStoreId); DCHECK(metadata.get()); DCHECK_EQ(metadata->name, name); m_deletedObjectStores.append(std::move(metadata)); } else { IDBObjectStore* objectStore = it->value; m_objectStoreMap.remove(name); objectStore->markDeleted(); if (objectStore->id() > m_oldDatabaseMetadata.maxObjectStoreId) { // The store was created and deleted in this transaction, so it will // not be restored even if the transaction aborts. We have just // removed our last reference to it. DCHECK(!m_oldStoreMetadata.contains(objectStore)); objectStore->clearIndexCache(); } else { // The store was created before this transaction, and we created an // IDBObjectStore instance for it. When that happened, we must have // snapshotted the store's metadata as well. DCHECK(m_oldStoreMetadata.contains(objectStore)); } } }
bool IDBCursor::IsSourceDeleted() const { AssertIsOnOwningThread(); MOZ_ASSERT(mTransaction); MOZ_ASSERT(mTransaction->IsOpen()); IDBObjectStore* sourceObjectStore; if (mType == Type_Index || mType == Type_IndexKey) { MOZ_ASSERT(mSourceIndex); if (mSourceIndex->IsDeleted()) { return true; } sourceObjectStore = mSourceIndex->ObjectStore(); MOZ_ASSERT(sourceObjectStore); } else { MOZ_ASSERT(mSourceObjectStore); sourceObjectStore = mSourceObjectStore; } return sourceObjectStore->IsDeleted(); }
void IDBTransaction::indexDeleted(IDBIndex* index) { DCHECK(index); DCHECK(!index->isDeleted()) << "indexDeleted called twice for the same index"; IDBObjectStore* objectStore = index->objectStore(); DCHECK_EQ(objectStore->transaction(), this); DCHECK(m_objectStoreMap.contains(objectStore->name())) << "An index was deleted without accessing its object store"; const auto& objectStoreIterator = m_oldStoreMetadata.find(objectStore); if (objectStoreIterator == m_oldStoreMetadata.end()) { // The index's object store was created in this transaction, so this // index was also created (and deleted) in this transaction, and will // not be restored if the transaction aborts. // // Subtle proof for the first sentence above: Deleting an index requires // calling deleteIndex() on the store's IDBObjectStore instance. // Whenever we create an IDBObjectStore instance for a previously // created store, we snapshot the store's metadata. So, deleting an // index of an "old" store can only be done after the store's metadata // is snapshotted. return; } const IDBObjectStoreMetadata* oldStoreMetadata = objectStoreIterator->value.get(); DCHECK(oldStoreMetadata); if (!oldStoreMetadata->indexes.contains(index->id())) { // The index's object store was created before this transaction, but the // index was created (and deleted) in this transaction, so it will not // be restored if the transaction aborts. return; } m_deletedIndexes.append(index); }
Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode) { LOG(IndexedDB, "IDBTransaction::requestPutOrAdd"); ASSERT(isActive()); ASSERT(!isReadOnly()); ASSERT(objectStore.info().autoIncrement() || key); Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this); addRequest(request.get()); auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode); scheduleOperation(WTFMove(operation)); return request; }
already_AddRefed<IDBRequest> IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); MOZ_ASSERT(!mKey.IsUnset()); MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset()); IDBObjectStore* objectStore; if (mType == Type_ObjectStore) { objectStore = mSourceObjectStore; } else { objectStore = mSourceIndex->ObjectStore(); } MOZ_ASSERT(objectStore); const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; nsRefPtr<IDBRequest> request; if (objectStore->HasValidKeyPath()) { // Make sure the object given has the correct keyPath value set on it. const KeyPath& keyPath = objectStore->GetKeyPath(); Key key; aRv = keyPath.ExtractKey(aCx, aValue, key); if (aRv.Failed()) { return nullptr; } if (key != primaryKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return nullptr; } request = objectStore->AddOrPut(aCx, aValue, /* aKey */ JS::UndefinedHandleValue, /* aOverwrite */ true, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } } else { JS::Rooted<JS::Value> keyVal(aCx); aRv = primaryKey.ToJSVal(aCx, &keyVal); if (aRv.Failed()) { return nullptr; } request = objectStore->AddOrPut(aCx, aValue, keyVal, /* aOverwrite */ true, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } } request->SetSource(this); if (mType == Type_ObjectStore) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).update(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(objectStore, primaryKey)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).update(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore), IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(objectStore, primaryKey)); } return request.forget(); }
already_AddRefed<IDBRequest> IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); MOZ_ASSERT(!mKey.IsUnset()); IDBObjectStore* objectStore; if (mType == Type_ObjectStore) { objectStore = mSourceObjectStore; } else { objectStore = mSourceIndex->ObjectStore(); } MOZ_ASSERT(objectStore); const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; JS::Rooted<JS::Value> key(aCx); aRv = primaryKey.ToJSVal(aCx, &key); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsRefPtr<IDBRequest> request = objectStore->Delete(aCx, key, aRv); if (aRv.Failed()) { return nullptr; } request->SetSource(this); #ifdef IDB_PROFILER_USE_MARKS { uint64_t requestSerial = request->GetSerialNumber(); if (mType == Type_ObjectStore) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).delete(%s)", "IDBRequest[%llu] MT IDBCursor.delete()", requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), IDB_PROFILER_STRING(objectStore), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : IDB_PROFILER_STRING(primaryKey)); } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).delete(%s)", "IDBRequest[%llu] MT IDBCursor.delete()", requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), IDB_PROFILER_STRING(objectStore), IDB_PROFILER_STRING(mSourceIndex), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : IDB_PROFILER_STRING(primaryKey)); } } #endif return request.forget(); }
already_AddRefed<IDBRequest> IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); MOZ_ASSERT(!mKey.IsUnset()); IDBObjectStore* objectStore; if (mType == Type_ObjectStore) { objectStore = mSourceObjectStore; } else { objectStore = mSourceIndex->ObjectStore(); } MOZ_ASSERT(objectStore); const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; JS::Rooted<JS::Value> key(aCx); aRv = primaryKey.ToJSVal(aCx, &key); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsRefPtr<IDBRequest> request = objectStore->DeleteInternal(aCx, key, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } request->SetSource(this); if (mType == Type_ObjectStore) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).delete(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(objectStore, primaryKey)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).delete(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore), IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(objectStore, primaryKey)); } return request.forget(); }