RefPtr<IDBObjectStore> LegacyTransaction::objectStore(const String& name, ExceptionCode& ec) { if (m_state == Finished) { ec = IDBDatabaseException::InvalidStateError; return 0; } IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); if (it != m_objectStoreMap.end()) return it->value; if (!isVersionChange() && !m_objectStoreNames.contains(name)) { ec = IDBDatabaseException::NotFoundError; return 0; } int64_t objectStoreId = m_database->findObjectStoreId(name); if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { ASSERT(isVersionChange()); ec = IDBDatabaseException::NotFoundError; return 0; } const IDBDatabaseMetadata& metadata = m_database->metadata(); RefPtr<LegacyObjectStore> objectStore = LegacyObjectStore::create(metadata.objectStores.get(objectStoreId), this); objectStoreCreated(name, objectStore); return objectStore.release(); }
IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState) { if (m_state == Finished) { 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_objectStoreNames.contains(name)) { exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); return nullptr; } int64_t objectStoreId = m_database->findObjectStoreId(name); if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { ASSERT(isVersionChange()); exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); return nullptr; } const IDBDatabaseMetadata& metadata = m_database->metadata(); IDBObjectStore* objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this); objectStoreCreated(name, objectStore); return objectStore; }
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); }
DispatchEventResult IDBTransaction::dispatchEventInternal(Event* event) { IDB_TRACE("IDBTransaction::dispatchEvent"); if (!getExecutionContext()) { m_state = Finished; return DispatchEventResult::CanceledBeforeDispatch; } DCHECK_NE(m_state, Finished); DCHECK(m_hasPendingActivity); DCHECK(getExecutionContext()); DCHECK_EQ(event->target(), this); m_state = Finished; HeapVector<Member<EventTarget>> targets; targets.append(this); targets.append(db()); // FIXME: When we allow custom event dispatching, this will probably need to // change. DCHECK(event->type() == EventTypeNames::complete || event->type() == EventTypeNames::abort); DispatchEventResult dispatchResult = IDBEventDispatcher::dispatch(event, targets); // FIXME: Try to construct a test where |this| outlives openDBRequest and we // get a crash. if (m_openDBRequest) { DCHECK(isVersionChange()); m_openDBRequest->transactionDidFinishAndDispatch(); } m_hasPendingActivity = false; return dispatchResult; }
void IDBTransaction::onAbort(DOMException* error) { IDB_TRACE("IDBTransaction::onAbort"); if (!getExecutionContext()) { finished(); return; } DCHECK_NE(m_state, Finished); if (m_state != Finishing) { // Abort was not triggered by front-end. DCHECK(error); setError(error); abortOutstandingRequests(); revertDatabaseMetadata(); m_state = Finishing; } if (isVersionChange()) m_database->close(); // Enqueue events before notifying database, as database may close which // enqueues more events and order matters. enqueueEvent(Event::createBubble(EventTypeNames::abort)); finished(); }
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 IDBTransaction::deleteIndexOnServer(TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName) { LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer"); ASSERT(isVersionChange()); serverConnection().deleteIndex(operation, objectStoreIdentifier, indexName); }
IDBError MemoryIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) { LOG(IndexedDB, "MemoryIDBBackingStore::renameIndex"); ASSERT(m_databaseInfo); auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); if (!objectStoreInfo) return { IDBDatabaseException::ConstraintError }; auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier); if (!indexInfo) return { IDBDatabaseException::ConstraintError }; auto transaction = m_transactions.get(transactionIdentifier); ASSERT(transaction); ASSERT(transaction->isVersionChange()); auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier); ASSERT(objectStore); if (!objectStore) return { IDBDatabaseException::ConstraintError }; auto* index = objectStore->indexForIdentifier(indexIdentifier); ASSERT(index); if (!index) return { IDBDatabaseException::ConstraintError }; String oldName = index->info().name(); objectStore->renameIndex(*index, newName); transaction->indexRenamed(*index, oldName); indexInfo->rename(newName); return { }; }
bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event) { IDB_TRACE("IDBTransaction::dispatchEvent"); ASSERT(m_state != Finished); ASSERT(m_hasPendingActivity); ASSERT(scriptExecutionContext()); ASSERT(event->target() == this); m_state = Finished; // Break reference cycles. for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it) it->value->transactionFinished(); m_objectStoreMap.clear(); for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it) (*it)->transactionFinished(); m_deletedObjectStores.clear(); Vector<RefPtr<EventTarget> > targets; targets.append(this); targets.append(db()); // FIXME: When we allow custom event dispatching, this will probably need to change. ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent); bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets); // FIXME: Try to construct a test where |this| outlives openDBRequest and we // get a crash. if (m_openDBRequest) { ASSERT(isVersionChange()); m_openDBRequest->transactionDidFinishAndDispatch(); } m_hasPendingActivity = false; return returnValue; }
bool IDBTransaction::dispatchEvent(Event& event) { LOG(IndexedDB, "IDBTransaction::dispatchEvent"); ASSERT(scriptExecutionContext()); ASSERT(!m_contextStopped); ASSERT(event.target() == this); ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent); Vector<RefPtr<EventTarget>> targets; targets.append(this); targets.append(db()); bool result = IDBEventDispatcher::dispatch(event, targets); if (isVersionChange()) { ASSERT(m_openDBRequest); m_openDBRequest->versionChangeTransactionDidFinish(); if (event.type() == eventNames().completeEvent) { if (m_database->isClosingOrClosed()) m_openDBRequest->fireErrorAfterVersionChangeCompletion(); else m_openDBRequest->fireSuccessAfterVersionChangeCommit(); } m_openDBRequest = nullptr; } return result; }
void IDBTransaction::objectStoreCreated(const String& name, IDBObjectStore* objectStore) { ASSERT(m_state != Finished); m_objectStoreMap.set(name, objectStore); if (isVersionChange()) m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); }
void IDBTransaction::deleteObjectStoreOnServer(TransactionOperation& operation, const String& objectStoreName) { LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer"); ASSERT(isVersionChange()); serverConnection().deleteObjectStore(operation, objectStoreName); }
bool LegacyTransaction::dispatchEvent(Event& event) { LOG(StorageAPI, "LegacyTransaction::dispatchEvent"); ASSERT(m_state != Finished); ASSERT(m_hasPendingActivity); ASSERT(scriptExecutionContext()); ASSERT(event.target() == this); m_state = Finished; // Break reference cycles. for (auto& objectStore : m_objectStoreMap) objectStore.value->transactionFinished(); m_objectStoreMap.clear(); for (auto& objectStore : m_deletedObjectStores) objectStore->transactionFinished(); m_deletedObjectStores.clear(); Vector<RefPtr<EventTarget>> targets; targets.append(this); targets.append(db()); // FIXME: When we allow custom event dispatching, this will probably need to change. ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent); bool returnValue = IDBEventDispatcher::dispatch(event, targets); // FIXME: Try to construct a test where |this| outlives openDBRequest and we // get a crash. if (m_openDBRequest) { ASSERT(isVersionChange()); m_openDBRequest->transactionDidFinishAndDispatch(); } m_hasPendingActivity = false; return returnValue; }
void LegacyTransaction::onAbort(PassRefPtr<IDBDatabaseError> prpError) { LOG(StorageAPI, "LegacyTransaction::onAbort"); RefPtr<IDBDatabaseError> error = prpError; ASSERT(m_state != Finished); if (m_state != Finishing) { ASSERT(error.get()); setError(DOMError::create(error->name()), error->message()); // Abort was not triggered by front-end, so outstanding requests must // be aborted now. while (!m_requestList.isEmpty()) { RefPtr<LegacyRequest> request = *m_requestList.begin(); m_requestList.remove(request); request->abort(); } m_state = Finishing; } if (isVersionChange()) { for (auto& objectStore : m_objectStoreCleanupMap) objectStore.key->setMetadata(objectStore.value); m_database->setMetadata(m_previousMetadata); m_database->close(); } m_objectStoreCleanupMap.clear(); closeOpenCursors(); // Enqueue events before notifying database, as database may close which enqueues more events and order matters. enqueueEvent(Event::create(eventNames().abortEvent, true, false)); m_database->transactionFinished(this); }
void IDBTransaction::createIndexOnServer(TransactionOperation& operation, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBTransaction::createIndexOnServer"); ASSERT(isVersionChange()); m_database->serverConnection().createIndex(operation, info); }
void LegacyTransaction::objectStoreCreated(const String& name, PassRefPtr<LegacyObjectStore> prpObjectStore) { ASSERT(m_state != Finished); RefPtr<LegacyObjectStore> objectStore = prpObjectStore; m_objectStoreMap.set(name, objectStore); if (isVersionChange()) m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); }
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; }
void MemoryBackingStoreTransaction::addNewIndex(MemoryIndex& index) { LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewIndex()"); ASSERT(isVersionChange()); m_versionChangeAddedIndexes.add(&index); addExistingIndex(index); }
void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName) { LOG(IndexedDB, "IDBTransaction::deleteIndex"); ASSERT(isVersionChange()); auto operation = createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName); scheduleOperation(WTFMove(operation)); }
void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore) { LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()"); ASSERT(isVersionChange()); m_versionChangeAddedObjectStores.add(&objectStore); addExistingObjectStore(objectStore); }
std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBTransaction::createIndex"); ASSERT(isVersionChange()); auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info); scheduleOperation(WTFMove(operation)); return std::make_unique<IDBIndex>(info, objectStore); }
void IDBTransaction::notifyDidAbort(const IDBError& error) { m_database->didAbortTransaction(*this); m_idbError = error; fireOnAbort(); if (isVersionChange()) { ASSERT(m_openDBRequest); m_openDBRequest->fireErrorAfterVersionChangeCompletion(); } }
void IDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName) { LOG(IndexedDB, "IDBServer::deleteIndex"); auto transaction = m_transactions.get(requestData.transactionIdentifier()); if (!transaction) return; ASSERT(transaction->isVersionChange()); transaction->deleteIndex(requestData, objectStoreIdentifier, indexName); }
DOMStringList* IDBTransaction::objectStoreNames() const { if (isVersionChange()) return m_database->objectStoreNames(); DOMStringList* objectStoreNames = DOMStringList::create(DOMStringList::IndexedDB); for (const String& objectStoreName : m_scope) objectStoreNames->append(objectStoreName); objectStoreNames->sort(); return objectStoreNames; }
void IDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBServer::createIndex"); auto transaction = m_transactions.get(requestData.transactionIdentifier()); if (!transaction) return; ASSERT(transaction->isVersionChange()); transaction->createIndex(requestData, info); }
void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore) { LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()"); ASSERT(isVersionChange()); ASSERT(!m_objectStores.contains(&objectStore)); m_objectStores.add(&objectStore); m_versionChangeAddedObjectStores.add(&objectStore); objectStore.writeTransactionStarted(*this); }
void IDBTransaction::deleteObjectStore(const String& objectStoreName) { LOG(IndexedDB, "IDBTransaction::deleteObjectStore"); ASSERT(isVersionChange()); if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) objectStore->markAsDeleted(); auto operation = createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName); scheduleOperation(WTFMove(operation)); }
Ref<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info) { LOG(IndexedDB, "IDBTransaction::createIndex"); ASSERT(isVersionChange()); Ref<IDBIndex> index = IDBIndex::create(info, objectStore); auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info); scheduleOperation(WTF::move(operation)); return WTF::move(index); }
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); } }
Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info) { LOG(IndexedDB, "IDBTransaction::createObjectStore"); ASSERT(isVersionChange()); Ref<IDBObjectStore> objectStore = IDBObjectStore::create(info, *this); m_referencedObjectStores.set(info.name(), &objectStore.get()); auto operation = createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info); scheduleOperation(WTFMove(operation)); return objectStore; }