IDB: Make opening/establishing a database asynchronous.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Nov 2013 05:08:11 +0000 (05:08 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Nov 2013 05:08:11 +0000 (05:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=123775

Reviewed by Andreas Kling.

No new tests (No behavior change for a tested port).

* Modules/indexeddb/IDBBackingStoreInterface.h: Add getOrEstablishIDBDatabaseMetadata with a callback,
  removing getIDBDatabaseMetaData, getObjectStores, and createIDBDatabaseMetaData in the process.

* Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp:
(WebCore::IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata): Adapted from getIDBDatabaseMetaData,
  implement the asynchronous interface in terms of other LevelDB methods, always calling back synchronously.
(WebCore::IDBBackingStoreLevelDB::createIDBDatabaseMetaData):
* Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h:

* Modules/indexeddb/IDBDatabaseBackendImpl.cpp:
(WebCore::IDBDatabaseBackendImpl::create):
(WebCore::IDBDatabaseBackendImpl::openInternalAsync): Broken off from openInternal.
(WebCore::IDBDatabaseBackendImpl::didOpenInternalAsync): Broken off from openInternal.
(WebCore::IDBDatabaseBackendImpl::processPendingCalls):
(WebCore::IDBDatabaseBackendImpl::processPendingOpenCalls): Broken off to allow didOpenInternalAsync
  to perform open callbacks in the failure case.
(WebCore::IDBDatabaseBackendImpl::openConnection): Always queue open connection calls, then immediately processPendingCalls.
(WebCore::IDBDatabaseBackendImpl::openConnectionInternal): Actually perform open connection calls.
* Modules/indexeddb/IDBDatabaseBackendImpl.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@158641 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBBackingStoreInterface.h
Source/WebCore/Modules/indexeddb/IDBDatabaseBackendImpl.cpp
Source/WebCore/Modules/indexeddb/IDBDatabaseBackendImpl.h
Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp
Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h

index 8d250da..68d6b49 100644 (file)
@@ -1,3 +1,33 @@
+2013-11-04  Brady Eidson  <beidson@apple.com>
+
+        IDB: Make opening/establishing a database asynchronous.
+        https://bugs.webkit.org/show_bug.cgi?id=123775
+
+        Reviewed by Andreas Kling.
+
+        No new tests (No behavior change for a tested port).
+
+        * Modules/indexeddb/IDBBackingStoreInterface.h: Add getOrEstablishIDBDatabaseMetadata with a callback,
+          removing getIDBDatabaseMetaData, getObjectStores, and createIDBDatabaseMetaData in the process.
+
+        * Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp:
+        (WebCore::IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata): Adapted from getIDBDatabaseMetaData,
+          implement the asynchronous interface in terms of other LevelDB methods, always calling back synchronously.
+        (WebCore::IDBBackingStoreLevelDB::createIDBDatabaseMetaData):
+        * Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h:
+
+        * Modules/indexeddb/IDBDatabaseBackendImpl.cpp:
+        (WebCore::IDBDatabaseBackendImpl::create):
+        (WebCore::IDBDatabaseBackendImpl::openInternalAsync): Broken off from openInternal.
+        (WebCore::IDBDatabaseBackendImpl::didOpenInternalAsync): Broken off from openInternal.
+        (WebCore::IDBDatabaseBackendImpl::processPendingCalls):
+        (WebCore::IDBDatabaseBackendImpl::processPendingOpenCalls): Broken off to allow didOpenInternalAsync
+          to perform open callbacks in the failure case.
+        (WebCore::IDBDatabaseBackendImpl::openConnection): Always queue open connection calls, then immediately processPendingCalls.
+        (WebCore::IDBDatabaseBackendImpl::openConnectionInternal): Actually perform open connection calls.
+        * Modules/indexeddb/IDBDatabaseBackendImpl.h:
+
+
 2013-11-04  Andreas Kling  <akling@apple.com>
 
         CTTE: RenderFrameBase's widget is always a FrameView.
index dad6690..ffc5020 100644 (file)
@@ -30,6 +30,7 @@
 #include "IDBMetadata.h"
 #include "IndexedDB.h"
 
+#include <functional>
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
@@ -81,9 +82,9 @@ public:
 
     virtual std::unique_ptr<Transaction> createBackingStoreTransaction() = 0;
 
-    virtual bool getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata*, bool& found) = 0;
-    virtual bool getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores) = 0;
-    virtual bool createIDBDatabaseMetaData(const String& name, const String& version, int64_t intVersion, int64_t& rowId) = 0;
+    typedef std::function<void (const IDBDatabaseMetadata&, bool success)> GetIDBDatabaseMetadataFunction;
+    virtual void getOrEstablishIDBDatabaseMetadata(const String& name, GetIDBDatabaseMetadataFunction) = 0;
+
     virtual bool keyExistsInObjectStore(IDBBackingStoreInterface::Transaction&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, RefPtr<IDBRecordIdentifier>& foundIDBRecordIdentifier) = 0;
 
     virtual bool putIndexDataForRecord(IDBBackingStoreInterface::Transaction&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const IDBRecordIdentifier*) = 0;
