diff options
Diffstat (limited to 'WebCore/storage')
23 files changed, 306 insertions, 105 deletions
diff --git a/WebCore/storage/IDBCursorBackendImpl.cpp b/WebCore/storage/IDBCursorBackendImpl.cpp index 3a4dd67..13df5f0 100644 --- a/WebCore/storage/IDBCursorBackendImpl.cpp +++ b/WebCore/storage/IDBCursorBackendImpl.cpp @@ -36,6 +36,7 @@ #include "IDBKeyRange.h" #include "IDBObjectStoreBackendImpl.h" #include "IDBRequest.h" +#include "IDBTransactionBackendInterface.h" #include "SQLiteDatabase.h" #include "SQLiteStatement.h" #include "SerializedScriptValue.h" diff --git a/WebCore/storage/IDBDatabase.cpp b/WebCore/storage/IDBDatabase.cpp index 81950d6..0ea7ac2 100644 --- a/WebCore/storage/IDBDatabase.cpp +++ b/WebCore/storage/IDBDatabase.cpp @@ -55,11 +55,12 @@ PassRefPtr<IDBRequest> IDBDatabase::createObjectStore(ScriptExecutionContext* co return request; } +// FIXME: remove this method. PassRefPtr<IDBObjectStore> IDBDatabase::objectStore(const String& name, unsigned short mode) { RefPtr<IDBObjectStoreBackendInterface> objectStore = m_backend->objectStore(name, mode); ASSERT(objectStore); // FIXME: If this is null, we should raise a NOT_FOUND_ERR. - return IDBObjectStore::create(objectStore.release()); + return IDBObjectStore::create(objectStore.release(), 0); } PassRefPtr<IDBRequest> IDBDatabase::removeObjectStore(ScriptExecutionContext* context, const String& name) diff --git a/WebCore/storage/IDBDatabaseBackendImpl.cpp b/WebCore/storage/IDBDatabaseBackendImpl.cpp index 021f70a..b8fe9b5 100644 --- a/WebCore/storage/IDBDatabaseBackendImpl.cpp +++ b/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -148,11 +148,12 @@ void IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& callbacks->onSuccess(objectStore.get()); } +// FIXME: Do not expose this method via IDL. PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name, unsigned short mode) { - // FIXME: If no transaction is running, this should implicitly start one. - ASSERT_UNUSED(mode, !mode); // FIXME: Handle non-standard modes. - return m_objectStores.get(name); + ASSERT_UNUSED(mode, !mode); // FIXME: Remove the mode parameter. Transactions have modes, not object stores. + RefPtr<IDBObjectStoreBackendInterface> objectStore = m_objectStores.get(name); + return objectStore.release(); } static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) diff --git a/WebCore/storage/IDBDatabaseBackendImpl.h b/WebCore/storage/IDBDatabaseBackendImpl.h index ab055f8..7e08ad6 100644 --- a/WebCore/storage/IDBDatabaseBackendImpl.h +++ b/WebCore/storage/IDBDatabaseBackendImpl.h @@ -62,6 +62,8 @@ public: virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>); virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, unsigned long timeout); + IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); } + private: IDBDatabaseBackendImpl(const String& name, const String& description, PassOwnPtr<SQLiteDatabase> database, IDBTransactionCoordinator*); diff --git a/WebCore/storage/IDBFactoryBackendImpl.cpp b/WebCore/storage/IDBFactoryBackendImpl.cpp index d819c92..6219c2d 100644 --- a/WebCore/storage/IDBFactoryBackendImpl.cpp +++ b/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -62,9 +62,7 @@ static PassOwnPtr<SQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOri return 0; } - String databaseIdentifier = securityOrigin->databaseIdentifier(); - String santizedName = encodeForFileName(name); - path = pathByAppendingComponent(pathBase, databaseIdentifier + "_" + santizedName + ".indexeddb"); + path = pathByAppendingComponent(pathBase, IDBFactoryBackendImpl::databaseFileName(name, securityOrigin)); } OwnPtr<SQLiteDatabase> sqliteDatabase = adoptPtr(new SQLiteDatabase()); @@ -142,10 +140,11 @@ void IDBFactoryBackendImpl::open(const String& name, const String& description, m_databaseBackendMap.set(name, databaseBackend.release()); } -void IDBFactoryBackendImpl::abortPendingTransactions(const Vector<int>& pendingIDs) +String IDBFactoryBackendImpl::databaseFileName(const String& name, SecurityOrigin* securityOrigin) { - for (size_t i = 0; i < pendingIDs.size(); ++i) - m_transactionCoordinator->abort(pendingIDs.at(i)); + String databaseIdentifier = securityOrigin->databaseIdentifier(); + String santizedName = encodeForFileName(name); + return databaseIdentifier + "@" + santizedName + ".indexeddb"; } } // namespace WebCore diff --git a/WebCore/storage/IDBFactoryBackendImpl.h b/WebCore/storage/IDBFactoryBackendImpl.h index f2e1af8..76e545e 100644 --- a/WebCore/storage/IDBFactoryBackendImpl.h +++ b/WebCore/storage/IDBFactoryBackendImpl.h @@ -50,7 +50,8 @@ public: virtual ~IDBFactoryBackendImpl(); virtual void open(const String& name, const String& description, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir); - virtual void abortPendingTransactions(const Vector<int>& pendingIDs); + + static String databaseFileName(const String& name, SecurityOrigin*); private: IDBFactoryBackendImpl(); diff --git a/WebCore/storage/IDBFactoryBackendInterface.h b/WebCore/storage/IDBFactoryBackendInterface.h index e591271..4914024 100644 --- a/WebCore/storage/IDBFactoryBackendInterface.h +++ b/WebCore/storage/IDBFactoryBackendInterface.h @@ -52,7 +52,6 @@ public: virtual ~IDBFactoryBackendInterface() { } virtual void open(const String& name, const String& description, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir) = 0; - virtual void abortPendingTransactions(const Vector<int>& ids) = 0; }; } // namespace WebCore diff --git a/WebCore/storage/IDBObjectStore.cpp b/WebCore/storage/IDBObjectStore.cpp index b457cd1..4c5cf34 100644 --- a/WebCore/storage/IDBObjectStore.cpp +++ b/WebCore/storage/IDBObjectStore.cpp @@ -31,6 +31,7 @@ #include "IDBIndex.h" #include "IDBKey.h" #include "IDBKeyRange.h" +#include "IDBTransactionBackendInterface.h" #include "SerializedScriptValue.h" #include <wtf/UnusedParam.h> @@ -38,8 +39,9 @@ namespace WebCore { -IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore) +IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) : m_objectStore(idbObjectStore) + , m_transaction(transaction) { // We pass a reference to this object before it can be adopted. relaxAdoptionRequirement(); @@ -62,9 +64,9 @@ PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key) { - RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this)); - m_objectStore->get(key, request); - return request; + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->get(key, request, m_transaction.get()); + return request.release(); } PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key) @@ -97,8 +99,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::createIndex(ScriptExecutionContext* conte PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name) { + // FIXME: If this is null, we should raise a NOT_FOUND_ERR. RefPtr<IDBIndexBackendInterface> index = m_objectStore->index(name); - ASSERT(index); // FIXME: If this is null, we should raise a NOT_FOUND_ERR. + if (!index) + return 0; return IDBIndex::create(index.release()); } diff --git a/WebCore/storage/IDBObjectStore.h b/WebCore/storage/IDBObjectStore.h index 035f5d8..df5b3f7 100644 --- a/WebCore/storage/IDBObjectStore.h +++ b/WebCore/storage/IDBObjectStore.h @@ -43,13 +43,14 @@ class DOMStringList; class IDBAny; class IDBIndexRequest; class IDBKey; +class IDBTransactionBackendInterface; class SerializedScriptValue; class IDBObjectStore : public RefCounted<IDBObjectStore> { public: - static PassRefPtr<IDBObjectStore> create(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore) + static PassRefPtr<IDBObjectStore> create(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) { - return adoptRef(new IDBObjectStore(idbObjectStore)); + return adoptRef(new IDBObjectStore(idbObjectStore, transaction)); } ~IDBObjectStore() { } @@ -69,9 +70,11 @@ public: PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange> = 0, unsigned short direction = IDBCursor::NEXT); private: - IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface>); + IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface>, IDBTransactionBackendInterface* transaction); + void removeTransactionFromPendingList(); RefPtr<IDBObjectStoreBackendInterface> m_objectStore; + RefPtr<IDBTransactionBackendInterface> m_transaction; }; } // namespace WebCore diff --git a/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/WebCore/storage/IDBObjectStoreBackendImpl.cpp index e5f81ab..18d6b0c 100644 --- a/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -36,6 +36,8 @@ #include "IDBKeyPath.h" #include "IDBKeyPathBackendImpl.h" #include "IDBKeyRange.h" +#include "IDBTransactionBackendInterface.h" +#include "ScriptExecutionContext.h" #include "SQLiteDatabase.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" @@ -44,6 +46,33 @@ namespace WebCore { +template <class T, class Method, class Param1, class Param2> +class IDBTask : public ScriptExecutionContext::Task { +public: + IDBTask(T* obj, Method method, const Param1& param1, const Param2& param2) + : m_obj(obj), m_method(method), m_param1(param1), m_param2(param2) + { + } + + virtual void performTask(ScriptExecutionContext*) + { + if (m_obj) + (m_obj->*m_method)(m_param1, m_param2); + } + +private: + T* m_obj; + Method m_method; + Param1 m_param1; + Param2 m_param2; +}; + +template <class T, class Method, class Param1, class Param2> +PassOwnPtr<ScriptExecutionContext::Task> createTask(T* object, Method method, const Param1& param1, const Param2& param2) +{ + return adoptPtr(new IDBTask<T, Method, Param1, Param2>(object, method, param1, param2)); +} + IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl() { } @@ -77,7 +106,13 @@ static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) key->bind(query, 2); } -void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) +void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks, IDBTransactionBackendInterface* transaction) +{ + if (!transaction->scheduleTask(createTask(this, &IDBObjectStoreBackendImpl::getInternal, key, callbacks))) + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_ALLOWED_ERR, "Get must be called in the context of a transaction.")); +} + +void IDBObjectStoreBackendImpl::getInternal(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { SQLiteStatement query(sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key.get())); bool ok = query.prepare() == SQLResultOk; diff --git a/WebCore/storage/IDBObjectStoreBackendImpl.h b/WebCore/storage/IDBObjectStoreBackendImpl.h index 32ef920..36c0014 100644 --- a/WebCore/storage/IDBObjectStoreBackendImpl.h +++ b/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -36,6 +36,7 @@ namespace WebCore { class IDBDatabaseBackendImpl; class IDBIndexBackendImpl; +class IDBTransactionBackendInterface; class SQLiteDatabase; class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface { @@ -51,7 +52,7 @@ public: String keyPath() const { return m_keyPath; } PassRefPtr<DOMStringList> indexNames() const; - void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); + void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface* transaction); void put(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>); void remove(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); @@ -69,6 +70,8 @@ private: void loadIndexes(); SQLiteDatabase& sqliteDatabase() const; + void getInternal(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); + RefPtr<IDBDatabaseBackendImpl> m_database; int64_t m_id; diff --git a/WebCore/storage/IDBObjectStoreBackendInterface.h b/WebCore/storage/IDBObjectStoreBackendInterface.h index 200ac29..c19855c 100644 --- a/WebCore/storage/IDBObjectStoreBackendInterface.h +++ b/WebCore/storage/IDBObjectStoreBackendInterface.h @@ -38,6 +38,7 @@ class IDBCallbacks; class IDBIndexBackendInterface; class IDBKey; class IDBKeyRange; +class IDBTransactionBackendInterface; class SerializedScriptValue; class IDBObjectStoreBackendInterface : public ThreadSafeShared<IDBObjectStoreBackendInterface> { @@ -48,7 +49,7 @@ public: virtual String keyPath() const = 0; virtual PassRefPtr<DOMStringList> indexNames() const = 0; - virtual void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>) = 0; + virtual void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface* transaction) = 0; virtual void put(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>) = 0; virtual void remove(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>) = 0; diff --git a/WebCore/storage/IDBPendingTransactionMonitor.cpp b/WebCore/storage/IDBPendingTransactionMonitor.cpp index d026099..799200d 100644 --- a/WebCore/storage/IDBPendingTransactionMonitor.cpp +++ b/WebCore/storage/IDBPendingTransactionMonitor.cpp @@ -25,47 +25,48 @@ #include "config.h" #include "IDBPendingTransactionMonitor.h" +#include "IDBTransactionBackendInterface.h" #if ENABLE(INDEXED_DATABASE) namespace WebCore { -Vector<int>* IDBPendingTransactionMonitor::m_ids = 0; +Vector<IDBTransactionBackendInterface*>* IDBPendingTransactionMonitor::m_transactions = 0; -bool IDBPendingTransactionMonitor::hasPendingTransactions() +void IDBPendingTransactionMonitor::addPendingTransaction(IDBTransactionBackendInterface* transaction) { - return m_ids && m_ids->size(); + if (!m_transactions) + m_transactions = new Vector<IDBTransactionBackendInterface*>(); + m_transactions->append(transaction); } -void IDBPendingTransactionMonitor::addPendingTransaction(int id) +void IDBPendingTransactionMonitor::removePendingTransaction(IDBTransactionBackendInterface* transaction) { - if (!m_ids) - m_ids = new Vector<int>(); - m_ids->append(id); -} + if (!m_transactions) + return; -void IDBPendingTransactionMonitor::removePendingTransaction(int id) -{ - m_ids->remove(id); - if (!m_ids->size()) { - delete m_ids; - m_ids = 0; + size_t pos = m_transactions->find(transaction); + if (pos == notFound) + return; + + m_transactions->remove(pos); + + if (!m_transactions->size()) { + delete m_transactions; + m_transactions = 0; } } -void IDBPendingTransactionMonitor::clearPendingTransactions() +void IDBPendingTransactionMonitor::abortPendingTransactions() { - if (!m_ids) + if (!m_transactions) return; - m_ids->clear(); - delete m_ids; - m_ids = 0; -} + for (size_t i = 0; i < m_transactions->size(); ++i) + m_transactions->at(i)->abort(); -const Vector<int>& IDBPendingTransactionMonitor::pendingTransactions() -{ - return *m_ids; + delete m_transactions; + m_transactions = 0; } }; diff --git a/WebCore/storage/IDBPendingTransactionMonitor.h b/WebCore/storage/IDBPendingTransactionMonitor.h index 00e833a..783a731 100644 --- a/WebCore/storage/IDBPendingTransactionMonitor.h +++ b/WebCore/storage/IDBPendingTransactionMonitor.h @@ -33,27 +33,27 @@ namespace WebCore { +class IDBTransactionBackendInterface; + // This class keeps track of the transactions created during the current // Javascript execution context. A transaction is 'pending' if no asynchronous // operation is currently queued for it (e.g. an IDBObjectStore::put() or similar). // All pending transactions are aborted as soon as execution returns from // the script engine. // -// FIXME: move the vector of transaction IDs to TLS. Keeping it static +// FIXME: move the vector of transactions to TLS. Keeping it static // will not work once we add support for workers. Another possible // solution is to keep the vector in the ScriptExecutionContext. class IDBPendingTransactionMonitor : public Noncopyable { public: - static bool hasPendingTransactions(); - static void addPendingTransaction(int id); - static void removePendingTransaction(int id); - static void clearPendingTransactions(); - static const Vector<int>& pendingTransactions(); + static void addPendingTransaction(IDBTransactionBackendInterface*); + static void removePendingTransaction(IDBTransactionBackendInterface*); + static void abortPendingTransactions(); private: IDBPendingTransactionMonitor(); - static Vector<int>* m_ids; + static Vector<IDBTransactionBackendInterface*>* m_transactions; }; } // namespace WebCore diff --git a/WebCore/storage/IDBRequest.cpp b/WebCore/storage/IDBRequest.cpp index 6fbda0e..c672642 100644 --- a/WebCore/storage/IDBRequest.cpp +++ b/WebCore/storage/IDBRequest.cpp @@ -40,29 +40,38 @@ #include "IDBIndex.h" #include "IDBErrorEvent.h" #include "IDBObjectStore.h" +#include "IDBPendingTransactionMonitor.h" #include "IDBSuccessEvent.h" #include "ScriptExecutionContext.h" namespace WebCore { -IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source) +IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction) : ActiveDOMObject(context, this) , m_source(source) + , m_transaction(transaction) , m_timer(this, &IDBRequest::timerFired) - , m_aborted(false) , m_readyState(LOADING) { + if (transaction) + IDBPendingTransactionMonitor::removePendingTransaction(transaction); } IDBRequest::~IDBRequest() { - abort(); + // The transaction pointer is used to notify the transaction once the JS events were + // dispatched by this request object. If no new tasks were added by the event JS callbacks, + // the transaction can commit. Otherwise, it can continue executing the new tasks. + // It is important to guarantee that the transaction is notified after the events are + // dispatched, as the transaction cannot commit or execute new tasks in the absence + // of these notifications. We clear the transaction pointer once the events have dispatched, + // so having a non-zero pointer at IDBRequest destruction time shows that the events have not + // yet fired and there is a transaction waiting to be notified. This is an error. + ASSERT(!m_transaction); } bool IDBRequest::resetReadyState() { - if (m_aborted) - return false; ASSERT(m_readyState == DONE); m_readyState = LOADING; return true; @@ -100,7 +109,9 @@ void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend) { - scheduleEvent(IDBAny::create(IDBObjectStore::create(backend)), 0); + // FIXME: the transaction pointer should be the one of the setVersion transaction. This is because + // this callback is only executed for operations that neen to run in a setVersion transaction. + scheduleEvent(IDBAny::create(IDBObjectStore::create(backend, 0)), 0); } void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) @@ -108,15 +119,6 @@ void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptVal scheduleEvent(IDBAny::create(serializedScriptValue), 0); } -void IDBRequest::abort() -{ - m_timer.stop(); - m_aborted = true; - m_pendingEvents.clear(); - - // FIXME: This should cancel any pending work being done in the backend. -} - ScriptExecutionContext* IDBRequest::scriptExecutionContext() const { return ActiveDOMObject::scriptExecutionContext(); @@ -142,7 +144,6 @@ EventTargetData* IDBRequest::ensureEventTargetData() void IDBRequest::timerFired(Timer<IDBRequest>*) { ASSERT(m_selfRef); - ASSERT(!m_aborted); ASSERT(m_pendingEvents.size()); // We need to keep self-referencing ourself, otherwise it's possible we'll be deleted. @@ -161,6 +162,13 @@ void IDBRequest::timerFired(Timer<IDBRequest>*) dispatchEvent(IDBSuccessEvent::create(m_source, pendingEvents[i].m_result)); } } + if (m_transaction) { + // Now that we processed all pending events, let the transaction monitor check if + // it can commit the current transaction or if there's anything new pending. + // FIXME: Handle the workers case. + m_transaction->didCompleteTaskEvents(); + m_transaction.clear(); + } } void IDBRequest::scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError> error) @@ -168,9 +176,6 @@ void IDBRequest::scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabase ASSERT(m_readyState < DONE); ASSERT(!!m_selfRef == m_timer.isActive()); - if (m_aborted) - return; - PendingEvent pendingEvent; pendingEvent.m_result = result; pendingEvent.m_error = error; diff --git a/WebCore/storage/IDBRequest.h b/WebCore/storage/IDBRequest.h index 9b0ea7e..75cd9a8 100644 --- a/WebCore/storage/IDBRequest.h +++ b/WebCore/storage/IDBRequest.h @@ -42,13 +42,14 @@ namespace WebCore { +class IDBTransactionBackendInterface; + class IDBRequest : public IDBCallbacks, public EventTarget, public ActiveDOMObject { public: - static PassRefPtr<IDBRequest> create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source) { return adoptRef(new IDBRequest(context, source)); } + static PassRefPtr<IDBRequest> create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction = 0) { return adoptRef(new IDBRequest(context, source, transaction)); } virtual ~IDBRequest(); // Defined in the IDL - void abort(); enum ReadyState { LOADING = 1, DONE = 2 @@ -80,7 +81,7 @@ public: using RefCounted<IDBCallbacks>::deref; private: - IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source); + IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction); void timerFired(Timer<IDBRequest>*); void scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError>); @@ -92,6 +93,7 @@ private: virtual EventTargetData* ensureEventTargetData(); RefPtr<IDBAny> m_source; + RefPtr<IDBTransactionBackendInterface> m_transaction; struct PendingEvent { RefPtr<IDBAny> m_result; @@ -103,7 +105,6 @@ private: Timer<IDBRequest> m_timer; RefPtr<IDBRequest> m_selfRef; // This is set to us iff there's an event pending. - bool m_aborted; ReadyState m_readyState; EventTargetData m_eventTargetData; }; diff --git a/WebCore/storage/IDBRequest.idl b/WebCore/storage/IDBRequest.idl index 3036b6b..58872f0 100644 --- a/WebCore/storage/IDBRequest.idl +++ b/WebCore/storage/IDBRequest.idl @@ -32,7 +32,6 @@ module storage { Conditional=INDEXED_DATABASE, EventTarget ] IDBRequest { - void abort(); // States const unsigned short LOADING = 1; diff --git a/WebCore/storage/IDBTransaction.cpp b/WebCore/storage/IDBTransaction.cpp index 4e93378..a223b7f 100644 --- a/WebCore/storage/IDBTransaction.cpp +++ b/WebCore/storage/IDBTransaction.cpp @@ -32,6 +32,7 @@ #include "EventException.h" #include "IDBAbortEvent.h" #include "IDBDatabase.h" +#include "IDBDatabaseException.h" #include "IDBObjectStore.h" #include "IDBObjectStoreBackendInterface.h" #include "IDBPendingTransactionMonitor.h" @@ -46,7 +47,7 @@ IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTr , m_stopped(false) , m_timer(this, &IDBTransaction::timerFired) { - IDBPendingTransactionMonitor::addPendingTransaction(m_backend->id()); + IDBPendingTransactionMonitor::addPendingTransaction(m_backend.get()); } IDBTransaction::~IDBTransaction() @@ -66,7 +67,11 @@ IDBDatabase* IDBTransaction::db() PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, const ExceptionCode&) { RefPtr<IDBObjectStoreBackendInterface> objectStoreBackend = m_backend->objectStore(name); - RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend); + if (!objectStoreBackend) { + // FIXME: throw IDBDatabaseException::NOT_ALLOWED_ERR. + return 0; + } + RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend, m_backend.get()); return objectStore.release(); } diff --git a/WebCore/storage/IDBTransactionBackendImpl.cpp b/WebCore/storage/IDBTransactionBackendImpl.cpp index 51b33b2..4f18437 100644 --- a/WebCore/storage/IDBTransactionBackendImpl.cpp +++ b/WebCore/storage/IDBTransactionBackendImpl.cpp @@ -29,11 +29,12 @@ #if ENABLE(INDEXED_DATABASE) #include "IDBDatabaseBackendImpl.h" +#include "IDBTransactionCoordinator.h" #include "SQLiteDatabase.h" namespace WebCore { -PassRefPtr<IDBTransactionBackendInterface> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, int id, IDBDatabaseBackendImpl* database) +PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, int id, IDBDatabaseBackendImpl* database) { return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, timeout, id, database)); } @@ -43,26 +44,104 @@ IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores , m_mode(mode) , m_timeout(timeout) , m_id(id) - , m_aborted(false) + , m_state(NotStarted) , m_database(database) + , m_transaction(new SQLiteTransaction(database->sqliteDatabase())) + , m_timer(this, &IDBTransactionBackendImpl::timerFired) + , m_pendingEvents(0) { } PassRefPtr<IDBObjectStoreBackendInterface> IDBTransactionBackendImpl::objectStore(const String& name) { + if (isFinished()) + return 0; return m_database->objectStore(name, 0); // FIXME: remove mode param. } -void IDBTransactionBackendImpl::scheduleTask(PassOwnPtr<ScriptExecutionContext::Task>) +bool IDBTransactionBackendImpl::scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task) { - // FIXME: implement. - ASSERT_NOT_REACHED(); + if (isFinished()) + return false; + + m_taskQueue.append(task); + if (m_state == NotStarted) + start(); + + return true; } void IDBTransactionBackendImpl::abort() { - m_aborted = true; + if (isFinished()) + return; + + m_state = Finished; + m_transaction->rollback(); m_callbacks->onAbort(); + m_database->transactionCoordinator()->didFinishTransaction(this); +} + +void IDBTransactionBackendImpl::didCompleteTaskEvents() +{ + ASSERT(m_state == Started); + ASSERT(m_pendingEvents); + + m_pendingEvents--; + + if (!m_pendingEvents && m_taskQueue.isEmpty()) { + // The last task event has completed and the task + // queue is empty. Commit the transaction. + commit(); + return; + } + + // We are still waiting for other events to complete. However, + // the task queue is non-empty and the timer is inactive. + // We can therfore schedule the timer again. + if (!m_taskQueue.isEmpty() && !m_timer.isActive()) + m_timer.startOneShot(0); +} + +void IDBTransactionBackendImpl::run() +{ + ASSERT(m_state == Started); + ASSERT(!m_timer.isActive()); + + m_timer.startOneShot(0); +} + +void IDBTransactionBackendImpl::start() +{ + ASSERT(m_state == NotStarted); + + m_state = Started; + m_transaction->begin(); + m_database->transactionCoordinator()->didStartTransaction(this); +} + +void IDBTransactionBackendImpl::commit() +{ + ASSERT(m_state == Started); + + m_state = Finished; + m_transaction->commit(); + m_database->transactionCoordinator()->didFinishTransaction(this); +} + +void IDBTransactionBackendImpl::timerFired(Timer<IDBTransactionBackendImpl>*) +{ + ASSERT(!m_taskQueue.isEmpty()); + ASSERT(m_state == Started); + + TaskQueue queue; + queue.swap(m_taskQueue); + while (!queue.isEmpty()) { + OwnPtr<ScriptExecutionContext::Task> task(queue.first().release()); + queue.removeFirst(); + m_pendingEvents++; + task->performTask(0); + } } }; diff --git a/WebCore/storage/IDBTransactionBackendImpl.h b/WebCore/storage/IDBTransactionBackendImpl.h index fb57401..c037a11 100644 --- a/WebCore/storage/IDBTransactionBackendImpl.h +++ b/WebCore/storage/IDBTransactionBackendImpl.h @@ -31,6 +31,9 @@ #include "DOMStringList.h" #include "IDBTransactionBackendInterface.h" #include "IDBTransactionCallbacks.h" +#include "SQLiteTransaction.h" +#include "Timer.h" +#include <wtf/Deque.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -39,26 +42,51 @@ class IDBDatabaseBackendImpl; class IDBTransactionBackendImpl : public IDBTransactionBackendInterface { public: - static PassRefPtr<IDBTransactionBackendInterface> create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, int id, IDBDatabaseBackendImpl*); - virtual ~IDBTransactionBackendImpl() { } + static PassRefPtr<IDBTransactionBackendImpl> create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, int id, IDBDatabaseBackendImpl*); + virtual ~IDBTransactionBackendImpl() { abort(); } virtual PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name); virtual unsigned short mode() const { return m_mode; } - virtual void scheduleTask(PassOwnPtr<ScriptExecutionContext::Task>); + virtual bool scheduleTask(PassOwnPtr<ScriptExecutionContext::Task>); + virtual void didCompleteTaskEvents(); virtual void abort(); virtual int id() const { return m_id; } virtual void setCallbacks(IDBTransactionCallbacks* callbacks) { m_callbacks = callbacks; } + void run(); + bool isFinished() const { return m_state == Finished; } + private: IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, int id, IDBDatabaseBackendImpl*); + enum State { + NotStarted, + Started, + Finished, + }; + + void start(); + void commit(); + + void timerFired(Timer<IDBTransactionBackendImpl>*); + RefPtr<DOMStringList> m_objectStoreNames; unsigned short m_mode; unsigned long m_timeout; int m_id; - bool m_aborted; + + State m_state; RefPtr<IDBTransactionCallbacks> m_callbacks; RefPtr<IDBDatabaseBackendImpl> m_database; + + typedef Deque<OwnPtr<ScriptExecutionContext::Task> > TaskQueue; + TaskQueue m_taskQueue; + + OwnPtr<SQLiteTransaction> m_transaction; + + // FIXME: delete the timer once we have threads instead. + Timer<IDBTransactionBackendImpl> m_timer; + int m_pendingEvents; }; } // namespace WebCore diff --git a/WebCore/storage/IDBTransactionBackendInterface.h b/WebCore/storage/IDBTransactionBackendInterface.h index 39651f1..db95d03 100644 --- a/WebCore/storage/IDBTransactionBackendInterface.h +++ b/WebCore/storage/IDBTransactionBackendInterface.h @@ -50,7 +50,8 @@ public: virtual PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name) = 0; virtual unsigned short mode() const = 0; - virtual void scheduleTask(PassOwnPtr<ScriptExecutionContext::Task>) = 0; + virtual bool scheduleTask(PassOwnPtr<ScriptExecutionContext::Task>) = 0; + virtual void didCompleteTaskEvents() = 0; virtual void abort() = 0; virtual int id() const = 0; virtual void setCallbacks(IDBTransactionCallbacks*) = 0; diff --git a/WebCore/storage/IDBTransactionCoordinator.cpp b/WebCore/storage/IDBTransactionCoordinator.cpp index 9790c1f..0ece309 100644 --- a/WebCore/storage/IDBTransactionCoordinator.cpp +++ b/WebCore/storage/IDBTransactionCoordinator.cpp @@ -47,21 +47,45 @@ IDBTransactionCoordinator::~IDBTransactionCoordinator() PassRefPtr<IDBTransactionBackendInterface> IDBTransactionCoordinator::createTransaction(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl* database) { - RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStores, mode, timeout, ++m_nextID, database); - m_transactionQueue.add(transaction.get()); - m_idMap.add(m_nextID, transaction); + RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::create(objectStores, mode, timeout, ++m_nextID, database); + m_transactions.add(m_nextID, transaction); return transaction.release(); } -void IDBTransactionCoordinator::abort(int id) +void IDBTransactionCoordinator::didStartTransaction(IDBTransactionBackendImpl* transaction) { - ASSERT(m_idMap.contains(id)); - RefPtr<IDBTransactionBackendInterface> transaction = m_idMap.get(id); - ASSERT(transaction); - m_transactionQueue.remove(transaction.get()); - m_idMap.remove(id); - transaction->abort(); - // FIXME: this will change once we have transactions actually running. + ASSERT(m_transactions.contains(transaction->id())); + + m_startedTransactions.add(transaction); + processStartedTransactions(); +} + +void IDBTransactionCoordinator::didFinishTransaction(IDBTransactionBackendImpl* transaction) +{ + ASSERT(m_transactions.contains(transaction->id())); + + if (m_startedTransactions.contains(transaction)) { + ASSERT(!m_runningTransactions.contains(transaction)); + m_startedTransactions.remove(transaction); + } else if (m_runningTransactions.contains(transaction)) + m_runningTransactions.remove(transaction); + + m_transactions.remove(transaction->id()); + + processStartedTransactions(); +} + +void IDBTransactionCoordinator::processStartedTransactions() +{ + // FIXME: This should allocate a thread to the next transaction that's + // ready to run. For now we only have a single running transaction. + if (m_startedTransactions.isEmpty() || !m_runningTransactions.isEmpty()) + return; + + IDBTransactionBackendImpl* transaction = *m_startedTransactions.begin(); + m_startedTransactions.remove(transaction); + m_runningTransactions.add(transaction); + transaction->run(); } }; diff --git a/WebCore/storage/IDBTransactionCoordinator.h b/WebCore/storage/IDBTransactionCoordinator.h index 104a956..5e54ab4 100644 --- a/WebCore/storage/IDBTransactionCoordinator.h +++ b/WebCore/storage/IDBTransactionCoordinator.h @@ -35,6 +35,7 @@ namespace WebCore { +class IDBTransactionBackendImpl; class IDBTransactionCallbacks; class IDBDatabaseBackendImpl; @@ -55,14 +56,21 @@ public: virtual ~IDBTransactionCoordinator(); PassRefPtr<IDBTransactionBackendInterface> createTransaction(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl*); - void abort(int transactionId); + + // Called by transactions as they start and finish. + void didStartTransaction(IDBTransactionBackendImpl*); + void didFinishTransaction(IDBTransactionBackendImpl*); private: IDBTransactionCoordinator(); - ListHashSet<IDBTransactionBackendInterface*> m_transactionQueue; - typedef HashMap<int, RefPtr<IDBTransactionBackendInterface> > IdToTransactionMap; - IdToTransactionMap m_idMap; + void processStartedTransactions(); + + // This map owns all transactions known to the coordinator. + HashMap<int, RefPtr<IDBTransactionBackendImpl> > m_transactions; + // Transactions in different states are grouped below. + ListHashSet<IDBTransactionBackendImpl* > m_startedTransactions; + HashSet<IDBTransactionBackendImpl* > m_runningTransactions; int m_nextID; }; |