diff options
Diffstat (limited to 'Source/WebCore/storage')
45 files changed, 618 insertions, 554 deletions
diff --git a/Source/WebCore/storage/Database.cpp b/Source/WebCore/storage/Database.cpp index 8ef780e..75f616a 100644 --- a/Source/WebCore/storage/Database.cpp +++ b/Source/WebCore/storage/Database.cpp @@ -36,7 +36,7 @@ #include "DatabaseThread.h" #include "DatabaseTracker.h" #include "Document.h" -#include "InspectorInstrumentation.h" +#include "InspectorDatabaseInstrumentation.h" #include "Logging.h" #include "NotImplemented.h" #include "Page.h" @@ -107,7 +107,7 @@ PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, con context->setHasOpenDatabases(); - InspectorInstrumentation::didOpenDatabase(context, database.get(), context->securityOrigin()->host(), name, expectedVersion); + InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion); // If it's a new database and a creation callback was provided, reset the expected // version to "" and schedule the creation callback. Because of some subtle String diff --git a/Source/WebCore/storage/IDBAbortEvent.cpp b/Source/WebCore/storage/IDBAbortEvent.cpp index 21760f8..980d656 100644 --- a/Source/WebCore/storage/IDBAbortEvent.cpp +++ b/Source/WebCore/storage/IDBAbortEvent.cpp @@ -36,13 +36,13 @@ namespace WebCore { -PassRefPtr<IDBAbortEvent> IDBAbortEvent::create() +PassRefPtr<IDBAbortEvent> IDBAbortEvent::create(PassRefPtr<IDBAny> source) { - return adoptRef(new IDBAbortEvent()); + return adoptRef(new IDBAbortEvent(source)); } -IDBAbortEvent::IDBAbortEvent() - : IDBEvent(eventNames().abortEvent, 0) // FIXME: set the source to the transaction +IDBAbortEvent::IDBAbortEvent(PassRefPtr<IDBAny> source) + : IDBEvent(eventNames().abortEvent, source, true) { } diff --git a/Source/WebCore/storage/IDBAbortEvent.h b/Source/WebCore/storage/IDBAbortEvent.h index bdc2202..fc27989 100644 --- a/Source/WebCore/storage/IDBAbortEvent.h +++ b/Source/WebCore/storage/IDBAbortEvent.h @@ -40,14 +40,14 @@ namespace WebCore { class IDBAbortEvent : public IDBEvent { public: - static PassRefPtr<IDBAbortEvent> create(); + static PassRefPtr<IDBAbortEvent> create(PassRefPtr<IDBAny> source); // FIXME: Need to allow creation of these events from JS. virtual ~IDBAbortEvent(); virtual bool isIDBAbortEvent() const { return true; } private: - IDBAbortEvent(); + IDBAbortEvent(PassRefPtr<IDBAny> source); }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBCallbacks.h b/Source/WebCore/storage/IDBCallbacks.h index e62c085..29fb1c4 100644 --- a/Source/WebCore/storage/IDBCallbacks.h +++ b/Source/WebCore/storage/IDBCallbacks.h @@ -49,7 +49,6 @@ public: virtual ~IDBCallbacks() { } virtual void onError(PassRefPtr<IDBDatabaseError>) = 0; - virtual void onSuccess() = 0; // For "null". virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>) = 0; virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>) = 0; diff --git a/Source/WebCore/storage/IDBCompleteEvent.cpp b/Source/WebCore/storage/IDBCompleteEvent.cpp index f0ad9fc..20ee57a 100644 --- a/Source/WebCore/storage/IDBCompleteEvent.cpp +++ b/Source/WebCore/storage/IDBCompleteEvent.cpp @@ -36,13 +36,13 @@ namespace WebCore { -PassRefPtr<IDBCompleteEvent> IDBCompleteEvent::create() +PassRefPtr<IDBCompleteEvent> IDBCompleteEvent::create(PassRefPtr<IDBAny> source) { - return adoptRef(new IDBCompleteEvent()); + return adoptRef(new IDBCompleteEvent(source)); } -IDBCompleteEvent::IDBCompleteEvent() - : IDBEvent(eventNames().completeEvent, 0) // FIXME: set the source to the transaction +IDBCompleteEvent::IDBCompleteEvent(PassRefPtr<IDBAny> source) + : IDBEvent(eventNames().completeEvent, source, false) { } diff --git a/Source/WebCore/storage/IDBCompleteEvent.h b/Source/WebCore/storage/IDBCompleteEvent.h index c407096..c004a72 100644 --- a/Source/WebCore/storage/IDBCompleteEvent.h +++ b/Source/WebCore/storage/IDBCompleteEvent.h @@ -40,14 +40,14 @@ namespace WebCore { class IDBCompleteEvent : public IDBEvent { public: - static PassRefPtr<IDBCompleteEvent> create(); + static PassRefPtr<IDBCompleteEvent> create(PassRefPtr<IDBAny> source); // FIXME: Need to allow creation of these events from JS. virtual ~IDBCompleteEvent(); virtual bool isIDBCompleteEvent() const { return true; } private: - IDBCompleteEvent(); + IDBCompleteEvent(PassRefPtr<IDBAny> source); }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBCursor.cpp b/Source/WebCore/storage/IDBCursor.cpp index 444c109..531cae1 100644 --- a/Source/WebCore/storage/IDBCursor.cpp +++ b/Source/WebCore/storage/IDBCursor.cpp @@ -33,13 +33,13 @@ #include "IDBCursorBackendInterface.h" #include "IDBKey.h" #include "IDBRequest.h" -#include "IDBTransactionBackendInterface.h" +#include "IDBTransaction.h" #include "ScriptExecutionContext.h" #include "SerializedScriptValue.h" namespace WebCore { -IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransactionBackendInterface* transaction) +IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction) : m_backend(backend) , m_request(request) , m_transaction(transaction) @@ -84,7 +84,7 @@ void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec) if (m_request->resetReadyState(m_transaction.get())) m_backend->continueFunction(key, m_request, ec); else - ASSERT_NOT_REACHED(); + ec = IDBDatabaseException::NOT_ALLOWED_ERR; } PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec) diff --git a/Source/WebCore/storage/IDBCursor.h b/Source/WebCore/storage/IDBCursor.h index 54bf51a..9f5ffad 100644 --- a/Source/WebCore/storage/IDBCursor.h +++ b/Source/WebCore/storage/IDBCursor.h @@ -40,9 +40,9 @@ class IDBCallbacks; class IDBCursorBackendInterface; class IDBKey; class IDBRequest; +class IDBTransaction; class ScriptExecutionContext; class SerializedScriptValue; -class IDBTransactionBackendInterface; class IDBCursor : public RefCounted<IDBCursor> { public: @@ -52,7 +52,7 @@ public: PREV = 2, PREV_NO_DUPLICATE = 3, }; - static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransactionBackendInterface* transaction) + static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction) { return adoptRef(new IDBCursor(backend, request, transaction)); } @@ -70,11 +70,11 @@ public: PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&); private: - explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBTransactionBackendInterface*); + explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBTransaction*); RefPtr<IDBCursorBackendInterface> m_backend; RefPtr<IDBRequest> m_request; - RefPtr<IDBTransactionBackendInterface> m_transaction; + RefPtr<IDBTransaction> m_transaction; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBCursor.idl b/Source/WebCore/storage/IDBCursor.idl index 12d0baf..2e1459f 100644 --- a/Source/WebCore/storage/IDBCursor.idl +++ b/Source/WebCore/storage/IDBCursor.idl @@ -37,9 +37,8 @@ module storage { readonly attribute IDBKey key; readonly attribute IDBAny value; - // FIXME: Implement. - //[CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value) - // raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value) + raises (IDBDatabaseException); [ImplementationFunction=continueFunction] void continue(in [Optional] IDBKey key) raises (IDBDatabaseException); [CallWith=ScriptExecutionContext, ImplementationFunction=deleteFunction] IDBRequest delete() diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.cpp b/Source/WebCore/storage/IDBCursorBackendImpl.cpp index d75e28d..089bb67 100644 --- a/Source/WebCore/storage/IDBCursorBackendImpl.cpp +++ b/Source/WebCore/storage/IDBCursorBackendImpl.cpp @@ -68,7 +68,6 @@ unsigned short IDBCursorBackendImpl::direction() const PassRefPtr<IDBKey> IDBCursorBackendImpl::key() const { - return m_currentKey; } @@ -79,43 +78,15 @@ PassRefPtr<IDBAny> IDBCursorBackendImpl::value() const return IDBAny::create(m_currentIDBKeyValue.get()); } -void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBCallbacks> callbacks, ExceptionCode& ec) { - RefPtr<IDBCursorBackendImpl> cursor = this; - RefPtr<SerializedScriptValue> value = prpValue; - RefPtr<IDBCallbacks> callbacks = prpCallbacks; - // FIXME: Throw DATA_ERR and SERIAL_ERR when appropriate. - if (!m_transaction->scheduleTask(createCallbackTask(&IDBCursorBackendImpl::updateInternal, cursor, value, callbacks))) + if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) { ec = IDBDatabaseException::NOT_ALLOWED_ERR; -} - -void IDBCursorBackendImpl::updateInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> cursor, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBCallbacks> callbacks) -{ - // FIXME: This method doesn't update indexes. It's dangerous to call in its current state. - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Not implemented.")); - return; - - RefPtr<SerializedScriptValue> value = prpValue; - - if (!cursor->m_query || cursor->m_currentId == InvalidId) { - // FIXME: Use the proper error code when it's specced. - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Operation not possible.")); return; } - String sql = "UPDATE ObjectStoreData SET value = ? WHERE id = ?"; - SQLiteStatement updateQuery(cursor->database(), sql); - - bool ok = updateQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - updateQuery.bindText(1, value->toWireString()); - updateQuery.bindInt64(2, cursor->m_currentId); - ok = updateQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - - if (cursor->m_isSerializedScriptValueCursor) - cursor->m_currentSerializedScriptValue = value.release(); - callbacks->onSuccess(); + RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey; + m_objectStore->put(value, key.release(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec); } void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) @@ -127,6 +98,20 @@ void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPt ec = IDBDatabaseException::NOT_ALLOWED_ERR; } +bool IDBCursorBackendImpl::currentRowExists() +{ + String sql = m_currentIDBKeyValue ? "SELECT id FROM IndexData WHERE id = ?" : "SELECT id FROM ObjectStoreData WHERE id = ?"; + SQLiteStatement statement(m_database->db(), sql); + + bool ok = statement.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); + + statement.bindInt64(1, m_currentId); + return statement.step() == SQLResultRow; +} + +// IMPORTANT: If this ever 1) fires an 'error' event and 2) it's possible to fire another event afterwards, +// IDBRequest::hasPendingActivity() will need to be modified to handle this!!! void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> prpCursor, PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> callbacks) { RefPtr<IDBCursorBackendImpl> cursor = prpCursor; @@ -138,13 +123,17 @@ void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, Pas cursor->m_currentKey = 0; cursor->m_currentSerializedScriptValue = 0; cursor->m_currentIDBKeyValue = 0; - callbacks->onSuccess(); + callbacks->onSuccess(SerializedScriptValue::nullValue()); return; } RefPtr<IDBKey> oldKey = cursor->m_currentKey; cursor->loadCurrentRow(); + // Skip if this entry has been deleted from the object store. + if (!cursor->currentRowExists()) + continue; + // If a key was supplied, we must loop until we find that key (or hit the end). if (key && !key->isEqual(cursor->m_currentKey.get())) continue; @@ -178,7 +167,7 @@ void IDBCursorBackendImpl::loadCurrentRow() m_currentId = m_query->getColumnInt64(0); m_currentKey = IDBKey::fromQuery(*m_query, 1); if (m_isSerializedScriptValueCursor) - m_currentSerializedScriptValue = SerializedScriptValue::createFromWire(m_query->getColumnText(4)); + m_currentSerializedScriptValue = SerializedScriptValue::createFromWire(m_query->getColumnBlobAsString(4)); m_currentIDBKeyValue = IDBKey::fromQuery(*m_query, 5); } diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.h b/Source/WebCore/storage/IDBCursorBackendImpl.h index e3a8995..f459139 100644 --- a/Source/WebCore/storage/IDBCursorBackendImpl.h +++ b/Source/WebCore/storage/IDBCursorBackendImpl.h @@ -65,10 +65,10 @@ public: private: IDBCursorBackendImpl(IDBSQLiteDatabase*, PassRefPtr<IDBKeyRange>, IDBCursor::Direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*); + bool currentRowExists(); void loadCurrentRow(); SQLiteDatabase& database() const; - static void updateInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>); static void continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>); static const int64_t InvalidId = -1; @@ -80,11 +80,15 @@ private: OwnPtr<SQLiteStatement> m_query; bool m_isSerializedScriptValueCursor; int64_t m_currentId; + + // The key in the objectStore or index that this cursor iterates over. RefPtr<IDBKey> m_currentKey; // m_isSerializedScriptValueCursor will only be available for object cursors. RefPtr<SerializedScriptValue> m_currentSerializedScriptValue; + // FIXME: make the primary key available via script for all types of cursors. + // For cursors on indices, this is the key in the objectstore that corresponds to the current entry in the index. RefPtr<IDBKey> m_currentIDBKeyValue; RefPtr<IDBTransactionBackendInterface> m_transaction; diff --git a/Source/WebCore/storage/IDBDatabase.cpp b/Source/WebCore/storage/IDBDatabase.cpp index 33f004b..9a5eb6c 100644 --- a/Source/WebCore/storage/IDBDatabase.cpp +++ b/Source/WebCore/storage/IDBDatabase.cpp @@ -41,11 +41,16 @@ namespace WebCore { -// FIXME: We need to spec this differently. -const unsigned long defaultTimeout = 0; // Infinite. +PassRefPtr<IDBDatabase> IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackendInterface> database) +{ + return adoptRef(new IDBDatabase(context, database)); +} -IDBDatabase::IDBDatabase(PassRefPtr<IDBDatabaseBackendInterface> backend) - : m_backend(backend) +IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackendInterface> backend) + : ActiveDOMObject(context, this) + , m_backend(backend) + , m_noNewTransactions(false) + , m_stopped(false) { // We pass a reference of this object before it can be adopted. relaxAdoptionRequirement(); @@ -53,9 +58,10 @@ IDBDatabase::IDBDatabase(PassRefPtr<IDBDatabaseBackendInterface> backend) IDBDatabase::~IDBDatabase() { + ASSERT(m_stopped); } -void IDBDatabase::setSetVersionTransaction(IDBTransactionBackendInterface* transaction) +void IDBDatabase::setSetVersionTransaction(IDBTransaction* transaction) { m_setVersionTransaction = transaction; } @@ -73,7 +79,7 @@ PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, co options.getKeyBool("autoIncrement", autoIncrement); // FIXME: Look up evictable and pass that on as well. - RefPtr<IDBObjectStoreBackendInterface> objectStore = m_backend->createObjectStore(name, keyPath, autoIncrement, m_setVersionTransaction.get(), ec); + RefPtr<IDBObjectStoreBackendInterface> objectStore = m_backend->createObjectStore(name, keyPath, autoIncrement, m_setVersionTransaction->backend(), ec); if (!objectStore) { ASSERT(ec); return 0; @@ -88,7 +94,7 @@ void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec) return; } - m_backend->deleteObjectStore(name, m_setVersionTransaction.get(), ec); + m_backend->deleteObjectStore(name, m_setVersionTransaction->backend(), ec); } PassRefPtr<IDBRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec) @@ -98,38 +104,27 @@ PassRefPtr<IDBRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, return request; } -PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, PassRefPtr<DOMStringList> prpStoreNames, unsigned short mode, ExceptionCode& ec) { - RefPtr<DOMStringList> storeNames = options.getKeyDOMStringList("objectStoreNames"); - if (!storeNames) { + RefPtr<DOMStringList> storeNames = prpStoreNames; + if (!storeNames) storeNames = DOMStringList::create(); - String storeName; - if (options.getKeyString("objectStoreNames", storeName)) - storeNames->append(storeName); - } - // Gets cast to an unsigned short. - int32_t mode = IDBTransaction::READ_ONLY; - options.getKeyInt32("mode", mode); if (mode != IDBTransaction::READ_WRITE && mode != IDBTransaction::READ_ONLY) { // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 ec = IDBDatabaseException::CONSTRAINT_ERR; return 0; } - - // Gets cast to an unsigned long. - // FIXME: The spec needs to be updated on this. It should probably take a double. - int32_t timeout = defaultTimeout; - options.getKeyInt32("timeout", timeout); - int64_t unsignedLongMax = std::numeric_limits<unsigned long>::max(); - if (timeout < 0 || timeout > unsignedLongMax) - timeout = defaultTimeout; // Ignore illegal values. + if (m_noNewTransactions) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } // We need to create a new transaction synchronously. Locks are acquired asynchronously. Operations // can be queued against the transaction at any point. They will start executing as soon as the // appropriate locks have been acquired. // Also note that each backend object corresponds to exactly one IDBTransaction object. - RefPtr<IDBTransactionBackendInterface> transactionBackend = m_backend->transaction(storeNames.get(), mode, timeout, ec); + RefPtr<IDBTransactionBackendInterface> transactionBackend = m_backend->transaction(storeNames.get(), mode, ec); if (!transactionBackend) { ASSERT(ec); return 0; @@ -141,7 +136,40 @@ PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* cont void IDBDatabase::close() { - m_backend->close(); + m_noNewTransactions = true; +} + +bool IDBDatabase::hasPendingActivity() const +{ + // FIXME: Try to find some way not to just leak this object until page navigation. + // FIXME: In an ideal world, we should return true as long as anyone has or can + // get a handle to us or any derivative transaction/request object and any + // of those have event listeners. This is in order to handle user generated + // events properly. + return !m_stopped || ActiveDOMObject::hasPendingActivity(); +} + +void IDBDatabase::stop() +{ + // Stop fires at a deterministic time, so we need to call close in it. + close(); + + m_stopped = true; +} + +ScriptExecutionContext* IDBDatabase::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); +} + +EventTargetData* IDBDatabase::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* IDBDatabase::ensureEventTargetData() +{ + return &m_eventTargetData; } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBDatabase.h b/Source/WebCore/storage/IDBDatabase.h index 9ebbf00..f90ddd3 100644 --- a/Source/WebCore/storage/IDBDatabase.h +++ b/Source/WebCore/storage/IDBDatabase.h @@ -26,7 +26,10 @@ #ifndef IDBDatabase_h #define IDBDatabase_h +#include "ActiveDOMObject.h" #include "DOMStringList.h" +#include "Event.h" +#include "EventTarget.h" #include "ExceptionCode.h" #include "IDBDatabaseBackendInterface.h" #include "IDBObjectStore.h" @@ -42,17 +45,13 @@ namespace WebCore { class IDBAny; class IDBRequest; -class ScriptExecutionContext; -class IDBDatabase : public RefCounted<IDBDatabase> { +class IDBDatabase : public RefCounted<IDBDatabase>, public EventTarget, public ActiveDOMObject { public: - static PassRefPtr<IDBDatabase> create(PassRefPtr<IDBDatabaseBackendInterface> database) - { - return adoptRef(new IDBDatabase(database)); - } + static PassRefPtr<IDBDatabase> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>); ~IDBDatabase(); - void setSetVersionTransaction(IDBTransactionBackendInterface*); + void setSetVersionTransaction(IDBTransaction*); // Implement the IDL String name() const { return m_backend->name(); } @@ -61,19 +60,45 @@ public: // FIXME: Try to modify the code generator so this is unneeded. PassRefPtr<IDBObjectStore> createObjectStore(const String& name, ExceptionCode& ec) { return createObjectStore(name, OptionsObject(), ec); } - PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, ExceptionCode& ec) { return transaction(context, OptionsObject(), ec); } + PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, ExceptionCode& ec) { return transaction(context, 0, ec); } + PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, PassRefPtr<DOMStringList> storeNames, ExceptionCode& ec) { return transaction(context, storeNames, IDBTransaction::READ_ONLY, ec); } + PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, PassRefPtr<DOMStringList>, unsigned short mode, ExceptionCode&); PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const OptionsObject&, ExceptionCode&); void deleteObjectStore(const String& name, ExceptionCode&); PassRefPtr<IDBRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&); - PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); void close(); + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + + // ActiveDOMObject + virtual bool hasPendingActivity() const; + virtual void stop(); + + // EventTarget + virtual IDBDatabase* toIDBDatabase() { return this; } + virtual ScriptExecutionContext* scriptExecutionContext() const; + + using RefCounted<IDBDatabase>::ref; + using RefCounted<IDBDatabase>::deref; + private: - IDBDatabase(PassRefPtr<IDBDatabaseBackendInterface>); + IDBDatabase(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>); + + // EventTarget + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); RefPtr<IDBDatabaseBackendInterface> m_backend; - RefPtr<IDBTransactionBackendInterface> m_setVersionTransaction; + RefPtr<IDBTransaction> m_setVersionTransaction; + + bool m_noNewTransactions; + bool m_stopped; + + EventTargetData m_eventTargetData; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBDatabase.idl b/Source/WebCore/storage/IDBDatabase.idl index 7eb43e9..3922144 100644 --- a/Source/WebCore/storage/IDBDatabase.idl +++ b/Source/WebCore/storage/IDBDatabase.idl @@ -26,22 +26,35 @@ module storage { interface [ - Conditional=INDEXED_DATABASE + Conditional=INDEXED_DATABASE, + EventTarget ] IDBDatabase { readonly attribute DOMString name; readonly attribute DOMString version; readonly attribute DOMStringList objectStoreNames; + attribute EventListener onabort; + attribute EventListener onerror; + IDBObjectStore createObjectStore(in DOMString name, in [Optional] OptionsObject options) raises (IDBDatabaseException); void deleteObjectStore(in DOMString name) raises (IDBDatabaseException); [CallWith=ScriptExecutionContext] IDBRequest setVersion(in DOMString version) raises (IDBDatabaseException); - [CallWith=ScriptExecutionContext] IDBTransaction transaction(in [Optional] OptionsObject optionsObject) + [CallWith=ScriptExecutionContext] IDBTransaction transaction(in [Optional] DOMStringList storeNames, in [Optional] unsigned short mode) raises (IDBDatabaseException); - // FIXME: Implement. - //void close(); + void close(); + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); }; } diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp index fa9a336..133c5ae 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -214,7 +214,7 @@ void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCal RefPtr<IDBDatabaseBackendImpl> database = this; RefPtr<IDBCallbacks> callbacks = prpCallbacks; RefPtr<DOMStringList> objectStoreNames = DOMStringList::create(); - RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, 0, this); + RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this); if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction), createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) { ec = IDBDatabaseException::NOT_ALLOWED_ERR; @@ -234,7 +234,7 @@ void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRef callbacks->onSuccess(transaction); } -PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, unsigned long timeout, ExceptionCode& ec) +PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec) { for (size_t i = 0; i < objectStoreNames->length(); ++i) { if (!m_objectStores.contains(objectStoreNames->item(i))) { @@ -244,7 +244,7 @@ PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(D } // FIXME: Return not allowed err if close has been called. - return IDBTransactionBackendImpl::create(objectStoreNames, mode, timeout, this); + return IDBTransactionBackendImpl::create(objectStoreNames, mode, this); } void IDBDatabaseBackendImpl::close() diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.h b/Source/WebCore/storage/IDBDatabaseBackendImpl.h index 570f6a5..8942968 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendImpl.h +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.h @@ -61,7 +61,7 @@ public: virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&); virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&); virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&); - virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* objectStoreNames, unsigned short mode, unsigned long timeout, ExceptionCode&); + virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode&); virtual void close(); PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name); diff --git a/Source/WebCore/storage/IDBDatabaseBackendInterface.h b/Source/WebCore/storage/IDBDatabaseBackendInterface.h index 0dc59b1..9cc7230 100644 --- a/Source/WebCore/storage/IDBDatabaseBackendInterface.h +++ b/Source/WebCore/storage/IDBDatabaseBackendInterface.h @@ -57,7 +57,7 @@ public: virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&) = 0; virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0; virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; - virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, unsigned long timeout, ExceptionCode&) = 0; + virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, ExceptionCode&) = 0; virtual void close() = 0; }; diff --git a/Source/WebCore/storage/IDBErrorEvent.cpp b/Source/WebCore/storage/IDBErrorEvent.cpp index cba980d..e576fa8 100644 --- a/Source/WebCore/storage/IDBErrorEvent.cpp +++ b/Source/WebCore/storage/IDBErrorEvent.cpp @@ -43,7 +43,7 @@ PassRefPtr<IDBErrorEvent> IDBErrorEvent::create(PassRefPtr<IDBAny> source, const } IDBErrorEvent::IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError& error) - : IDBEvent(eventNames().errorEvent, source) + : IDBEvent(eventNames().errorEvent, source, true) , m_code(error.code()) , m_message(error.message()) { diff --git a/Source/WebCore/storage/IDBEvent.cpp b/Source/WebCore/storage/IDBEvent.cpp index f9f6060..a7f3db1 100644 --- a/Source/WebCore/storage/IDBEvent.cpp +++ b/Source/WebCore/storage/IDBEvent.cpp @@ -35,8 +35,8 @@ namespace WebCore { -IDBEvent::IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source) - : Event(type, false, false) +IDBEvent::IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble) + : Event(type, canBubble, true) , m_source(source) { } @@ -50,6 +50,57 @@ PassRefPtr<IDBAny> IDBEvent::source() return m_source; } +bool IDBEvent::dispatch(Vector<RefPtr<EventTarget> >& eventTargets) +{ + size_t size = eventTargets.size(); + ASSERT(size); + + setEventPhase(Event::CAPTURING_PHASE); + for (size_t i = size - 1; i; --i) { // Don't do the first element. + setCurrentTarget(eventTargets[i].get()); + eventTargets[i]->fireEventListeners(this); + if (propagationStopped()) + goto doneDispatching; + } + + setEventPhase(Event::AT_TARGET); + setCurrentTarget(eventTargets[0].get()); + eventTargets[0]->fireEventListeners(this); + if (propagationStopped() || !bubbles() || cancelBubble()) + goto doneDispatching; + + setEventPhase(Event::BUBBLING_PHASE); + for (size_t i = 1; i < size; ++i) { // Don't do the first element. + setCurrentTarget(eventTargets[i].get()); + eventTargets[i]->fireEventListeners(this); + if (propagationStopped() || cancelBubble()) + goto doneDispatching; + } + + // FIXME: "...However, we also wanted to integrate the window.onerror feature in + // HTML5. So after we've fired an "error" event, if .preventDefault() was + // never called on the event, we fire an error event on the window (can't + // remember if this happens before or after we abort the transaction). + // This is a separate event, which for example means that even if you + // attach a capturing "error" handler on window, you won't see any events + // unless an error really went unhandled. And you also can't call + // .preventDefault on the error event fired on the window in order to + // prevent the transaction from being aborted. It's purely there for + // error reporting and distinctly different from the event propagating to + // the window. + // + // This is similar to how "error" events are handled in workers. + // + // (I think that so far webkit hasn't implemented the window.onerror + // feature yet, so you probably don't want to fire the separate error + // event on the window until that has been implemented)." + +doneDispatching: + setCurrentTarget(0); + setEventPhase(0); + return !defaultPrevented(); +} + } // namespace WebCore #endif diff --git a/Source/WebCore/storage/IDBEvent.h b/Source/WebCore/storage/IDBEvent.h index c44e449..d28885b 100644 --- a/Source/WebCore/storage/IDBEvent.h +++ b/Source/WebCore/storage/IDBEvent.h @@ -32,8 +32,10 @@ #if ENABLE(INDEXED_DATABASE) #include "Event.h" +#include "EventTarget.h" #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> +#include <wtf/Vector.h> namespace WebCore { @@ -44,9 +46,10 @@ public: virtual ~IDBEvent(); PassRefPtr<IDBAny> source(); + bool dispatch(Vector<RefPtr<EventTarget> >&); // The target first and then its ancestors in order of how the event bubbles. protected: - IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source); + IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble); private: RefPtr<IDBAny> m_source; diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp index 45cffeb..0e883cf 100644 --- a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -49,7 +49,7 @@ IDBFactoryBackendImpl::IDBFactoryBackendImpl() : m_transactionCoordinator(IDBTransactionCoordinator::create()) { } - + IDBFactoryBackendImpl::~IDBFactoryBackendImpl() { } @@ -93,11 +93,24 @@ static PassRefPtr<IDBSQLiteDatabase> openSQLiteDatabase(SecurityOrigin* security return sqliteDatabase.release(); } +static bool runCommands(SQLiteDatabase& sqliteDatabase, const char** commands, size_t numberOfCommands) +{ + SQLiteTransaction transaction(sqliteDatabase, false); + transaction.begin(); + for (size_t i = 0; i < numberOfCommands; ++i) { + if (!sqliteDatabase.executeCommand(commands[i])) { + LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); + return false; + } + } + transaction.commit(); + return true; +} + static bool createTables(SQLiteDatabase& sqliteDatabase) { if (sqliteDatabase.tableExists("Databases")) return true; - static const char* commands[] = { "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", "CREATE UNIQUE INDEX Databases_name ON Databases(name)", @@ -117,17 +130,7 @@ static bool createTables(SQLiteDatabase& sqliteDatabase) "CREATE INDEX IndexData_indexId ON IndexData(indexId)", }; - SQLiteTransaction transaction(sqliteDatabase, false); - transaction.begin(); - for (size_t i = 0; i < arraysize(commands); ++i) { - if (!sqliteDatabase.executeCommand(commands[i])) { - // FIXME: We should try to recover from this situation. Maybe nuke the database and start over? - LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); - return false; - } - } - transaction.commit(); - return true; + return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); } static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) @@ -137,14 +140,7 @@ static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) "INSERT INTO MetaData VALUES ('version', 1)", }; - SQLiteTransaction transaction(sqliteDatabase, false); - transaction.begin(); - for (size_t i = 0; i < arraysize(commands); ++i) { - if (!sqliteDatabase.executeCommand(commands[i])) - return false; - } - transaction.commit(); - return true; + return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])); } static bool getDatabaseVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion) @@ -187,19 +183,27 @@ static bool migrateDatabase(SQLiteDatabase& sqliteDatabase) "UPDATE MetaData SET value = 2 WHERE name = 'version'", }; - SQLiteTransaction transaction(sqliteDatabase, false); - transaction.begin(); - for (size_t i = 0; i < arraysize(commands); ++i) { - if (!sqliteDatabase.executeCommand(commands[i])) { - LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); - return false; - } - } - transaction.commit(); + if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) + return false; databaseVersion = 2; } + if (databaseVersion == 2) { + // We need to make the ObjectStoreData.value be a BLOB instead of TEXT. + static const char* commands[] = { + "DROP TABLE IF EXISTS ObjectStoreData", // This drops associated indices. + "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value BLOB NOT NULL)", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + "UPDATE MetaData SET value = 3 WHERE name = 'version'", + }; + + if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]))) + return false; + + databaseVersion = 3; + } + return true; } diff --git a/Source/WebCore/storage/IDBFactoryBackendInterface.cpp b/Source/WebCore/storage/IDBFactoryBackendInterface.cpp index 935ccac..ac13652 100644 --- a/Source/WebCore/storage/IDBFactoryBackendInterface.cpp +++ b/Source/WebCore/storage/IDBFactoryBackendInterface.cpp @@ -43,10 +43,6 @@ PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create() return IDBFactoryBackendImpl::create(); } -IDBFactoryBackendInterface::~IDBFactoryBackendInterface() -{ -} - } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBFactoryBackendInterface.h b/Source/WebCore/storage/IDBFactoryBackendInterface.h index 166d517..e05f316 100644 --- a/Source/WebCore/storage/IDBFactoryBackendInterface.h +++ b/Source/WebCore/storage/IDBFactoryBackendInterface.h @@ -49,7 +49,7 @@ class SecurityOrigin; class IDBFactoryBackendInterface : public ThreadSafeShared<IDBFactoryBackendInterface> { public: static PassRefPtr<IDBFactoryBackendInterface> create(); - virtual ~IDBFactoryBackendInterface(); + virtual ~IDBFactoryBackendInterface() { } virtual void open(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir, int64_t maximumSize) = 0; }; diff --git a/Source/WebCore/storage/IDBIndex.cpp b/Source/WebCore/storage/IDBIndex.cpp index 8a38a27..615767b 100644 --- a/Source/WebCore/storage/IDBIndex.cpp +++ b/Source/WebCore/storage/IDBIndex.cpp @@ -34,13 +34,13 @@ #include "IDBKey.h" #include "IDBKeyRange.h" #include "IDBRequest.h" -#include "IDBTransactionBackendInterface.h" +#include "IDBTransaction.h" namespace WebCore { static const unsigned short defaultDirection = IDBCursor::NEXT; -IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransactionBackendInterface* transaction) +IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction) : m_backend(backend) , m_transaction(transaction) { @@ -52,13 +52,8 @@ IDBIndex::~IDBIndex() { } -PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, unsigned short direction, ExceptionCode& ec) { - RefPtr<IDBKeyRange> keyRange = options.getKeyKeyRange("range"); - - // Converted to an unsigned short. - int32_t direction = defaultDirection; - options.getKeyInt32("direction", direction); if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 ec = IDBDatabaseException::CONSTRAINT_ERR; @@ -66,19 +61,14 @@ PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, con } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->openCursor(keyRange, direction, request, m_transaction.get(), ec); + m_backend->openCursor(keyRange, direction, request, m_transaction->backend(), ec); if (ec) return 0; return request; } -PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, unsigned short direction, ExceptionCode& ec) { - RefPtr<IDBKeyRange> keyRange = options.getKeyKeyRange("range"); - - // Converted to an unsigned short. - int32_t direction = defaultDirection; - options.getKeyInt32("direction", direction); if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 ec = IDBDatabaseException::CONSTRAINT_ERR; @@ -86,7 +76,7 @@ PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->openKeyCursor(keyRange, direction, request, m_transaction.get(), ec); + m_backend->openKeyCursor(keyRange, direction, request, m_transaction->backend(), ec); if (ec) return 0; return request; @@ -95,7 +85,7 @@ PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->get(key, request, m_transaction.get(), ec); + m_backend->get(key, request, m_transaction->backend(), ec); if (ec) return 0; return request; @@ -104,7 +94,7 @@ PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_backend->getKey(key, request, m_transaction.get(), ec); + m_backend->getKey(key, request, m_transaction->backend(), ec); if (ec) return 0; return request; diff --git a/Source/WebCore/storage/IDBIndex.h b/Source/WebCore/storage/IDBIndex.h index 7f1aae3..10b46f1 100644 --- a/Source/WebCore/storage/IDBIndex.h +++ b/Source/WebCore/storage/IDBIndex.h @@ -30,7 +30,6 @@ #include "IDBIndexBackendInterface.h" #include "IDBKeyRange.h" #include "IDBRequest.h" -#include "OptionsObject.h" #include "PlatformString.h" #include <wtf/Forward.h> @@ -40,7 +39,7 @@ namespace WebCore { class IDBIndex : public RefCounted<IDBIndex> { public: - static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransactionBackendInterface* transaction) + static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction) { return adoptRef(new IDBIndex(backend, transaction)); } @@ -53,19 +52,22 @@ public: bool unique() const { return m_backend->unique(); } // FIXME: Try to modify the code generator so this is unneeded. - PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, OptionsObject(), ec); } - PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, OptionsObject(), ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, 0, ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openCursor(context, keyRange, IDBCursor::NEXT, ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, unsigned short direction, ExceptionCode&); + + PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, 0, ec); } + PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openKeyCursor(context, keyRange, IDBCursor::NEXT, ec); } + PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, unsigned short direction, ExceptionCode&); - PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); - PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); private: - IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBTransactionBackendInterface* transaction); + IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBTransaction*); RefPtr<IDBIndexBackendInterface> m_backend; - RefPtr<IDBTransactionBackendInterface> m_transaction; + RefPtr<IDBTransaction> m_transaction; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBIndex.idl b/Source/WebCore/storage/IDBIndex.idl index e6ba1e6..13089cc 100644 --- a/Source/WebCore/storage/IDBIndex.idl +++ b/Source/WebCore/storage/IDBIndex.idl @@ -33,9 +33,9 @@ module storage { readonly attribute DOMString keyPath; readonly attribute boolean unique; - [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] OptionsObject options) + [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] IDBKeyRange range, in [Optional] unsigned short direction) raises (IDBDatabaseException); - [CallWith=ScriptExecutionContext] IDBRequest openKeyCursor(in [Optional] OptionsObject options) + [CallWith=ScriptExecutionContext] IDBRequest openKeyCursor(in [Optional] IDBKeyRange range, in [Optional] unsigned short direction)) raises (IDBDatabaseException); [CallWith=ScriptExecutionContext] IDBRequest get(in IDBKey key) raises (IDBDatabaseException); diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp index df88fdb..6eba189 100644 --- a/Source/WebCore/storage/IDBIndexBackendImpl.cpp +++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp @@ -100,7 +100,7 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr query->bindInt64(indexColumn, index->id()); if (query->step() != SQLResultRow) { - callbacks->onSuccess(); + callbacks->onSuccess(SerializedScriptValue::nullValue()); return; } diff --git a/Source/WebCore/storage/IDBObjectStore.cpp b/Source/WebCore/storage/IDBObjectStore.cpp index ed5c96a..53ae279 100644 --- a/Source/WebCore/storage/IDBObjectStore.cpp +++ b/Source/WebCore/storage/IDBObjectStore.cpp @@ -34,7 +34,7 @@ #include "IDBIndex.h" #include "IDBKey.h" #include "IDBKeyRange.h" -#include "IDBTransactionBackendInterface.h" +#include "IDBTransaction.h" #include "SerializedScriptValue.h" #include <wtf/UnusedParam.h> @@ -42,7 +42,7 @@ namespace WebCore { static const unsigned short defaultDirection = IDBCursor::NEXT; -IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) +IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransaction* transaction) : m_objectStore(idbObjectStore) , m_transaction(transaction) { @@ -70,7 +70,7 @@ PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_objectStore->get(key, request, m_transaction.get(), ec); + m_objectStore->get(key, request, m_transaction->backend(), ec); if (ec) return 0; return request.release(); @@ -79,7 +79,7 @@ PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, Pass PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_objectStore->put(value, key, true, request, m_transaction.get(), ec); + m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec); if (ec) return 0; return request; @@ -88,7 +88,7 @@ PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, Pass PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_objectStore->put(value, key, false, request, m_transaction.get(), ec); + m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec); if (ec) return 0; return request; @@ -97,7 +97,16 @@ PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, Pass PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) { RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_objectStore->deleteFunction(key, request, m_transaction.get(), ec); + m_objectStore->deleteFunction(key, request, m_transaction->backend(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->clear(request, m_transaction->backend(), ec); if (ec) return 0; return request; @@ -108,7 +117,7 @@ PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const Strin bool unique = false; options.getKeyBool("unique", unique); - RefPtr<IDBIndexBackendInterface> index = m_objectStore->createIndex(name, keyPath, unique, m_transaction.get(), ec); + RefPtr<IDBIndexBackendInterface> index = m_objectStore->createIndex(name, keyPath, unique, m_transaction->backend(), ec); ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa. if (!index) return 0; @@ -126,16 +135,11 @@ PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec) { - m_objectStore->deleteIndex(name, m_transaction.get(), ec); + m_objectStore->deleteIndex(name, m_transaction->backend(), ec); } -PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, unsigned short direction, ExceptionCode& ec) { - RefPtr<IDBKeyRange> range = options.getKeyKeyRange("range"); - - // Converted to an unsigned short. - int32_t direction = defaultDirection; - options.getKeyInt32("direction", direction); if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 ec = IDBDatabaseException::CONSTRAINT_ERR; @@ -143,7 +147,7 @@ PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* contex } RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); - m_objectStore->openCursor(range, direction, request, m_transaction.get(), ec); + m_objectStore->openCursor(range, direction, request, m_transaction->backend(), ec); if (ec) return 0; return request.release(); diff --git a/Source/WebCore/storage/IDBObjectStore.h b/Source/WebCore/storage/IDBObjectStore.h index 0e9a4a9..f0c5ebf 100644 --- a/Source/WebCore/storage/IDBObjectStore.h +++ b/Source/WebCore/storage/IDBObjectStore.h @@ -45,11 +45,10 @@ namespace WebCore { class DOMStringList; class IDBAny; -class IDBTransactionBackendInterface; class IDBObjectStore : public RefCounted<IDBObjectStore> { public: - static PassRefPtr<IDBObjectStore> create(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) + static PassRefPtr<IDBObjectStore> create(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransaction* transaction) { return adoptRef(new IDBObjectStore(idbObjectStore, transaction)); } @@ -63,25 +62,27 @@ public: PassRefPtr<IDBRequest> add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) { return add(context, value, 0, ec); } PassRefPtr<IDBRequest> put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) { return put(context, value, 0, ec); } PassRefPtr<IDBIndex> createIndex(const String& name, const String& keyPath, ExceptionCode& ec) { return createIndex(name, keyPath, OptionsObject(), ec); } - PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, OptionsObject(), ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, 0, ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openCursor(context, keyRange, IDBCursor::NEXT, ec); } PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); PassRefPtr<IDBRequest> add(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, ExceptionCode&); PassRefPtr<IDBRequest> put(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, ExceptionCode&); PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, PassRefPtr<IDBKey> key, ExceptionCode&); + PassRefPtr<IDBRequest> clear(ScriptExecutionContext*, ExceptionCode&); PassRefPtr<IDBIndex> createIndex(const String& name, const String& keyPath, const OptionsObject&, ExceptionCode&); PassRefPtr<IDBIndex> index(const String& name, ExceptionCode&); void deleteIndex(const String& name, ExceptionCode&); - PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, unsigned short direction, ExceptionCode&); private: - IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface>, IDBTransactionBackendInterface* transaction); + IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface>, IDBTransaction*); void removeTransactionFromPendingList(); RefPtr<IDBObjectStoreBackendInterface> m_objectStore; - RefPtr<IDBTransactionBackendInterface> m_transaction; + RefPtr<IDBTransaction> m_transaction; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBObjectStore.idl b/Source/WebCore/storage/IDBObjectStore.idl index f023dbe..c95fb9d 100644 --- a/Source/WebCore/storage/IDBObjectStore.idl +++ b/Source/WebCore/storage/IDBObjectStore.idl @@ -38,9 +38,11 @@ module storage { raises (IDBDatabaseException); [CallWith=ScriptExecutionContext, ImplementationFunction=deleteFunction] IDBRequest delete(in IDBKey key) raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest clear() + raises (IDBDatabaseException); [CallWith=ScriptExecutionContext] IDBRequest get(in IDBKey key) raises (IDBDatabaseException); - [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] OptionsObject options) + [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] IDBKeyRange range, in [Optional] unsigned short direction) raises (IDBDatabaseException); IDBIndex createIndex(in DOMString name, in [ConvertNullToNullString] DOMString keyPath, in [Optional] OptionsObject options) raises (IDBDatabaseException); diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp index 6b162ef..921bbab 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -118,7 +118,7 @@ void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr< ASSERT((key->type() == IDBKey::DateType) != query.isColumnNull(1)); ASSERT((key->type() == IDBKey::NumberType) != query.isColumnNull(2)); - callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(3))); + callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnBlobAsString(3))); ASSERT(query.step() != SQLResultRow); } @@ -142,7 +142,7 @@ static bool putObjectStoreData(SQLiteDatabase& db, IDBKey* key, SerializedScript if (query.prepare() != SQLResultOk) return false; key->bindWithNulls(query, 1); - query.bindText(4, value->toWireString()); + query.bindBlob(4, value->toWireString()); if (dataRowId != IDBDatabaseBackendImpl::InvalidId) query.bindInt64(5, dataRowId); else @@ -179,7 +179,7 @@ static bool putIndexData(SQLiteDatabase& db, IDBKey* key, int64_t indexId, int64 return putQuery.step() == SQLResultDone; } -void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) { if (transactionPtr->mode() == IDBTransaction::READ_ONLY) { ec = IDBDatabaseException::READ_ONLY_ERR; @@ -193,45 +193,75 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; // FIXME: This should throw a SERIAL_ERR on structured clone problems. // FIXME: This should throw a DATA_ERR when the wrong key/keyPath data is supplied. - if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, addOnly, callbacks, transaction))) + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, putMode, callbacks, transaction))) ec = IDBDatabaseException::NOT_ALLOWED_ERR; } -void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, SerializedScriptValue* value, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks) { - RefPtr<SerializedScriptValue> value = prpValue; - RefPtr<IDBKey> key = prpKey; + if (putMode == CursorUpdate) + ASSERT(key); - if (!objectStore->m_keyPath.isNull() && key) { + const bool autoIncrement = objectStore->autoIncrement(); + const bool hasKeyPath = !objectStore->m_keyPath.isNull(); + + if (hasKeyPath && key && putMode != CursorUpdate) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "A key was supplied for an objectStore that has a keyPath.")); - return; + return 0; } - if (objectStore->autoIncrement() && key) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "A key was supplied for an objectStore that is using auto increment.")); - return; + if (autoIncrement && key) { + objectStore->resetAutoIncrementKeyCache(); + return key; } - if (objectStore->autoIncrement()) { - key = objectStore->genAutoIncrementKey(); + if (autoIncrement) { + ASSERT(!key); + if (!hasKeyPath) + return objectStore->genAutoIncrementKey(); - if (!objectStore->m_keyPath.isNull()) { - // FIXME: Inject the generated key into the object. - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Adding data to object stores with auto increment and in-line keys not yet supported.")); - return; + RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath); + if (keyPathKey) { + objectStore->resetAutoIncrementKeyCache(); + return keyPathKey; } - } else if (!objectStore->m_keyPath.isNull()) { - key = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); - if (!key) { + // FIXME: Generate auto increment key, and inject it through the key path. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Adding data to object stores with auto increment and in-line keys not yet supported.")); + return 0; + } + + if (hasKeyPath) { + RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath); + + if (!keyPathKey) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The key could not be fetched from the keyPath.")); - return; + return 0; } - } else if (!key) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied.")); - return; + + if (putMode == CursorUpdate && !keyPathKey->isEqual(key)) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The key fetched from the keyPath does not match the key of the cursor.")); + return 0; + } + + return keyPathKey.release(); + } + + if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied")); + return 0; } + return key; +} + +void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + RefPtr<SerializedScriptValue> value = prpValue; + RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), value.get(), prpKey.get(), putMode, callbacks.get()); + if (!key) + return; + if (key->type() == IDBKey::NullType) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "NULL key is not allowed.")); return; @@ -261,7 +291,7 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr< bindWhereClause(getQuery, objectStore->id(), key.get()); bool isExistingValue = getQuery.step() == SQLResultRow; - if (addOnly && isExistingValue) { + if (putMode == AddOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; } @@ -342,7 +372,39 @@ void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefP ok = indexQuery.step() == SQLResultDone; ASSERT_UNUSED(ok, ok); - callbacks->onSuccess(); + callbacks->onSuccess(SerializedScriptValue::nullValue()); +} + +void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + if (transaction->mode() == IDBTransaction::READ_ONLY) { + ec = IDBDatabaseException::READ_ONLY_ERR; + return; + } + + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::clearInternal, objectStore, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) +{ + SQLiteStatement deleteQuery(db, sql); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindInt64(1, id); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. +} + +void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks) +{ + doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStore->id()); + doDelete(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id()); + + callbacks->onSuccess(SerializedScriptValue::undefinedValue()); } PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) @@ -401,16 +463,6 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const Stri return index.release(); } -static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) -{ - SQLiteStatement deleteQuery(db, sql); - bool ok = deleteQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - deleteQuery.bindInt64(1, id); - ok = deleteQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. -} - void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) { if (transaction->mode() != IDBTransaction::VERSION_CHANGE) { @@ -483,7 +535,7 @@ void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, Pass query->bindInt64(currentColumn, objectStore->id()); if (query->step() != SQLResultRow) { - callbacks->onSuccess(); + callbacks->onSuccess(SerializedScriptValue::nullValue()); return; } diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h index 9fb1b7c..b54f9fd 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -67,8 +67,9 @@ public: virtual bool autoIncrement() const { return m_autoIncrement; } virtual void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); - virtual void put(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void put(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); virtual void deleteFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void clear(PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); virtual PassRefPtr<IDBIndexBackendInterface> createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface*, ExceptionCode&); virtual PassRefPtr<IDBIndexBackendInterface> index(const String& name, ExceptionCode&); @@ -83,10 +84,13 @@ private: void loadIndexes(); SQLiteDatabase& sqliteDatabase() const; PassRefPtr<IDBKey> genAutoIncrementKey(); + void resetAutoIncrementKeyCache() { m_autoIncrementNumber = -1; } + static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, SerializedScriptValue*, IDBKey*, PutMode, IDBCallbacks*); static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); - static void putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + static void putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); static void deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); + static void clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBCallbacks>); static void createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); static void deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); diff --git a/Source/WebCore/storage/IDBObjectStoreBackendInterface.h b/Source/WebCore/storage/IDBObjectStoreBackendInterface.h index 02ceb27..177701c 100644 --- a/Source/WebCore/storage/IDBObjectStoreBackendInterface.h +++ b/Source/WebCore/storage/IDBObjectStoreBackendInterface.h @@ -51,9 +51,17 @@ public: virtual PassRefPtr<DOMStringList> indexNames() const = 0; virtual void get(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; - virtual void put(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, bool addOnly, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + + enum PutMode { + AddOrUpdate, + AddOnly, + CursorUpdate + }; + virtual void put(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; virtual void deleteFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void clear(PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual PassRefPtr<IDBIndexBackendInterface> createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface*, ExceptionCode&) = 0; virtual PassRefPtr<IDBIndexBackendInterface> index(const String& name, ExceptionCode&) = 0; virtual void deleteIndex(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0; @@ -66,4 +74,3 @@ public: #endif #endif // IDBObjectStoreBackendInterface_h - diff --git a/Source/WebCore/storage/IDBRequest.cpp b/Source/WebCore/storage/IDBRequest.cpp index cbd635c..e7498ec 100644 --- a/Source/WebCore/storage/IDBRequest.cpp +++ b/Source/WebCore/storage/IDBRequest.cpp @@ -31,10 +31,11 @@ #if ENABLE(INDEXED_DATABASE) -#include "Event.h" +#include "Document.h" #include "EventException.h" #include "EventListener.h" #include "EventNames.h" +#include "EventQueue.h" #include "IDBCursor.h" #include "IDBDatabase.h" #include "IDBIndex.h" @@ -42,163 +43,169 @@ #include "IDBObjectStore.h" #include "IDBPendingTransactionMonitor.h" #include "IDBSuccessEvent.h" -#include "ScriptExecutionContext.h" namespace WebCore { -IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction) +PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) +{ + return adoptRef(new IDBRequest(context, source, transaction)); +} + +IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction) : ActiveDOMObject(context, this) , m_source(source) , m_transaction(transaction) - , m_timer(this, &IDBRequest::timerFired) , m_readyState(LOADING) + , m_finished(false) { if (m_transaction) - IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); } IDBRequest::~IDBRequest() { } -bool IDBRequest::resetReadyState(IDBTransactionBackendInterface* transaction) +bool IDBRequest::resetReadyState(IDBTransaction* transaction) { - ASSERT(m_readyState == DONE); - m_readyState = LOADING; - ASSERT(!m_transaction); + ASSERT(!m_finished); + ASSERT(scriptExecutionContext()); + if (m_readyState != DONE) + return false; + m_transaction = transaction; - IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); + m_readyState = LOADING; + + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); + return true; } void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error) { - scheduleEvent(0, error); -} - -void IDBRequest::onSuccess() -{ - scheduleEvent(IDBAny::createNull(), 0); + enqueueEvent(IDBErrorEvent::create(m_source, *error)); } void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend) { - scheduleEvent(IDBAny::create(IDBCursor::create(backend, this, m_transaction.get())), 0); + enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBCursor::create(backend, this, m_transaction.get())))); } void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend) { - scheduleEvent(IDBAny::create(IDBDatabase::create(backend)), 0); + enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBDatabase::create(scriptExecutionContext(), backend)))); } void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend) { - scheduleEvent(IDBAny::create(IDBIndex::create(backend, m_transaction.get())), 0); + ASSERT_NOT_REACHED(); // FIXME: This method should go away. } void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) { - scheduleEvent(IDBAny::create(idbKey), 0); + enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(idbKey))); } void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend) { - // FIXME: This function should go away once createObjectStore is sync. - scheduleEvent(IDBAny::create(IDBObjectStore::create(backend, m_transaction.get())), 0); + ASSERT_NOT_REACHED(); // FIXME: This method should go away. } void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend) { + if (!scriptExecutionContext()) + return; + RefPtr<IDBTransactionBackendInterface> backend = prpBackend; - // This is only used by setVersion which will always have a source that's an IDBDatabase. - m_source->idbDatabase()->setSetVersionTransaction(backend.get()); RefPtr<IDBTransaction> frontend = IDBTransaction::create(scriptExecutionContext(), backend, m_source->idbDatabase().get()); backend->setCallbacks(frontend.get()); - m_transaction = backend; - IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); - scheduleEvent(IDBAny::create(frontend.release()), 0); -} + m_transaction = frontend; -void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) -{ - scheduleEvent(IDBAny::create(serializedScriptValue), 0); -} + ASSERT(m_source->type() == IDBAny::IDBDatabaseType); + m_source->idbDatabase()->setSetVersionTransaction(frontend.get()); -ScriptExecutionContext* IDBRequest::scriptExecutionContext() const -{ - return ActiveDOMObject::scriptExecutionContext(); + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend()); + enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(frontend.release()))); } -bool IDBRequest::canSuspend() const +void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) { - // IDBTransactions cannot be suspended at the moment. We therefore - // disallow the back/forward cache for pages that use IndexedDatabase. - return false; + enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(serializedScriptValue))); } -EventTargetData* IDBRequest::eventTargetData() +bool IDBRequest::hasPendingActivity() const { - return &m_eventTargetData; + // FIXME: In an ideal world, we should return true as long as anyone has a or can + // get a handle to us and we have event listeners. This is order to handle + // user generated events properly. + return !m_finished || ActiveDOMObject::hasPendingActivity(); } -EventTargetData* IDBRequest::ensureEventTargetData() +ScriptExecutionContext* IDBRequest::scriptExecutionContext() const { - return &m_eventTargetData; + return ActiveDOMObject::scriptExecutionContext(); } -void IDBRequest::timerFired(Timer<IDBRequest>*) +bool IDBRequest::dispatchEvent(PassRefPtr<Event> event) { - ASSERT(m_selfRef); - ASSERT(m_pendingEvents.size()); - // FIXME: We should handle the stop event and stop any timers when we see it. We can then assert here that scriptExecutionContext is non-null. - - // We need to keep self-referencing ourself, otherwise it's possible we'll be deleted. - // But in some cases, suspend() could be called while we're dispatching an event, so we - // need to make sure that resume() doesn't re-start the timer based on m_selfRef being set. - RefPtr<IDBRequest> selfRef = m_selfRef.release(); - - // readyStateReset can be called synchronously while we're dispatching the event. - RefPtr<IDBTransactionBackendInterface> transaction = m_transaction; - m_transaction.clear(); - - Vector<PendingEvent> pendingEvents; - pendingEvents.swap(m_pendingEvents); - for (size_t i = 0; i < pendingEvents.size(); ++i) { - // It's possible we've navigated in which case we'll crash. - if (!scriptExecutionContext()) - return; + ASSERT(!m_finished); + ASSERT(scriptExecutionContext()); + ASSERT(event->target() == this); + ASSERT(m_readyState < DONE); + m_readyState = DONE; - if (pendingEvents[i].m_error) { - ASSERT(!pendingEvents[i].m_result); - dispatchEvent(IDBErrorEvent::create(m_source, *pendingEvents[i].m_error)); - } else { - ASSERT(pendingEvents[i].m_result->type() != IDBAny::UndefinedType); - dispatchEvent(IDBSuccessEvent::create(m_source, pendingEvents[i].m_result)); - } + Vector<RefPtr<EventTarget> > targets; + targets.append(this); + if (m_transaction) { + targets.append(m_transaction); + // If there ever are events that are associated with a database but + // that do not have a transaction, then this will not work and we need + // this object to actually hold a reference to the database (to ensure + // it stays alive). + targets.append(m_transaction->db()); } - if (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. - transaction->didCompleteTaskEvents(); + + ASSERT(event->isIDBErrorEvent() || event->isIDBSuccessEvent()); + bool dontPreventDefault = static_cast<IDBEvent*>(event.get())->dispatch(targets); + + // If the event's result was of type IDBCursor, then it's possible for us to + // fire again (unless the transaction completes). + if (event->isIDBSuccessEvent()) { + RefPtr<IDBAny> any = static_cast<IDBSuccessEvent*>(event.get())->result(); + if (any->type() != IDBAny::IDBCursorType) + m_finished = true; + } else + m_finished = true; + + if (m_transaction) { + if (dontPreventDefault && event->isIDBErrorEvent()) + m_transaction->backend()->abort(); + m_transaction->backend()->didCompleteTaskEvents(); } + return dontPreventDefault; } -void IDBRequest::scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError> error) +void IDBRequest::enqueueEvent(PassRefPtr<Event> event) { + ASSERT(!m_finished); ASSERT(m_readyState < DONE); - ASSERT(!!m_selfRef == m_timer.isActive()); + if (!scriptExecutionContext()) + return; - PendingEvent pendingEvent; - pendingEvent.m_result = result; - pendingEvent.m_error = error; - m_pendingEvents.append(pendingEvent); + ASSERT(scriptExecutionContext()->isDocument()); + EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); + event->setTarget(this); + eventQueue->enqueueEvent(event); +} - m_readyState = DONE; - if (!m_timer.isActive()) { - m_selfRef = this; - m_timer.startOneShot(0); - } +EventTargetData* IDBRequest::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* IDBRequest::ensureEventTargetData() +{ + return &m_eventTargetData; } } // namespace WebCore diff --git a/Source/WebCore/storage/IDBRequest.h b/Source/WebCore/storage/IDBRequest.h index fa68208..5c31318 100644 --- a/Source/WebCore/storage/IDBRequest.h +++ b/Source/WebCore/storage/IDBRequest.h @@ -32,21 +32,21 @@ #if ENABLE(INDEXED_DATABASE) #include "ActiveDOMObject.h" +#include "Event.h" #include "EventListener.h" #include "EventNames.h" #include "EventTarget.h" #include "IDBAny.h" #include "IDBCallbacks.h" -#include "Timer.h" -#include <wtf/Vector.h> namespace WebCore { -class IDBTransactionBackendInterface; +class IDBEvent; +class IDBTransaction; class IDBRequest : public IDBCallbacks, public EventTarget, public ActiveDOMObject { public: - static PassRefPtr<IDBRequest> create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction) { return adoptRef(new IDBRequest(context, source, transaction)); } + static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*); virtual ~IDBRequest(); // Defined in the IDL @@ -58,11 +58,10 @@ public: DEFINE_ATTRIBUTE_EVENT_LISTENER(success); DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - bool resetReadyState(IDBTransactionBackendInterface*); + bool resetReadyState(IDBTransaction*); // IDBCallbacks virtual void onError(PassRefPtr<IDBDatabaseError>); - virtual void onSuccess(); // For "null". virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>); virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>); virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>); @@ -71,21 +70,22 @@ public: virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>); virtual void onSuccess(PassRefPtr<SerializedScriptValue>); + // ActiveDOMObject + virtual bool hasPendingActivity() const; + // EventTarget virtual IDBRequest* toIDBRequest() { return this; } - - // ActiveDOMObject virtual ScriptExecutionContext* scriptExecutionContext() const; - virtual bool canSuspend() const; + virtual bool dispatchEvent(PassRefPtr<Event>); + bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); } using ThreadSafeShared<IDBCallbacks>::ref; using ThreadSafeShared<IDBCallbacks>::deref; private: - IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction); + IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*); - void timerFired(Timer<IDBRequest>*); - void scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError>); + void enqueueEvent(PassRefPtr<Event>); // EventTarget virtual void refEventTarget() { ref(); } @@ -94,19 +94,11 @@ private: virtual EventTargetData* ensureEventTargetData(); RefPtr<IDBAny> m_source; - RefPtr<IDBTransactionBackendInterface> m_transaction; - - struct PendingEvent { - RefPtr<IDBAny> m_result; - RefPtr<IDBDatabaseError> m_error; - }; - Vector<PendingEvent> m_pendingEvents; - - // Used to fire events asynchronously. - Timer<IDBRequest> m_timer; - RefPtr<IDBRequest> m_selfRef; // This is set to us iff there's an event pending. + RefPtr<IDBTransaction> m_transaction; ReadyState m_readyState; + bool m_finished; // Is it possible that we'll fire any more events? If not, we're finished. + EventTargetData m_eventTargetData; }; diff --git a/Source/WebCore/storage/IDBSuccessEvent.cpp b/Source/WebCore/storage/IDBSuccessEvent.cpp index 2dcd964..110b78b 100644 --- a/Source/WebCore/storage/IDBSuccessEvent.cpp +++ b/Source/WebCore/storage/IDBSuccessEvent.cpp @@ -42,7 +42,7 @@ PassRefPtr<IDBSuccessEvent> IDBSuccessEvent::create(PassRefPtr<IDBAny> source, P } IDBSuccessEvent::IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result) - : IDBEvent(eventNames().successEvent, source) + : IDBEvent(eventNames().successEvent, source, false) , m_result(result) { } diff --git a/Source/WebCore/storage/IDBTimeoutEvent.cpp b/Source/WebCore/storage/IDBTimeoutEvent.cpp deleted file mode 100644 index b61ee47..0000000 --- a/Source/WebCore/storage/IDBTimeoutEvent.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IDBTimeoutEvent.h" - -#if ENABLE(INDEXED_DATABASE) - -#include "EventNames.h" -#include "IDBAny.h" - -namespace WebCore { - -PassRefPtr<IDBTimeoutEvent> IDBTimeoutEvent::create() -{ - return adoptRef(new IDBTimeoutEvent()); -} - -IDBTimeoutEvent::IDBTimeoutEvent() - : IDBEvent(eventNames().abortEvent, 0) // FIXME: set the source to the transaction -{ -} - -IDBTimeoutEvent::~IDBTimeoutEvent() -{ -} - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/storage/IDBTimeoutEvent.h b/Source/WebCore/storage/IDBTimeoutEvent.h deleted file mode 100644 index afc9ba4..0000000 --- a/Source/WebCore/storage/IDBTimeoutEvent.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IDBTimeoutEvent_h -#define IDBTimeoutEvent_h - -#if ENABLE(INDEXED_DATABASE) - -#include "IDBEvent.h" -#include "PlatformString.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class IDBTimeoutEvent : public IDBEvent { -public: - static PassRefPtr<IDBTimeoutEvent> create(); - // FIXME: Need to allow creation of these events from JS. - virtual ~IDBTimeoutEvent(); - - virtual bool isIDBTimeoutEvent() const { return true; } - -private: - IDBTimeoutEvent(); -}; - -} // namespace WebCore - -#endif // ENABLE(INDEXED_DATABASE) - -#endif // IDBTimeoutEvent_h diff --git a/Source/WebCore/storage/IDBTransaction.cpp b/Source/WebCore/storage/IDBTransaction.cpp index e3625d4..1f696b3 100644 --- a/Source/WebCore/storage/IDBTransaction.cpp +++ b/Source/WebCore/storage/IDBTransaction.cpp @@ -28,8 +28,9 @@ #if ENABLE(INDEXED_DATABASE) -#include "Event.h" +#include "Document.h" #include "EventException.h" +#include "EventQueue.h" #include "IDBAbortEvent.h" #include "IDBCompleteEvent.h" #include "IDBDatabase.h" @@ -38,20 +39,22 @@ #include "IDBObjectStore.h" #include "IDBObjectStoreBackendInterface.h" #include "IDBPendingTransactionMonitor.h" -#include "IDBTimeoutEvent.h" -#include "ScriptExecutionContext.h" namespace WebCore { +PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db) +{ + return adoptRef(new IDBTransaction(context, backend, db)); +} + IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db) : ActiveDOMObject(context, this) , m_backend(backend) , m_database(db) , m_mode(m_backend->mode()) - , m_onAbortTimer(this, &IDBTransaction::onAbortTimerFired) - , m_onCompleteTimer(this, &IDBTransaction::onCompleteTimerFired) - , m_onTimeoutTimer(this, &IDBTransaction::onTimeoutTimerFired) + , m_finished(false) { + ASSERT(m_backend); IDBPendingTransactionMonitor::addPendingTransaction(m_backend.get()); } @@ -59,19 +62,29 @@ IDBTransaction::~IDBTransaction() { } +IDBTransactionBackendInterface* IDBTransaction::backend() const +{ + return m_backend.get(); +} + +bool IDBTransaction::finished() const +{ + return m_finished; +} + unsigned short IDBTransaction::mode() const { return m_mode; } -IDBDatabase* IDBTransaction::db() +IDBDatabase* IDBTransaction::db() const { return m_database.get(); } PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionCode& ec) { - if (!m_backend) { + if (m_finished) { ec = IDBDatabaseException::NOT_ALLOWED_ERR; return 0; } @@ -80,94 +93,96 @@ PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, Excep ASSERT(ec); return 0; } - RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend, m_backend.get()); + RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend, this); return objectStore.release(); } void IDBTransaction::abort() { + RefPtr<IDBTransaction> selfRef = this; if (m_backend) m_backend->abort(); } -ScriptExecutionContext* IDBTransaction::scriptExecutionContext() const -{ - return ActiveDOMObject::scriptExecutionContext(); -} - void IDBTransaction::onAbort() { - ASSERT(!m_onAbortTimer.isActive()); - ASSERT(!m_onCompleteTimer.isActive()); - ASSERT(!m_onTimeoutTimer.isActive()); - m_selfRef = this; - m_onAbortTimer.startOneShot(0); - m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. + enqueueEvent(IDBAbortEvent::create(IDBAny::create(this))); } void IDBTransaction::onComplete() { - ASSERT(!m_onAbortTimer.isActive()); - ASSERT(!m_onCompleteTimer.isActive()); - ASSERT(!m_onTimeoutTimer.isActive()); - m_selfRef = this; - m_onCompleteTimer.startOneShot(0); - m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. + enqueueEvent(IDBCompleteEvent::create(IDBAny::create(this))); } -void IDBTransaction::onTimeout() +bool IDBTransaction::hasPendingActivity() const { - ASSERT(!m_onAbortTimer.isActive()); - ASSERT(!m_onCompleteTimer.isActive()); - ASSERT(!m_onTimeoutTimer.isActive()); - m_selfRef = this; - m_onTimeoutTimer.startOneShot(0); - m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. + // FIXME: In an ideal world, we should return true as long as anyone has a or can + // get a handle to us or any child request object and any of those have + // event listeners. This is in order to handle user generated events properly. + return !m_finished || ActiveDOMObject::hasPendingActivity(); } -bool IDBTransaction::canSuspend() const +ScriptExecutionContext* IDBTransaction::scriptExecutionContext() const { - // We may be in the middle of a transaction so we cannot suspend our object. - // Instead, we simply don't allow the owner page to go into the back/forward cache. - return false; + return ActiveDOMObject::scriptExecutionContext(); } -void IDBTransaction::stop() +bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event) { - if (m_backend) - m_backend->abort(); + ASSERT(!m_finished); + ASSERT(scriptExecutionContext()); + ASSERT(event->target() == this); + ASSERT(!m_finished); + m_finished = true; + + Vector<RefPtr<EventTarget> > targets; + targets.append(this); + targets.append(db()); + + ASSERT(event->isIDBAbortEvent() || event->isIDBCompleteEvent()); + return static_cast<IDBEvent*>(event.get())->dispatch(targets); } -EventTargetData* IDBTransaction::eventTargetData() +bool IDBTransaction::canSuspend() const { - return &m_eventTargetData; + // FIXME: Technically we can suspend before the first request is schedule + // and after the complete/abort event is enqueued. + return m_finished; } -EventTargetData* IDBTransaction::ensureEventTargetData() +void IDBTransaction::contextDestroyed() { - return &m_eventTargetData; + ActiveDOMObject::contextDestroyed(); + + // Must happen in contextDestroyed since it can result in ActiveDOMObjects being destructed + // (and contextDestroyed is the only one resilient against this). + RefPtr<IDBTransaction> selfRef = this; + if (m_backend) + m_backend->abort(); + + m_finished = true; } -void IDBTransaction::onAbortTimerFired(Timer<IDBTransaction>* transaction) +void IDBTransaction::enqueueEvent(PassRefPtr<Event> event) { - ASSERT(m_selfRef); - RefPtr<IDBTransaction> selfRef = m_selfRef.release(); - dispatchEvent(IDBAbortEvent::create()); + ASSERT(!m_finished); + if (!scriptExecutionContext()) + return; + + ASSERT(scriptExecutionContext()->isDocument()); + EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); + event->setTarget(this); + eventQueue->enqueueEvent(event); } -void IDBTransaction::onCompleteTimerFired(Timer<IDBTransaction>* transaction) +EventTargetData* IDBTransaction::eventTargetData() { - ASSERT(m_selfRef); - RefPtr<IDBTransaction> selfRef = m_selfRef.release(); - dispatchEvent(IDBCompleteEvent::create()); + return &m_eventTargetData; } - -void IDBTransaction::onTimeoutTimerFired(Timer<IDBTransaction>* transaction) +EventTargetData* IDBTransaction::ensureEventTargetData() { - ASSERT(m_selfRef); - RefPtr<IDBTransaction> selfRef = m_selfRef.release(); - dispatchEvent(IDBTimeoutEvent::create()); + return &m_eventTargetData; } } diff --git a/Source/WebCore/storage/IDBTransaction.h b/Source/WebCore/storage/IDBTransaction.h index d0a9f0f..ff4feb6 100644 --- a/Source/WebCore/storage/IDBTransaction.h +++ b/Source/WebCore/storage/IDBTransaction.h @@ -30,12 +30,12 @@ #include "ActiveDOMObject.h" #include "DOMStringList.h" +#include "Event.h" #include "EventListener.h" #include "EventNames.h" #include "EventTarget.h" #include "IDBTransactionBackendInterface.h" #include "IDBTransactionCallbacks.h" -#include "Timer.h" #include <wtf/RefCounted.h> namespace WebCore { @@ -45,10 +45,7 @@ class IDBObjectStore; class IDBTransaction : public IDBTransactionCallbacks, public EventTarget, public ActiveDOMObject { public: - static PassRefPtr<IDBTransaction> create(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db) - { - return adoptRef(new IDBTransaction(context, backend, db)); - } + static PassRefPtr<IDBTransaction> create(ScriptExecutionContext*, PassRefPtr<IDBTransactionBackendInterface>, IDBDatabase*); virtual ~IDBTransaction(); enum Mode { @@ -57,27 +54,32 @@ public: VERSION_CHANGE = 2 }; + IDBTransactionBackendInterface* backend() const; + bool finished() const; + unsigned short mode() const; - IDBDatabase* db(); + IDBDatabase* db() const; PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&); void abort(); DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); DEFINE_ATTRIBUTE_EVENT_LISTENER(complete); - DEFINE_ATTRIBUTE_EVENT_LISTENER(timeout); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); // IDBTransactionCallbacks virtual void onAbort(); virtual void onComplete(); - virtual void onTimeout(); // EventTarget virtual IDBTransaction* toIDBTransaction() { return this; } + virtual ScriptExecutionContext* scriptExecutionContext() const; + virtual bool dispatchEvent(PassRefPtr<Event>); + bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); } // ActiveDOMObject - virtual ScriptExecutionContext* scriptExecutionContext() const; + virtual bool hasPendingActivity() const; virtual bool canSuspend() const; - virtual void stop(); + virtual void contextDestroyed(); using RefCounted<IDBTransactionCallbacks>::ref; using RefCounted<IDBTransactionCallbacks>::deref; @@ -85,25 +87,20 @@ public: private: IDBTransaction(ScriptExecutionContext*, PassRefPtr<IDBTransactionBackendInterface>, IDBDatabase*); + void enqueueEvent(PassRefPtr<Event>); + // EventTarget virtual void refEventTarget() { ref(); } virtual void derefEventTarget() { deref(); } virtual EventTargetData* eventTargetData(); virtual EventTargetData* ensureEventTargetData(); - void onAbortTimerFired(Timer<IDBTransaction>*); - void onCompleteTimerFired(Timer<IDBTransaction>*); - void onTimeoutTimerFired(Timer<IDBTransaction>*); - - EventTargetData m_eventTargetData; RefPtr<IDBTransactionBackendInterface> m_backend; RefPtr<IDBDatabase> m_database; unsigned short m_mode; + bool m_finished; // Is it possible that we'll fire any more events or allow any new transactions? If not, we're finished. - Timer<IDBTransaction> m_onAbortTimer; - Timer<IDBTransaction> m_onCompleteTimer; - Timer<IDBTransaction> m_onTimeoutTimer; - RefPtr<IDBTransaction> m_selfRef; // This is set to us iff there's an event pending. + EventTargetData m_eventTargetData; }; } // namespace WebCore diff --git a/Source/WebCore/storage/IDBTransaction.idl b/Source/WebCore/storage/IDBTransaction.idl index b57ac4a..2f02195 100644 --- a/Source/WebCore/storage/IDBTransaction.idl +++ b/Source/WebCore/storage/IDBTransaction.idl @@ -44,7 +44,7 @@ module storage { // Events attribute EventListener onabort; attribute EventListener oncomplete; - attribute EventListener ontimeout; + attribute EventListener onerror; // EventTarget interface void addEventListener(in DOMString type, in EventListener listener, diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp index 0012231..1357838 100644 --- a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp @@ -35,15 +35,14 @@ namespace WebCore { -PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl* database) +PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database) { - return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, timeout, database)); + return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, database)); } -IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl* database) +IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database) : m_objectStoreNames(objectStores) , m_mode(mode) - , m_timeout(timeout) // FIXME: Implement timeout. , m_state(Unused) , m_database(database) , m_transaction(new SQLiteTransaction(database->sqliteDatabase())) @@ -180,12 +179,12 @@ void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>* if (m_state == StartPending) { m_transaction->begin(); m_state = Running; - } else - ASSERT(m_state == Running); + } TaskQueue queue; queue.swap(m_taskQueue); while (!queue.isEmpty() && m_state != Finished) { + ASSERT(m_state == Running); OwnPtr<ScriptExecutionContext::Task> task(queue.first().release()); queue.removeFirst(); m_pendingEvents++; diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.h b/Source/WebCore/storage/IDBTransactionBackendImpl.h index f4dfaa8..1297e74 100644 --- a/Source/WebCore/storage/IDBTransactionBackendImpl.h +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.h @@ -42,7 +42,7 @@ class IDBDatabaseBackendImpl; class IDBTransactionBackendImpl : public IDBTransactionBackendInterface { public: - static PassRefPtr<IDBTransactionBackendImpl> create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl*); + static PassRefPtr<IDBTransactionBackendImpl> create(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl*); virtual ~IDBTransactionBackendImpl(); virtual PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name, ExceptionCode&); @@ -55,7 +55,7 @@ public: void run(); private: - IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl*); + IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl*); enum State { Unused, // Created, but no tasks yet. @@ -72,7 +72,6 @@ private: RefPtr<DOMStringList> m_objectStoreNames; unsigned short m_mode; - unsigned long m_timeout; State m_state; RefPtr<IDBTransactionCallbacks> m_callbacks; diff --git a/Source/WebCore/storage/IDBTransactionCallbacks.h b/Source/WebCore/storage/IDBTransactionCallbacks.h index 348608d..8d906a6 100644 --- a/Source/WebCore/storage/IDBTransactionCallbacks.h +++ b/Source/WebCore/storage/IDBTransactionCallbacks.h @@ -42,7 +42,6 @@ public: virtual void onAbort() = 0; virtual void onComplete() = 0; - virtual void onTimeout() = 0; }; } // namespace WebCore diff --git a/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp b/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp index 92e9a7b..81eeb3e 100644 --- a/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp +++ b/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp @@ -39,11 +39,6 @@ PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create() return PlatformBridge::idbFactory(); } -IDBFactoryBackendInterface::~IDBFactoryBackendInterface() -{ - PlatformBridge::idbShutdown(); -} - } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE) |