index 242d3c7..6b01962 100644 (file)
@@ -47,8 +47,8 @@ namespace WebCore {
 PassRefPtr<IDBDatabaseBackendImpl> IDBDatabaseBackendImpl::create(const String& name, IDBBackingStoreInterface* backingStore, IDBFactoryBackendInterface* factory, const String& uniqueIdentifier)
 {
     RefPtr<IDBDatabaseBackendImpl> backend = adoptRef(new IDBDatabaseBackendImpl(name, backingStore, factory, uniqueIdentifier));
-    if (!backend->openInternal())
-        return 0;
+    backend->openInternalAsync();
+    
     return backend.release();
 }
 
@@ -103,17 +103,24 @@ void IDBDatabaseBackendImpl::removeIndex(int64_t objectStoreId, int64_t indexId)
     m_metadata.objectStores.set(objectStoreId, objectStore);
 }
 
-bool IDBDatabaseBackendImpl::openInternal()
+void IDBDatabaseBackendImpl::openInternalAsync()
 {
-    bool success = false;
-    bool ok = m_backingStore->getIDBDatabaseMetaData(m_metadata.name, &m_metadata, success);
-    ASSERT_WITH_MESSAGE(success == (m_metadata.id != InvalidId), "success = %s, m_id = %lld", success ? "true" : "false", static_cast<long long>(m_metadata.id));
-    if (!ok)
-        return false;
-    if (success)
-        return m_backingStore->getObjectStores(m_metadata.id, &m_metadata.objectStores);
+    RefPtr<IDBDatabaseBackendImpl> self = this;
+    m_backingStore->getOrEstablishIDBDatabaseMetadata(m_metadata.name, [self](const IDBDatabaseMetadata& metadata, bool success) {
+        self->didOpenInternalAsync(metadata, success);
+    });
+}
+
+void IDBDatabaseBackendImpl::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success)
+{
+    if (!success) {
+        processPendingOpenCalls(false);
+        return;
+    }
 
-    return m_backingStore->createIDBDatabaseMetaData(m_metadata.name, String::number(m_metadata.version), m_metadata.version, m_metadata.id);
+    m_metadata = metadata;
+
+    processPendingCalls();
 }
 
 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
@@ -413,17 +420,40 @@ void IDBDatabaseBackendImpl::processPendingCalls()
     // deleteDatabaseFinal should never re-queue calls.
     ASSERT(m_pendingDeleteCalls.isEmpty());
 
-    // This check is also not really needed, openConnection would just requeue its calls.
     if (m_runningVersionChangeTransaction)
         return;
 
-    // Open calls can be requeued if an open call started a version change transaction.
+    processPendingOpenCalls(true);
+}
+
+void IDBDatabaseBackendImpl::processPendingOpenCalls(bool success)
+{
+    // Open calls can be requeued if an open call started a version change transaction or deletes the database.
     Deque<OwnPtr<IDBPendingOpenCall>> pendingOpenCalls;
     m_pendingOpenCalls.swap(pendingOpenCalls);
 
     while (!pendingOpenCalls.isEmpty()) {
         OwnPtr<IDBPendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
-        openConnection(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version());
+        if (success) {
+            if (m_metadata.id == InvalidId) {
+                // This database was deleted then quickly re-opened.
+                // openInternalAsync() will recreate it in the backing store and then resume processing pending callbacks.
+                pendingOpenCalls.prepend(pendingOpenCall.release());
+                pendingOpenCalls.swap(m_pendingOpenCalls);
+
+                openInternalAsync();
+                return;
+            }
+            openConnectionInternal(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version());
+        } else {
+            String message;
+            RefPtr<IDBDatabaseError> error;
+            if (pendingOpenCall->version() == IDBDatabaseMetadata::NoIntVersion)
+                message = "Internal error opening database with no version specified.";
+            else
+                message = String::format("Internal error opening database with version %llu", static_cast<unsigned long long>(pendingOpenCall->version()));
+            pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
+        }
     }
 }
 
@@ -440,30 +470,23 @@ void IDBDatabaseBackendImpl::createTransaction(int64_t transactionId, PassRefPtr
 
 void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
 {
-    ASSERT(m_backingStore.get());
     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
 
-    if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
-        m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
-        return;
-    }
+    m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
 
-    if (m_metadata.id == InvalidId) {
-        // The database was deleted then immediately re-opened; openInternal() recreates it in the backing store.
-        if (openInternal())
-            ASSERT(m_metadata.version == IDBDatabaseMetadata::NoIntVersion);
-        else {
-            String message;
-            RefPtr<IDBDatabaseError> error;
-            if (version == IDBDatabaseMetadata::NoIntVersion)
-                message = "Internal error opening database with no version specified.";
-            else
-                message = String::format("Internal error opening database with version %llu", static_cast<unsigned long long>(version));
-            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
-            return;
-        }
-    }
+    processPendingCalls();
+}
+
+
+void IDBDatabaseBackendImpl::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
+{
+    ASSERT(m_backingStore.get());
+    ASSERT(m_pendingDeleteCalls.isEmpty());
+    ASSERT(!m_runningVersionChangeTransaction);
+
+    RefPtr<IDBCallbacks> callbacks = prpCallbacks;
+    RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
 
     // We infer that the database didn't exist from its lack of either type of version.
     bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion;
index 6e5e54e..6fba9f7 100644 (file)
@@ -105,10 +105,15 @@ public:
 private:
     IDBDatabaseBackendImpl(const String& name, IDBBackingStoreInterface*, IDBFactoryBackendInterface*, const String& uniqueIdentifier);
 
-    bool openInternal();
+    void openConnectionInternal(PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version);
+
+    void openInternalAsync();
+    void didOpenInternalAsync(const IDBDatabaseMetadata&, bool success);
+
     void runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, int64_t requestedVersion);
     size_t connectionCount();
     void processPendingCalls();
+    void processPendingOpenCalls(bool success);
 
     bool isDeleteDatabaseBlocked();
     void deleteDatabaseFinal(PassRefPtr<IDBCallbacks>);
index aa60244..81df4ff 100644 (file)
@@ -543,6 +543,64 @@ bool IDBBackingStoreLevelDB::getIDBDatabaseMetaData(const String& name, IDBDatab
     return true;
 }
 
+void IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata(const String& name, GetIDBDatabaseMetadataFunction metadataFunction)
+{
+    const Vector<char> key = DatabaseNameKey::encode(m_identifier, name);
+    bool found = false;
+
+    IDBDatabaseMetadata resultMetadata;
+    
+    bool ok = getInt(m_db.get(), key, resultMetadata.id, found);
+    if (!ok) {
+        INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
+        metadataFunction(resultMetadata, false);
+        return;
+    }
+
+    if (!found) {
+        resultMetadata.name = name;
+        resultMetadata.version = IDBDatabaseMetadata::DefaultIntVersion;
+
+        metadataFunction(resultMetadata, createIDBDatabaseMetaData(resultMetadata));
+        return;
+    }
+
+    int64_t version;
+    ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(resultMetadata.id, DatabaseMetaDataKey::UserIntVersion), version, found);
+    if (!ok) {
+        INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
+        metadataFunction(resultMetadata, false);
+        return;
+    }
+    if (!found) {
+        INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData);
+        metadataFunction(resultMetadata, false);
+        return;
+    }
+
+    // FIXME: The versioning semantics have changed since this original code was written, and what was once a negative number
+    // stored in the database is no longer a valid version.
+    if (version < 0)
+        version = 0;
+    resultMetadata.version = version;
+
+    ok = getMaxObjectStoreId(m_db.get(), resultMetadata.id, resultMetadata.maxObjectStoreId);
+    if (!ok) {
+        INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
+        metadataFunction(resultMetadata, false);
+        return;
+    }
+
+    ok = getObjectStores(resultMetadata.id, &resultMetadata.objectStores);
+    if (!ok) {
+        INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
+        metadataFunction(resultMetadata, false);
+        return;
+    }
+
+    metadataFunction(resultMetadata, true);
+}
+
 WARN_UNUSED_RETURN static bool getNewDatabaseId(LevelDBDatabase* db, int64_t& newId)
 {
     RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(db);
@@ -570,21 +628,17 @@ WARN_UNUSED_RETURN static bool getNewDatabaseId(LevelDBDatabase* db, int64_t& ne
     return true;
 }
 
-// FIXME: The version semantics have changed. String versions no longer exist, and the integer version is now a uint64_t
-bool IDBBackingStoreLevelDB::createIDBDatabaseMetaData(const String& name, const String& version, int64_t intVersion, int64_t& rowId)
+// FIXME: LevelDB needs to support uint64_t as the version type.
+bool IDBBackingStoreLevelDB::createIDBDatabaseMetaData(IDBDatabaseMetadata& metadata)
 {
-    bool ok = getNewDatabaseId(m_db.get(), rowId);
+    bool ok = getNewDatabaseId(m_db.get(), metadata.id);
     if (!ok)
         return false;
-    ASSERT(rowId >= 0);
-
-    if (intVersion == IDBDatabaseMetadata::NoIntVersion)
-        intVersion = IDBDatabaseMetadata::DefaultIntVersion;
+    ASSERT(metadata.id >= 0);
 
     RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(m_db.get());
-    putInt(transaction.get(), DatabaseNameKey::encode(m_identifier, name), rowId);
-    putString(transaction.get(), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserVersion), version);
-    putVarInt(transaction.get(), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserIntVersion), intVersion);
+    putInt(transaction.get(), DatabaseNameKey::encode(m_identifier, metadata.name), metadata.id);
+    putVarInt(transaction.get(), DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::UserIntVersion), metadata.version);
     if (!transaction->commit()) {
         INTERNAL_WRITE_ERROR(CreateIDBDatabaseMetaData);
         return false;
@@ -600,12 +654,6 @@ bool IDBBackingStoreLevelDB::updateIDBDatabaseVersion(IDBBackingStoreInterface::
     return true;
 }
 
-bool IDBBackingStoreLevelDB::updateIDBDatabaseMetaData(IDBBackingStoreInterface::Transaction& transaction, int64_t rowId, const String& version)
-{
-    putString(Transaction::levelDBTransactionFrom(transaction), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserVersion), version);
-    return true;
-}
-
 static void deleteRange(LevelDBTransaction* transaction, const Vector<char>& begin, const Vector<char>& end)
 {
     OwnPtr<LevelDBIterator> it = transaction->createIterator();
index e922186..f130bc0 100644 (file)
@@ -70,13 +70,12 @@ public:
     virtual std::unique_ptr<IDBBackingStoreInterface::Transaction> createBackingStoreTransaction();
 
     virtual Vector<String> getDatabaseNames();
-    virtual bool getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata*, bool& success) OVERRIDE WARN_UNUSED_RETURN;
-    virtual bool createIDBDatabaseMetaData(const String& name, const String& version, int64_t intVersion, int64_t& rowId) OVERRIDE;
-    virtual bool updateIDBDatabaseMetaData(IDBBackingStoreInterface::Transaction&, int64_t rowId, const String& version);
+
+    virtual void getOrEstablishIDBDatabaseMetadata(const String& name, GetIDBDatabaseMetadataFunction) OVERRIDE;
+
     virtual bool updateIDBDatabaseVersion(IDBBackingStoreInterface::Transaction&, int64_t rowId, uint64_t version) OVERRIDE;
     virtual bool deleteDatabase(const String& name) OVERRIDE;
 
-    virtual bool getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap*) OVERRIDE WARN_UNUSED_RETURN;
     virtual bool createObjectStore(IDBBackingStoreInterface::Transaction&, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement) OVERRIDE;
     virtual bool deleteObjectStore(IDBBackingStoreInterface::Transaction&, int64_t databaseId, int64_t objectStoreId) OVERRIDE WARN_UNUSED_RETURN;
 
@@ -180,6 +179,11 @@ protected:
 private:
     static PassRefPtr<IDBBackingStoreLevelDB> create(const String& identifier, PassOwnPtr<LevelDBDatabase>, PassOwnPtr<LevelDBComparator>);
 
+    // FIXME: LevelDB needs to support uint64_t as the version type.
+    bool createIDBDatabaseMetaData(IDBDatabaseMetadata&);
+    bool getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata*, bool& success) WARN_UNUSED_RETURN;
+    bool getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores) WARN_UNUSED_RETURN;
+
     bool findKeyInIndex(IDBBackingStoreInterface::Transaction&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, Vector<char>& foundEncodedPrimaryKey, bool& found);
     bool getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap*) WARN_UNUSED_RETURN;